关于同步与异步机制,听过很多通俗的解释。
日常生活中其实也有很多这样的例子,比如吃饭的时候
同步:就是你通知我去吃饭,我听到了就和你一起去,如果我没有回应,你就会一直喊,直到我有反映了为止。
异步:你通知我一声后,不管我有没有回应,你自己就去吃饭去了。
在通讯当中:
同步:发送方发出数据后,等接收方发回响应以后才发下一个数据包的通讯方式。
异步:发送方发出数据后,不等接收方发回响应,接着发送下个数据包的通讯方式。
java在实现同步机制的过程中,主要用到了两种方法,一种是同步方法,一种是同步代码块。两种都用到了synchronized关键字。
同步方法:
public void sychronized study() {
System.out.println("studying");
}
同步代码块:
(sychronized){
......
}
说到这里,下面说一下在同步代码块中使用notify(),以及wait()来实现一个经典的线程打印问题--利用三个线程A,B,C分别各自循环打印A,B,C.
关于notify()以及wait(),这里叙述一下。
我们可以看到,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再进入等待的状态,依次下去。