天天看点

同步机制中notify()与wait()--实现A,B,C循环打印

关于同步与异步机制,听过很多通俗的解释。

日常生活中其实也有很多这样的例子,比如吃饭的时候

同步:就是你通知我去吃饭,我听到了就和你一起去,如果我没有回应,你就会一直喊,直到我有反映了为止。

异步:你通知我一声后,不管我有没有回应,你自己就去吃饭去了。

在通讯当中:

同步:发送方发出数据后,等接收方发回响应以后才发下一个数据包的通讯方式。 

异步:发送方发出数据后,不等接收方发回响应,接着发送下个数据包的通讯方式。

java在实现同步机制的过程中,主要用到了两种方法,一种是同步方法,一种是同步代码块。两种都用到了synchronized关键字。

同步方法:

public void sychronized study() {
        System.out.println("studying");
}
           

同步代码块:

(sychronized){
     ......
}
           

说到这里,下面说一下在同步代码块中使用notify(),以及wait()来实现一个经典的线程打印问题--利用三个线程A,B,C分别各自循环打印A,B,C.

关于notify()以及wait(),这里叙述一下。

同步机制中notify()与wait()--实现A,B,C循环打印

我们可以看到,wait()与notify()同属于object类的方法,由于object被所有类所继承,所以这两个方法也会被所有类所继承。而且,我们看源码,关于两个方法,

public final native void notify();
           
public final void wait() throws InterruptedException {
        wait(0);
    }
           

两个方法都有final修饰,则在子类中不能够被重写。

wait 方法:

使当前线程一直处于等待的状态,直到另一个线程在调用了notify()或者notifyAll()方法。

当前线程拥有当前对象的锁,即wait()方法应该在sychronized{}方法或者代码块中。调用wait()方法后,释放对当前对象锁的拥有。

notify()方法:

唤醒处于等待状态的线程,这里应该注意是,如果有多个线程处于等待的状态,那么调用一次notify()方法后,具体释放的是哪一个线程是不确定的,执行的过程是java虚拟机来实现的。

跟wait方法一样,notify也是必须放在synchronized方法或synchronized块中。

关于wait()与notify()方法,利用三个线程循环打印A,B,C是很具有代表性的。

public class MyThread {
	public static void main(String[] args) throws Exception {
		Object A = new Object();
		Object B = new Object();
		Object C = new Object();

		Thread t1 = new Thread(new Print(A, B), "A");
		Thread t2 = new Thread(new Print(B, C), "B");
		Thread t3 = new Thread(new Print(C, A), "C");
                
                //Thread.sleep(1)为了让打印的时候依次执行
		t1.start();
		Thread.sleep(1);
		t2.start();
		Thread.sleep(1);
		t3.start();

	}
}

class Print implements Runnable {

	private Object self;
	private Object next;

	public Print(Object self, Object next) throws InterruptedException {
		this.self = self;
		this.next = next;

	}

	public void run() {
		for (int i = 0; i < 10; i++) {
			synchronized (self) {
				synchronized (next) {
					System.out.println(Thread.currentThread().getName());
					next.notify();
				}
				try {
					if (i == 9) {
						return; // 当i == 9 即最后一次循环, 将直接退出 不再进行等待
					}
					self.wait();
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		}

	}
}
           

打印的结果为ABC ABC ABC ABC ABC ABC ABC ABC ABC ABC(当然,这里未显示换行符)

现在简单的看一下程序执行的过程。

首先线程A(这里为线程的名字)调用start方法,在run方法中,self为A,next为B,打印一下Thread.CurrentThread.getName()为A.然后执行self.wait(),即此时A处于阻塞状态。然后线程B(线程的名字)进入run方法,self为B,next为C,然后先打印一下B,再执行next.notify()方法,此时把之前处于阻塞状态的线程A不再阻塞。当执行下面的self.wait的时候,此时线程B就处于阻塞状态了,而线程A,C处于就绪状态,等待处理机的调度。然后线程C进入run方法,self为C,next为A,打印一下C,next.notify使之前处于阻塞的B处于就绪状态,然后 C再进入等待的状态,依次下去。

继续阅读