本文共 4303 字,大约阅读时间需要 14 分钟。
其中,RUNNABLE状态包括 【运行中】 和 【就绪】;
BLOCKED(阻塞态)状态只有在【等待进入synchronized方法(块)】和 【其他Thread调用notify()或notifyAll(),但是还未获得锁】才会进入;sleep() 、yield()、join()是Thread的方法,只放弃cpu,但是不放弃锁
1、Thread.sleep(long millis),一定是当前线程调用此方法,当前线程进入TIMED_WAITING状态,但不释放对象锁,millis后线程自动苏醒进入就绪状态。作用:给其它线程执行机会的最佳方式。2、Thread.yield(),一定是当前线程调用此方法,当前线程放弃获取的CPU时间片,但不释放锁资源,由运行状态变为就绪状态,让OS再次选择线程。作用:让相同优先级的线程轮流执行,但并不保证一定会轮流执行。实际中无法保证yield()达到让步目的,因为让步的线程还有可能被线程调度程序再次选中。Thread.yield()不会导致阻塞。该方法与sleep()类似,只是不能由用户指定暂停多长时间。
3、t.join()/t.join(long millis),当前线程里调用其它线程t的join方法,当前线程进入WAITING/TIMED_WAITING状态,当前线程不会释放已经持有的对象锁。线程t执行完毕或者millis时间到,当前线程进入就绪状态。
wait()是Object的方法,放弃cpu,也放弃锁
4、obj.wait(),当前线程调用对象的wait()方法,当前线程释放对象锁,进入等待队列。依靠notify()/notifyAll()唤醒或者wait(long timeout) timeout时间到自动唤醒。
5、obj.notify()唤醒在此对象监视器上等待的单个线程,选择是任意性的。notifyAll()唤醒在此对象监视器上等待的所有线程。public class Test { public static void main(String[] args) { new Thread1().start(); new Thread2().start(); } public static class Thread1 extends Thread{ @Override public void run() { synchronized (Test.class){ System.out.println("Thread1 start"); try { /** * 1、wait()和notify()是Object锁的方法 * 2、wait()会让出锁 */ Test.class.wait(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("Thread1 go on "); } } } public static class Thread2 extends Thread{ @Override public void run() { synchronized (Test.class){ System.out.println("Thread2 start"); /** * 1、notify()调用后,该线程会等待该同步块执行完毕才释放锁 */ Test.class.notifyAll(); try { /** * 1、sleep()是Thread的方法 * 2、sleep()不让出锁,只让出cpu */ Thread2.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("Thread2 go on"); } } }}
总结一下,LockSupport比Object的wait/notify有两大优势:
①LockSupport不需要在同步代码块里 ,所以线程间也不需要维护一个共享的同步对象了,实现了线程间的解耦;
而wait/notify必须在同步块或同步方法中才能调用。②unpark函数可以先于park调用,所以不需要担心线程间的执行的先后顺序,而wait必须先于notify。
1、为什么LockSupport不需要在同步代码块里而wait()需要?
线程A执行一段业务逻辑后调用wait阻塞住自己。主线程调用notify方法唤醒线程A,线程A然后打印自己执行的结果:
public class TestObjWait { public static void main(String[] args)throws Exception { final Object obj = new Object(); Thread A = new Thread(new Runnable() { @Override public void run() { int sum = 0; for(int i=0;i<10;i++){ sum+=i; } try { synchronized (obj){ obj.wait(); } }catch (Exception e){ e.printStackTrace(); } System.out.println(sum); } }); A.start(); //睡眠一秒钟,保证线程A已经计算完成,阻塞在wait方法 Thread.sleep(1000); synchronized (obj){ obj.notify(); } }}
使用LockSupport实现:
public class TestObjWait { public static void main(String[] args)throws Exception { Thread A = new Thread(new Runnable() { @Override public void run() { int sum = 0; for(int i=0;i<10;i++){ sum+=i; } LockSupport.park(); System.out.println(sum); } }); A.start(); //睡眠一秒钟,保证线程A已经计算完成 Thread.sleep(1000); LockSupport.unpark(A); }}
2、为什么LockSupport不需要担心unpark函数和park调用顺序,而Object的wait/notify需要关心?
如果我们将上面代码的这一句去掉:
//睡眠一秒钟,保证线程A已经计算完成 Thread.sleep(1000);
那么,使用wait()和notify()的就会出题,可能A会永远被挂起,因为主线程的notify()先于wait()调用了;
但是LockSupport的代码还是正确的执行,因为 LockSupport和每个使用它的线程都与一个许可(permit)关联。permit相当于1,0的开关,默认是0; 调用unpark就将permit赋值1; 调用park时,会判断permit如果为1,就会将permit赋值0,并且立即返回,如果permit为0,会阻塞在这里,直到permit变为1转载地址:http://yhaji.baihongyu.com/