前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >LockSupport的一则问题(群友问题分析)

LockSupport的一则问题(群友问题分析)

作者头像
山行AI
发布2019-06-28 11:17:25
6090
发布2019-06-28 11:17:25
举报
文章被收录于专栏:山行AI山行AI

群友的问题是想写个锁,他的代码如下:

public class TestLockSupport implements Runnable{

private AtomicReference sign = new AtomicReference<>();

// 获取锁

public void lock() {

Thread current = Thread.currentThread();

System.out.println(current.getName() + “进入lock!”);

if (!sign.compareAndSet(null, current)){ //许可默认是被占用的

System.out.println(current.getName() + “获得锁!”);

LockSupport.park();// 获取许可

}

}

public AtomicReference<Thread> getSign() {

return sign;

}

// 释放锁

public void unlock() {

Thread current = Thread.currentThread();

System.out.println(current.getName() + "进入unlock!");

sign.compareAndSet(current, null);

LockSupport.unpark(current);//释放许可

System.out.println(current.getName() + "释放锁!");

}

volatile static int sum =0;

@Override

public void run() {

lock();

sum++;

System.out.println(Thread.currentThread().getName()+"--"+sum);

unlock();

}

public static void main(String[] args) throws InterruptedException {

TestLockSupport bl=new TestLockSupport();

for(int i = 0; i < 5; i++){

Thread t=new Thread(bl);

t.start();

// t.join();//在这里加上join之后,相当于for循环第一次,第二次...是串行着进行的,因为for循环是在main线程执行的,main线程需要等待join 线程执行

//而且加上join之后能成功是因为if (!sign.compareAndSet(null, current))一直为false,即不会有park出现,而如果多次unpark,只有一次park也不会出现什么问题,结果是许可处于可用状态。

}

Thread.currentThread().sleep(5000);

System.out.println(sum);

}

}

在他的代码中有两个问题:(1)对cas的使用错误,因为刚开始进入lock时cas的判断之后线程一直都不能park,而且cas这样频繁在多线程环境下改变的用法是不正确的;(2)在t.join()解开注释之后结果正确的原因在于lock中cas的判断部分错误,如果改成if (sign.compareAndSet(null, current))会发现线程会执行park而被挂起,然后所有的操作都阻塞。

关于LockSupport我以前也很少使用,现在摘抄部分内容如下,原文:

LockSupport是JDK中比较底层的类,用来创建锁和其他同步工具类的基本线程阻塞原语。java锁和同步器框架的核心 AQS: AbstractQueuedSynchronizer,就是通过调用 LockSupport .park()和 LockSupport .unpark()实现线程的阻塞和唤醒 的。 LockSupport 很类似于二元信号量(只有1个许可证可供使用),如果这个许可还没有被占用,当前线程获取许可并继 续 执行;如果许可已经被占用,当前线 程阻塞,等待获取许可。

public static void main(String[] args)

{

LockSupport.park();

System.out.println(“block.”);

}

运行该代码,可以发现主线程一直处于阻塞状态。因为 许可默认是被占用的 ,调用park()时获取不到许可,所以进入阻塞状态。

如下代码:先释放许可,再获取许可,主线程能够正常终止。LockSupport许可的获取和释放,一般来说是对应的,如果多次unpark,只有一次park也不会出现什么问题,结果是许可处于可用状态。

public static void main(String[] args)

{

Thread thread = Thread.currentThread();

LockSupport.unpark(thread);//释放许可

LockSupport.park();// 获取许可

System.out.println(“b”);

}

LockSupport是可不重入 的,如果一个线程连续2次调用 LockSupport .park(),那么该线程一定会一直阻塞下去。

public static void main(String[] args) throws Exception

{

Thread thread = Thread.currentThread();

LockSupport.unpark(thread);

System.out.println("a");

LockSupport.park();

System.out.println("b");

LockSupport.park();

System.out.println("c");

}

这段代码打印出a和b,不会打印c,因为第二次调用park的时候,线程无法获取许可出现死锁。

下面我们来看下LockSupport对应中断的响应性

public static void t2() throws Exception

{

Thread t = new Thread(new Runnable()

{

private int count = 0;

@Override

public void run()

{

long start = System.currentTimeMillis();

long end = 0;

while ((end - start) <= 1000)

{

count++;

end = System.currentTimeMillis();

}

System.out.println("after 1 second.count=" + count);

//等待或许许可

LockSupport.park();

System.out.println("thread over." + Thread.currentThread().isInterrupted());

}

});

t.start();

Thread.sleep(2000);

// 中断线程

t.interrupt();

System.out.println("main over");

}

最终线程会打印出thread over.true。这说明 线程如果因为调用park而阻塞的话,能够响应中断请求(中断状态被设置成true),但是不会抛出InterruptedException 。

结合群友的问题猜想他应该是想使用CAS做无锁操作,所以简单地写了个CAS的实现如下,望纠正:

public class MyLockSupport implements Runnable {

private final Object SIGN = new Object();

AtomicReference<Object> lock = new AtomicReference<>(SIGN);

public void lock(){

for(;;){

if(lock.compareAndSet(SIGN,Thread.currentThread())){//如果lock中放的还是SIGN的话,此处为true,然后里面的值会被改成当前的thread

break;

}else {

continue;

}

}

}

public void unlock(){

for(;;){

if(lock.compareAndSet(Thread.currentThread(),SIGN)){//如果lock中放的是当前线程,此处为true,并会将SIGN放会lock中去

break;

}else {

continue;

}

}

}

volatile static int sum =0;

@Override

public void run() {

lock();

sum++;

System.out.println(Thread.currentThread().getName()+"--"+sum);

unlock();

}

public static void main(String[] args) throws InterruptedException {

MyLockSupport bl=new MyLockSupport();

for(int i = 0; i < 1000; i++){

Thread t=new Thread(bl);

t.start();

// t.join();

}

Thread.currentThread().sleep(5000);

System.out.println(sum);

}

}

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-06-03,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 开发架构二三事 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档