线程同步化的理解以及使用

时间:2022-03-12 05:15:28

        又是忙碌的一天,Java结束了复习,老师也开始讲新的课程了。下午下课,很任性的没有去吃饭,后来与老师讨论时就说到了线程同步的问题。有了点自己的感触,想给大家分享一下,希望能让像我一样的程序员能有所收获。

        我们都知道,多线程编程为程序开发带来了很多的便利,但是也带来了一些问题,这些问题时在程序开发过程中必须进行处理的。这些问题的核心是,如果多个线程同时访问一个资源,如变量、文件等,如何保证访问安全?

        举个例子,假设有两个线程:线程A和线程B,并且程序为这两个线程开辟了一个公共的内存空间,情况则是线程B写一次,线程A读一次。那么则会出现如下几种情况:

        1.在某个时候线程B运行速度比较快,在线程A未读取上一个数据之前,B就写了第二次数据,造成数据遗漏。

        2.在某个时候线程 A 运行速度比较快,它读完一次数据之后,线程 B 还没来得及写,线程 A 又来读第二次。结果线程 A 读不到数据,导致运行出错。
        3.线程 B 正在写数据时,线程 A 也来读取数据,这时可能线程 B 还没将数据写完,线程 A 将数据读走,导致程序出错。
 接下来,就让我用代码来演示同步产生的错误的情况:
  利用线程来模拟售票系统:
 
票类:
 
public class Tickets {
	public int total;//在票这个类中定义票的数量
	public Tickets(int total){//初始化
		this.total=total;
	}
	public  void action(String threadName){
		System.out.println(threadName+"卖票之前"+total);
		total--;
		System.out.println(threadName+"卖票之后"+total);
	}
}

线程类:
public class TicketsThread extends Thread{
	private Tickets tickets;//票的对象
	public TicketsThread(Tickets tickets) {//初始化票的对象
		this.tickets=tickets;
	}
	@Override
	public void run() {
		while(true){
			if(tickets.total<=1){
				break;
			}
			try {
				Thread.sleep(200);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			tickets.action(Thread.currentThread().getName());
		}
	}
}

测试类:
public class Test {
	public static void main(String[] args) {
		Tickets t=new Tickets(10);
		TicketsThread tt1=new TicketsThread(t);
		tt1.start();
		TicketsThread tt2=new TicketsThread(t);
		tt2.start();
	}
}
通过测试类的运行结果为:

线程同步化的理解以及使用
 
       从结果来看,明显在我用荧光笔标识的地方有错误,明显这里应该是“Thread-0卖票之前3”,也就是读到了“脏数据”(就是不该读到的数据),再把测试类反复运行,总难免会有一些读错数据的情况,那么我们就不难发现线程在同步中也会产生一些无法预料的错误,那么究竟该怎么解决呢?
       <注:在这里,我并没有说每次测试都是错误的,但是完全正确的几率十分低>
       在多线程编程中,这种被多个线程同时访问的资源叫做临界资源,那么解决临界资源问题,最基本、最简单的思路就是使用同步关键字synchronized。
       因为我们同时访问的所谓临界资源就是票这个类,所以只需要改正这里即可,以下是代码:
public class Tickets {
	public int total;//在票这个类中定义票的数量
	public Tickets(int total){//初始化
		this.total=total;
	}
	public synchronized void action(String threadName){//定义同步化,即线程A访问时,线程B必须等待;线程B访问时,线程A必须等待
		System.out.println(threadName+"卖票之前"+total);
		total--;
		System.out.println(threadName+"卖票之后"+total);
	}
}

      再让我们来看看输出的结果:
线程同步化的理解以及使用
       那么,这个关键字的作用到底是什么呢?让我们再回到我一开始举的例子上,我们可以这样理解,当有此关键字存在时“定义同步化,即线程A访问时,线程B必须等待;线程B访问时,线程A必须等待”。
       希望能给一些和我一样的初学者一些帮助,也希望技术大牛们多多指点。