LockSupport
文章目录
1. LockSupport是什么
- LockSupport是用于创建锁和其他同步类的基本线程阻塞原语
- LockSupport没有构造函数,说明不可以new对象,只可以使用静态方法
- 核心方法就是 park() 和 unpark()
- park():除非许可证可用,否则禁用当前线程
- unpark(Thread):如果给定线程不可用,则为其提供许可
- park()和unpark()分别是阻塞线程和解除被阻塞的线程
- LockSupport就是对线程等待唤醒机制(wait()/notify())的优化
2. 线程等待唤醒机制
- 使用Object中的wait()让线程等待,使用notify()唤醒等待的线程
- 通过调用对象的wait()来阻塞,然后再调用该对象的notify()来唤醒被阻塞的线程
- wait()方法会释放当前对象的锁,会释放当前对象的锁,而sleep()方法不会释放调用对象的锁
- wait 方法释放了锁,但是t1线程是阻塞状态不可以争夺cpu时间片,当t2线程notify之后唤醒t1线程并进入就绪态后才可以争夺cpu时间片进行运行
- 如果要使用wait()和notify(),必须要包在Synchronized代码块内
- 必须在获得某个对象的锁之后,才可以调用该对象的wait()和notify()方法来阻塞唤醒线程,故wait和notify必须在Synchronized代码块或者方法内,且使用锁对象调用
- 通过对象的wait()和notify()可以实现线程阻塞和唤醒,但必须是同一个对象才可以唤醒被阻塞的线程,且必须获得对象的锁之后才可以调用对象的wait()和notify()方法来阻塞唤醒线程
- 且如果使用对象的wait()和notify()来阻塞唤醒线程,则此时如果顺序出错,则会导致线程一直阻塞,必须先获得对象的锁之后,才可以调用对象的wait和notify来阻塞唤醒线程,且wait会释放当前对象的锁
- 必须保证wait之后再notify,否则会出现线程一直阻塞现象,不是死锁,因为只有一个线程一直阻塞
- 死锁是两个或以上线程因为竞争资源而造成的相互等待现象
- 使用JUC包中Lock锁对象的Condition的await()让线程等待,使用signal()唤醒线程
- 通过对象的wait()和notify()可以实现线程的阻塞和唤醒,但必须保证先获得对象的锁,即必须在Synchronized代码块中;且必须保证先wait之后再notify,否则会导致一直阻塞
- 通过Lock锁对象.newCondition()创建Condition对象,然后通过c1.await()阻塞线程,再通过c1.signal()唤醒被阻塞的线程
- 通过JUC中Lock锁对象自带的Condition的await()和signal()也可以实现线程的噪声和唤醒
- 使用Condition对象阻塞唤醒线程时,依旧要确保先加锁,必须先获得创建Condition对象的Lock锁之后才可以使用该Condition对象的await和signal;Condition的await和signal也必须在lock和unLock锁块之中使用
- 且必须要保证先await()阻塞线程之后再signal()唤醒线程,否则会导致线程一直阻塞,此时不是死锁,因为只有一个线程阻塞,而不是两个或以上相互竞争资源
- 使用LockSupport的park()阻塞线程,使用unpark()唤醒线程
- 使用Object对象的wait()和notify()或者使用Condition对象的await()和signal()可以实现线程阻塞唤醒机制,但都必须满足必须先加锁,然后才可以调用方法;且必须先阻塞后再唤醒,保证顺序不变,否则会导致线程一直阻塞
- LockSupport的park()和unpark()也可以实现线程的噪声和唤醒,且park()是当没有许可时阻塞线程,而unpark(Thread)是如果线程没有许可则给其许可,且通过LockSupport的静态方法来调用,故此时不需要先加锁,而且可以先unpark()后再park()
- LockSupport是创建锁和其他同步类的基本线程阻塞原语,通过park()和unpark()来阻塞和唤醒线程,且使用Permit许可来做到阻塞和唤醒线程,每一个线程都有最多一个许可,如果存在许可证,则park()不会阻塞,且会消耗许可证;最多只可以拥有一个许可证,且每次park都会消耗一个许可证,且许可证不可以累积,通过unpark(Thread)来获得许可证
- LockSupport的park()调用的是UNSAFE类的park(boolean,time阻塞时间),其底层调用的是native的park(),且当线程没有许可时就会阻塞time时间,此时就需要别的线程通过unpark(thread)来给线程许可来唤醒
- 当线程调用LockSupport.park()后,如果该线程没有许可,则会阻塞,直到其他线程通过unpark(thread)给指定线程一个许可后才可以使其唤醒;每一个线程最多只可以有一个许可,且每次park()都会消耗一个许可
- Java底层是使用C++实现的
- 如果使用Object对象的wait()/notify(),或者使用JUC中的Condition对象的await()和signal()实现线程阻塞和唤醒时,此时必须要先获得锁对象,即必须在锁块中才可以调用函数,且必须保证先阻塞再唤醒;为了克服这些限制,可以使用LockSupport的park()和unpark()来实现线程阻塞和唤醒,此时不需要在锁块中调用,直接使用LockSupport的静态方法,且不需要保证调用顺序,因为其是使用permit许可来实现的
- LockSupport不需要先加锁,直接就可以实现线程的阻塞和唤醒,不需要先加锁,直接进行线程阻塞和唤醒,通过LockSupport.park()来加锁,通过LockSupport.unpark(Thread)来对线程唤醒,park()会消耗permit许可,而unpark()会增加许可,最多只可以有一个许可
- LockSuport不需要先加锁,而且可以先唤醒再阻塞,顺序相反不会影响
- 使用LockSupport时不需要关注执行顺序,顺序相反仍可以实现,此时可以确保某个代码执行完成后再让另一个代码执行
3. LockSupport
- LockSupport通过许可来实现阻塞和唤醒,且每一个线程最多只有一个许可,此时unpark()后不会再增加许可
- LockSupport是创建锁和其他类的线程阻塞原语,是线程阻塞的工具类,与Object的wait/notify以及Condition的await/signal相比,不需要先加锁,而且唤醒和阻塞顺序不影响最终结果;通过静态方法park()和unpark(thread)来实现阻塞和唤醒;通过permit许可来实现,且每一个线程最多只可以有一个许可,且每次park()均会消耗一个许可,如果没有许可时就会阻塞,此时必须让别的线程通过unpark来给其许可才会唤醒
- Object和Condition的线程阻塞唤醒机制,必须先获得对应对象的锁,然后调用同一个对象的阻塞和唤醒方法才可以实现线程阻塞和唤醒
- 每一个线程最多只可以有一个许可,故多次unpark()的结果是一样的