在java多线程编程中,最被经常用到的便是wait与notfiy方法,这两个方法可以用来更加精确地控制被同步的代码,从而使得被同步的代码最小化,提高并发效率。
当某个类的某个方法被标记为synchronized时,这个方法在同一时间只能被一个线程访问。此时这个方法中的所有代码都是被同步的,只有当一个线程执行完所有的代码之后,下一个线程才能开始执行。当被同步的方法代码量比较小,而且每一步执行都非常快的时候仅仅使用synchronized关键字就够了。但是,如果被同步的方法里面有一些代码是可以被共享的,而且这些能够被共享的代码里面存在比较耗时的操作时,仅仅使用synchronized关键字就无法达到最高的效率,这个时候可以使用wait与notify方法来对并发访问做更进一步的控制。首先看两段代码:
在上面的类中定义了一个方法work,在这个方法中的try块之前可以执行一些可共享的代码,而try块中的代码是不能够被共享的。因此,我们在进入try之后会首先判断标志isIdle是否为true,如果为true就表示当前没有其他线程正在访问,因此当前线程就获得了执行try块中代码的权利。在执行代码前当前线程会将isIdle设置成false,这样当其他线程进入try,之后就会发现isIdle为false,从而进入等待状态。
当一个线程执行完try中的代码之后,会将isIdle重新设置为true,同是使用notifyAll方法通知所有等待获得执行try块中代码的权利的线程。
下面这个类用来测试上面的TestThread:
这个类在main方法中启动了五个不同的线程来访问TestThread的work方法,从打印的结果中可以看出,每次线程的执行顺序都会有些许的差别。
日志:
第一次运行:
第二次运行:
当某个类的某个方法被标记为synchronized时,这个方法在同一时间只能被一个线程访问。此时这个方法中的所有代码都是被同步的,只有当一个线程执行完所有的代码之后,下一个线程才能开始执行。当被同步的方法代码量比较小,而且每一步执行都非常快的时候仅仅使用synchronized关键字就够了。但是,如果被同步的方法里面有一些代码是可以被共享的,而且这些能够被共享的代码里面存在比较耗时的操作时,仅仅使用synchronized关键字就无法达到最高的效率,这个时候可以使用wait与notify方法来对并发访问做更进一步的控制。首先看两段代码:
- public class TestThread {
- private boolean isIdle = true;
- public synchronized void work(){
- /*
- * Some work which can be shared
- */
- try {
- /*
- * to check if we can have this object's monitor
- */
- if(!isIdle){
- System.out.println(Thread.currentThread().toString() + ":I'm waiting....");
- }
- while(!isIdle){
- wait();
- }
- /*
- * to set isIdle to false, I'm working....
- */
- this.isIdle = false;
- System.out.println(Thread.currentThread().toString() + ":I'm working....");
- Thread.currentThread().sleep(1000);
- System.out.println(Thread.currentThread().toString() + ":I'm finished....");
- /*
- * to notify all other thread which is waiting for this object's monitor
- */
- this.isIdle = true;
- notifyAll();
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- }
在上面的类中定义了一个方法work,在这个方法中的try块之前可以执行一些可共享的代码,而try块中的代码是不能够被共享的。因此,我们在进入try之后会首先判断标志isIdle是否为true,如果为true就表示当前没有其他线程正在访问,因此当前线程就获得了执行try块中代码的权利。在执行代码前当前线程会将isIdle设置成false,这样当其他线程进入try,之后就会发现isIdle为false,从而进入等待状态。
当一个线程执行完try中的代码之后,会将isIdle重新设置为true,同是使用notifyAll方法通知所有等待获得执行try块中代码的权利的线程。
下面这个类用来测试上面的TestThread:
- public class ThreadTester {
- private static TestThread tt = new TestThread();
- public static void main(String[] args){
- for(int i = 0; i < 5; i++){
- new Thread(new Runnable(){
- public void run(){
- tt.work();
- }
- }).start();
- }
- }
- }
这个类在main方法中启动了五个不同的线程来访问TestThread的work方法,从打印的结果中可以看出,每次线程的执行顺序都会有些许的差别。
日志:
第一次运行:
- Thread[Thread-0,5,main]:I'm working....
- Thread[Thread-0,5,main]:I'm finished....
- Thread[Thread-1,5,main]:I'm working....
- Thread[Thread-1,5,main]:I'm finished....
- Thread[Thread-3,5,main]:I'm working....
- Thread[Thread-3,5,main]:I'm finished....
- Thread[Thread-2,5,main]:I'm working....
- Thread[Thread-2,5,main]:I'm finished....
- Thread[Thread-4,5,main]:I'm working....
- Thread[Thread-4,5,main]:I'm finished....
第二次运行:
- Thread[Thread-0,5,main]:I'm working....
- Thread[Thread-0,5,main]:I'm finished....
- Thread[Thread-2,5,main]:I'm working....
- Thread[Thread-2,5,main]:I'm finished....
- Thread[Thread-1,5,main]:I'm working....
- Thread[Thread-1,5,main]:I'm finished....
- Thread[Thread-4,5,main]:I'm working....
- Thread[Thread-4,5,main]:I'm finished....
- Thread[Thread-3,5,main]:I'm working....
- Thread[Thread-3,5,main]:I'm finished....