一、sleep的使用
1 public class ThreadTest { 2 public static void main(String[] args) throws InterruptedException { 3 Object obj = new Object(); 4 MyThread mt = new MyThread(obj); 5 mt.start(); 6 MyThread mt2 = new MyThread(obj); 7 mt2.start(); 8 } 9 private static class MyThread extends Thread{ 10 private Object obj; 11 public MyThread(Object obj) { 12 this.obj = obj; 13 } 14 @Override 15 public void run() { 16 System.out.println(Thread.currentThread().getName() + 17 " synchronized之前: " + System.currentTimeMillis()); 18 synchronized(obj) { 19 System.out.println(Thread.currentThread().getName() + 20 " sleep之前: " + System.currentTimeMillis()); 21 try { 22 Thread.sleep(2000); 23 } catch (InterruptedException e) { 24 e.printStackTrace(); 25 } 26 System.out.println(Thread.currentThread().getName() + 27 " sleep之后: " + System.currentTimeMillis()); 28 } 29 } 30 } 31 }
输出:
Thread-1 synchronized之前: 1546337474050
Thread-0 synchronized之前: 1546337474050
Thread-1 sleep之前: 1546337474051
Thread-1 sleep之后: 1546337476051
Thread-0 sleep之前: 1546337476051
Thread-0 sleep之后: 1546337478052
线程Thread-0和线程Thread-1监控同一个资源obj,Thread-1在sleep之后并没有释放对象锁。
好比,A和B去店里买衣服,只有一间试衣间,一把试衣间的钥匙,A先拿到试衣间钥匙(obj),进入试衣间(synchronized(obj) {...}),
并在试衣间睡了一觉(sleep(2000)),B只能等A醒来走出试衣间,才能有机会拿到钥匙并进入试衣间。
调用native方法:public static native void sleep(long millis) throws InterruptedException;
二、wait和notify的使用
1 public class ThreadTest { 2 public static void main(String[] args) throws InterruptedException { 3 Object obj = new Object(); 4 MyThread mt = new MyThread(obj); 5 mt.start(); 6 MyThread2 mt2 = new MyThread2(obj); 7 mt2.start(); 8 } 9 private static class MyThread extends Thread{ 10 private Object obj; 11 public MyThread(Object obj) { 12 this.obj = obj; 13 } 14 @Override 15 public void run() { 16 System.out.println(Thread.currentThread().getName() + 17 " synchronized之前: " + System.currentTimeMillis()); 18 synchronized(obj) { 19 System.out.println(Thread.currentThread().getName() + 20 " wait之前: " + System.currentTimeMillis()); 21 try { 22 obj.wait(); 23 } catch (InterruptedException e) { 24 // TODO Auto-generated catch block 25 e.printStackTrace(); 26 } 27 System.out.println(Thread.currentThread().getName() + 28 " wait之后: " + System.currentTimeMillis()); 29 } 30 } 31 } 32 private static class MyThread2 extends Thread{ 33 private Object obj; 34 public MyThread2(Object obj) { 35 this.obj = obj; 36 } 37 @Override 38 public void run() { 39 System.out.println(Thread.currentThread().getName() + 40 " synchronized之前: " + System.currentTimeMillis()); 41 synchronized(obj) { 42 System.out.println(Thread.currentThread().getName() + 43 " notify之前: " + System.currentTimeMillis()); 44 obj.notify(); 45 System.out.println(Thread.currentThread().getName() + 46 " notify之后: " + System.currentTimeMillis()); 47 } 48 } 49 } 50 }
输出:
Thread-0 synchronized之前: 1546349737274
Thread-0 wait之前: 1546349737274
Thread-1 synchronized之前: 1546349737274
Thread-1 notify之前: 1546349737275
Thread-1 notify之后: 1546349737275
Thread-0 wait之后: 1546349737275
Thread-0在执行了obj.wait()之后,线程暂停并释放对象锁,之后,Thread-1获得对象锁,Thread-1执行obj.notify()后Thread-0苏醒,Thread-1执行完synchronized的代码块之后,Thread-0才有机会获得锁。
1、wait()让当前线程进入“等待状态”,并让当前线程释放它所持有的锁。直到其他线程调用此对象的notify()方法或notify()方法,当前线程被唤醒,进入“就绪状态”。
2、notify()和notifyAll()的作用,则是唤醒当前对象上的等待线程。notify()是唤醒单个线程(随机唤醒),而notifyAll()是唤醒所有的线程。
3、wait(long timeout)让当前线程处于“等待(阻塞)状态”,直到其他线程调用此对象的notify()方法或notifyAll()方法,或者超过指定的时间量,当前线程被唤醒,进入“就绪状态”。
4、调用 wait()、notify()、notifyAll() 方法之前,必须获得对象锁,即,只能在同步方法中调用。
5、执行 notify() 之后,并不会立即退出让wait的线程执行,必须要先将同步块中的程序执行完,退出同步块,才会释放锁,让等待线程执行。
6、每调用一次 notify() 只能唤醒一个线程,多次调用可通知多个线程。
wait()、notify()、notifyAll()都是Object的方法。
原理:
每个对象都有个monitor,初始是0,执行完synchronized值就是1。
wait/notify需要在获得monitor的线程中才可以执行。
所以,wait/notify需要在synchronized中执行。
其中,wait又会释放掉锁,破坏掉同步。
和synchronized的关系:
synchronized代码块生成的字节码,被monitorenter和monitorexit包围,持有对象的monitor,线程执行wait/notify方法时,必须持有对象的monitor,所以,wait/notify方法在synchronized同步快中执行,就持有了对象的锁。
互斥和协同:
java语言的同步机制在底层实现上就只有两种方式:互斥和协同。
互斥:即synchronized内置锁。
协同:即内置条件队列,wait/notify/notyfiAll。
条件队列是处于等待状态的线程,等待特定条件为真。每个java对象都可以作为一个锁,同样每个java对象都可以作为一个条件队列。通过wait/notify/notifyAll来操作条件队列。
可以理解为:有一个队列,o.wait()就push进去,o.notify()就pull出来。、
要调用条件队列的任何一个方法,都必须要获得对象上的锁。
三、join的使用
1 public class ThreadTest { 2 public static void main(String[] args) throws InterruptedException { 3 Object obj = new Object(); 4 MyThread mt = new MyThread(obj); 5 mt.start(); 6 //在main中调用mt.join() 7 mt.join(); 8 System.out.println("main"); 9 } 10 private static class MyThread extends Thread{ 11 private Object obj; 12 public MyThread(Object obj) { 13 this.obj = obj; 14 } 15 @Override 16 public void run() { 17 System.out.println(Thread.currentThread().getName() + 18 " synchronized之前: " + System.currentTimeMillis()); 19 synchronized(obj) { 20 System.out.println(Thread.currentThread().getName() + 21 " sleep之前: " + System.currentTimeMillis()); 22 try { 23 Thread.sleep(3000); 24 } catch (InterruptedException e) { 25 // TODO Auto-generated catch block 26 e.printStackTrace(); 27 } 28 System.out.println(Thread.currentThread().getName() + 29 " sleep之后: " + System.currentTimeMillis()); 30 } 31 } 32 } 33 }
输出:
Thread-0 synchronized之前: 1546354871560
Thread-0 sleep之前: 1546354871560
Thread-0 sleep之后: 1546354874561
main
在main中调用了线程mt的join方法(mt.join()),则线程main会等线程mt执行完毕后再恢复运行。
join()方法由wait()实现。源码:
原文地址:https://www.cnblogs.com/natian-ws/p/10211321.html