1_2、AQS前提知识之——LockSupport
作用:用于创建锁和其他同步类的基本线程阻塞原语,是一个线程阻塞工具类,所有方法都是静态方法,可以让线程在任意位置阻塞,阻塞之后也有对应的唤醒方法。归根结底LockSupport调用的是Unsafe中的native代码
对比:
1、Synchronized:
阻塞:wait();
通知:notify(); notifyAll();
2、Lock
阻塞:await();
通知:signal();
Synchronized的阻塞通知缺点:
(1)wait和notify需要在Synchronized代码块里才能生效,否则会出现异常
(2)b线程要是先notify(),a线程再wait()。则程序会一直无法结束。
1 static final Object objectLockA=new Object(); 2 public static void m1(){ 3 new Thread(()->{ 4 // try {TimeUnit.SECONDS.sleep(3);} catch (InterruptedException e) {e.printStackTrace(); } 5 synchronized (objectLockA){ 6 System.out.println(Thread.currentThread().getName() + "\t 进来"); 7 try { 8 objectLockA.wait(); 9 } catch (InterruptedException e) { 10 e.printStackTrace(); 11 } 12 System.out.println(Thread.currentThread().getName() + "\t 被唤醒"); 13 } 14 }, "a").start(); 15 new Thread(()->{ 16 synchronized (objectLockA){ 17 objectLockA.notify(); 18 System.out.println(Thread.currentThread().getName() + "\t 通知"); 19 } 20 }, "b").start(); 21 } 22 输出: 23 a 进来 24 b 通知 25 a 被唤醒
Lock的阻塞通知缺点:
(1)await和signal需要在lock.lock()和lock.unlock()代码块里才能生效,否则会出现异常
(2)b线程要是先signal(),a线程再await()。则程序会一直无法结束。
1 static Lock lock=new ReentrantLock(); 2 static Condition condition=lock.newCondition(); 3 public static void m1(){ 4 new Thread(()->{ 5 // try {TimeUnit.SECONDS.sleep(3);} catch (InterruptedException e) {e.printStackTrace(); } 6 lock.lock(); 7 try { 8 System.out.println(Thread.currentThread().getName() + "\t 进来"); 9 try { 10 condition.await(); 11 } catch (InterruptedException e) { 12 e.printStackTrace(); 13 } 14 System.out.println(Thread.currentThread().getName() + "\t 被唤醒"); 15 }finally { 16 lock.unlock(); 17 } 18 }, "a").start(); 19 new Thread(()->{ 20 lock.lock(); 21 try { 22 condition.signal(); 23 System.out.println(Thread.currentThread().getName() + "\t 通知"); 24 }finally { 25 lock.unlock(); 26 } 27 }, "b").start(); 28 }
3、LockSupport:
阻塞:park()阻塞当前线程 park(Object blocker);阻塞传入的具体线程
调用LockSupport.park()时
public static void park(){ UNSAFE.park(false,0L); }
permit默认是0,所以一开始调用park()方法当前线程就会阻塞,直到别的线程将当前线程的permit设置为1时,park()方法会被唤醒,然后会将permit再次设置为0并返回
通知:unpark(Thread thread);唤醒处于阻塞状态的指定线程
调用LockSupport.unpark(thread)时
public static void unpark(Thread thread){ if(thread!=null){ UNSAFE.unpark(thread); } }
调用unpark()方法后,就会将thread线程的许可证permit设置成1(注意多次调用unpark方法,permit值还是1)会自动唤醒thread线程,即之前阻塞中的LockSupper.park()方法会立即返回
1 public static void m3() { 2 Thread a = new Thread(() -> { 3 try { 4 TimeUnit.SECONDS.sleep(3); 5 } catch (InterruptedException e) { 6 e.printStackTrace(); 7 } 8 System.out.println(Thread.currentThread().getName() + "\t 进来"); 9 LockSupport.park(); 10 System.out.println(Thread.currentThread().getName() + "\t 被唤醒"); 11 }, "a"); 12 a.start(); 13 new Thread(() -> { 14 LockSupport.unpark(a); 15 System.out.println(Thread.currentThread().getName() + "\t 通知"); 16 }, "b").start(); 17 }
理解:
线程阻塞需要消耗凭证(permit)才能通行。这个凭证最多只有1个,可用先unpark颁发凭证,再park消耗凭证。也就是类似可用先通知,后阻塞。
当调用park方法时。如果有凭证,则会直接消耗掉这个凭证然后正常执行退出。如果没有,就必须阻塞等待凭证可用。
而调用unpark方法时。会给阻塞线程颁发一个凭证,但凭证最后只能有1个,再次调用不会累加。

浙公网安备 33010602011771号