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

群友的问题是想写个锁,他的代码如下:
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);
}

}
这个有个问题就是当开启的线程很多时,每个线程内部都相当于是while(true)不停重试,在电脑上测试时会导致cpu使用暴增,关于无锁编程部分请参见disruptor内的使用,关于disruptor源码解析部分,这位大神的博客有讲解

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值