六、LockSupport
1、为什么使用LockSupport类
如果只是LockSupport在使用起来比Object的wait/notify简单,
那还真没必要专门讲解下LockSupport。最主要的是灵活性。
①LockSupport不需要在同步代码块里 。所以线程间也不需要维护一个共享的同步对象了,实现了线程间的解耦。
②unpark函数可以先于park调用,所以不需要担心线程间的执行的先后顺序。
上边的例子代码中,主线程调用了Thread.sleep(1000)方法来等待线程A计算完成进入wait状态。如果去掉Thread.sleep()调用,代码如下:
note:这个场景需要注意一下 防止在业务场景中出现这种bug。
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();
}
}
}
多运行几次上边的代码,有的时候能够正常打印结果并退出程序,但有的时候线程无法打印结果阻塞住了。原因就在于:主线程调用完notify后,线程A才进入wait方法,
导致线程A一直阻塞住。由于线程A不是后台线程,所以整个程序无法退出。
那如果换做LockSupport呢?LockSupport就支持主线程先调用unpark后,线程A再调用park而不被阻塞吗?是的,没错。代码如下:
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;
}
LockSupport.park();
System.out.println(sum);
}
});
A.start();
//睡眠一秒钟,保证线程A已经计算完成,阻塞在wait方法
//Thread.sleep(1000);
LockSupport.unpark(A);
}
}
不管你执行多少次,这段代码都能正常打印结果并退出。这就是LockSupport最大的灵活所在。
总结一下,LockSupport比Object的wait/notify有两大优势:
①LockSupport不需要在同步代码块里 。所以线程间也不需要维护一个共享的同步对象了,实现了线程间的解耦。
②unpark函数可以先于park调用,所以不需要担心线程间的执行的先后顺序。
1.LockSupport的等待和唤醒例子
package day03.part2;
import java.util.concurrent.locks.LockSupport;
/**
*LockSupport的等待和唤醒
* @author xzq
*/
public class LockSupportTest01 {
public static void main(String[] args) {
Thread t= new Thread("子线程"){
@Override
public void run() {
String threadName = Thread.currentThread().getName();
System.out.println(threadName+":开始执行……");
System.out.println(threadName+":睡眠中……");
LockSupport.park();//等待
System.out.println(threadName+":执行结束!");
}
};
t.start();
try{
Thread.sleep(1000);
}catch(Exception e){
}
System.out.println("main:已唤醒子线程1次");
LockSupport.unpark(t);//唤醒 可以唤醒指定线程 t
System.out.println("main:执行结束……");
}
}
package day03.part2;
/**
*LockSupport的等待和唤醒
*先唤醒,再等待的情况
*相当于对唤醒进行了一个抵消作用,
*本人称之为:0 -1 0作用
* @author xzq
*/
public class LockSupportTest02 {
public static void main(String[] args) {
Thread t= new Thread("子线程"){
@Override
public synchronized void run() {
String threadName = Thread.currentThread().getName();
System.out.println(threadName+":开始执行……");
System.out.println(threadName+":唤醒前---------");
this.notify();
// LockSupport.unpark(this);
System.out.println(threadName+":唤醒后---------");
System.out.println(threadName+":等待前---------");
try {
this.wait();
} catch (InterruptedException e) {
}
// LockSupport.park();
System.out.println(threadName+":等待后---------");
System.out.println(threadName+":执行结束!");
}
};
t.start();
System.out.println("main:执行结束……");
}
}
2.LockSupport的等待是否释放锁
package day03.part2;
/**
*LockSupport的等待是否释放锁
* @author xzq
*/
public class LockSupportTest03 {
private synchronized static void testSync() throws InterruptedException{
String threadName = Thread.currentThread().getName();
System.out.println(threadName+":进入同步方法……");
System.out.println(threadName+":开始等待……");
for(int i=1;i<=3;i++){
//sleep不释放锁
System.out.println(threadName+":现在是在同步方法中的等待的第"+i+"秒");
Thread.sleep(1_000);
/*//wait要释放锁
LockSupportTest03.class.wait(1_000);
System.out.println(threadName+":现在是在同步方法中的第"+i+"秒");*/
/*//LockSupport的park方法不释放锁
//注意这个参数是纳秒:1秒等于1千毫秒,等于100万微秒,等于10亿纳秒
LockSupport.parkNanos(1_000_000_000);
LockSupport.parkNanos(TimeUnit.SECONDS.toNanos(1));*/
System.out.println(threadName+":现在是在同步方法中的第"+i+"秒");
}
System.out.println(threadName+":离开同步方法……");
}
public static void main(String[] args) {
//开启两个线程去执行同步方法
for(int i=1;i<=2;i++){
new Thread("线程"+i){
public void run() {
try {
testSync();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}.start();
}
System.out.println("主线程执行完毕!!!!");
}
}
如上可以看到 LockSupport的park方法不释放锁
3.LockSupport对interrupt的感知 不需要抛异常直接被中断
package day03.part2;
import java.util.concurrent.locks.LockSupport;
/**
* LockSupport对interrupt的感知
* 不需要抛异常直接被中断
* @author xzq
*/
public class LockSupportTest04 {
public static void main(String[] args) {
Thread t= new Thread("子线程"){
@Override
public void run() {
String threadName = Thread.currentThread().getName();
System.out.println(threadName+":开始执行……");
System.out.println(threadName+":睡眠中……");
/*synchronized (this) {
try {
//使用wait进行等待
this.wait();
//使用sleep进行等待
// Thread.sleep(Long.MAX_VALUE);
} catch (InterruptedException e) {
System.out.println(threadName+":捕获到中断异常……");
}
}*/
LockSupport.park();//被中断时不需要俘获异常
System.out.println(threadName+":执行结束!");
}
};
t.start();
System.out.println("main:对子线程进行中断!");
t.interrupt();
System.out.println("main:执行结束……");
}
}
4.用LockSupport实现非重入锁
package day03.part2;
import java.util.concurrent.locks.LockSupport;
/**
* LockSupport的应用
* 用LockSupport实现非重入锁
* @author xzq
*/
public class LockSupportTest05 {
private static MyLock lock=new MyLock();
public static void main(String[] args) {
String threadName = Thread.currentThread().getName();
System.out.println(threadName+":进入同步区域……");
testSync_1();
System.out.println(threadName+":离开同步区域……");
}
private static /*synchronized*/ void testSync_1(){
String threadName = Thread.currentThread().getName();
System.out.println(threadName+":开始执行testSync_1方法……");
// lock.lock();
System.out.println(threadName+":准备进入testSync_2方法……");
testSync_2();
System.out.println(threadName+":执行了testSync_2中的逻辑!!!");
// lock.unlock();
System.out.println(threadName+":执行testSync_1方法结束!!!");
}
private static /*synchronized*/ void testSync_2(){
String threadName = Thread.currentThread().getName();
// lock.lock();
System.out.println(threadName+":执行了testSync_2中的逻辑!!!");
// lock.unlock();
}
static class MyLock{
private boolean isLocked = false;
public synchronized void lock(){
while(isLocked){
LockSupport.park();
}
isLocked = true;
}
public synchronized void unlock(){
isLocked = false;
LockSupport.unpark(Thread.currentThread());
}
}
}
本文地址:https://blog.csdn.net/XZQ_STUD/article/details/112251920