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