java多线程之LockSupport及ReentrantLock

目前java语言层面能够实现线程的阻塞与唤醒,主要包含两个组合对wait/notify以及park/unpark。wait/notify这组组合中wait必须发生在notify前面,否则很可能造成线程会一直阻塞,而park与unpark则不必,但是如果unpark先执行,后面执行park操作将不会阻塞。JDK并发中的AQS框架使用的就是LockSupport中的park/unpark操作,实际上调用的是Unsafe类的底层操作.

 public static void park() {//组设
        U.park(false, 0L);
    }

    public static void unpark(Thread thread) {//唤醒
        if (thread != null)
            U.unpark(thread);//调用UnSafe类的底层操作.
    }

    public static void parkNanos(long nanos)//可以指定等待的最长时间,参数是相对于当前时间的纳秒数,超时解锁
    public static void parkUntil(long deadline) //指定等待的最长时间毫秒数,它是相对于积怨开始的时间,调用时需要System.currentTimemillis()+durationMillis

    //下面这几个方法与上面的差不多,只是多了一个blocker参数,表示由于该对象造成的等待,一般传this
    public static void park(Object blocker)
    public static void parkNanos(Object blocker, long nanos)
    public static void parkUntil(Object blocker, long deadline)  

eg.

@Test
    public void testLockSupport(){

        Thread thread=new Thread(){

            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName()+"11111");
                LockSupport.park();//阻塞放弃cpu执行,进入等待队列,必须等待别的线程调用unpark解锁才可以继续执行(不同于yield)
                System.out.println(Thread.currentThread().getName()+"22222");
            }
        };
        try {
            thread.start();
            Thread.sleep(1000);
        }catch (InterruptedException e){}
        System.out.println(Thread.currentThread().getId());
        LockSupport.unpark(thread);//唤醒,主线程调用unpark(thread)解锁
    }

    输出:
    Thread-011111
    1
    Thread-022222

ReentrantLock是java中的显示锁,它有公平锁(按照申请锁的顺序,FIFO的顺序获取锁)和非公平锁之分(线程间竞争锁,synchronized是非公平锁),在其构造函数中传参的不同决定使用公平锁还是非公平锁,实现了Lock接口。

public class ReentrantLock implements Lock, Serializable {
    private static final long serialVersionUID = 7373984872572414699L;
    private final ReentrantLock.Sync sync;

    public ReentrantLock() {//默认情况下是非公平锁
        this.sync = new ReentrantLock.NonfairSync();//实例化Sync,它是AQS中的核心类,后续会讲到
    }

    public ReentrantLock(boolean var1) {//var1若为true采用公平锁,否则为非公平锁.
        this.sync = (ReentrantLock.Sync)(var1?new ReentrantLock.FairSync():new ReentrantLock.NonfairSync());
    }

    public void lock() {
        this.sync.lock();//其实是调用sync来实现加锁
    }
    .....
   }



   public interface Lock {
    void lock();//获取锁操作.lock()会获取锁直到成功

    void lockInterruptibly() throws InterruptedException;//与lock不同的是它可以获取中断,然后抛出InterruptedException

    boolean tryLock();//尝试获取锁,不会阻塞

    boolean tryLock(long var1, TimeUnit var3) throws InterruptedException;// 在指定的时间内尝试获取锁

    void unlock();//解锁

    Condition newCondition();//这个很重要,是"锁上的条件"


    public interface Condition {
    void await() throws InterruptedException;//相当于Object中的wait

    void awaitUninterruptibly();

    long awaitNanos(long var1) throws InterruptedException;

    boolean await(long var1, TimeUnit var3) throws InterruptedException;

    boolean awaitUntil(Date var1) throws InterruptedException;

    void signal();//相当于Object中的notify

    void signalAll();//相当于Object中的notifyAll
}
}

eg.

 @Test
    public void testReentrantLock() {

        final Lock lock = new ReentrantLock(false);
        lock.lock();

        try {
            //doSomething(); 
        } catch (Exception e) {
        } finally {
            lock.unlock();//必须显示的调用unlock
        }
    }

    需要注意的是synchronized修饰的方法或者同步代码块,在方法执行完毕的时候会自动释放锁,而ReentrantLock需要手动的调用lock.unlock()释放锁,为了避免出现异常情况,一般在finally中做释放锁的操作.

当我们调用lock的时候实际上执行的是sync.lock这里以非公平锁为例,sync=new ReentrantLock.NonfairSync();


//AQS简化了并发数据的处理
abstract static class Sync extends AbstractQueuedSynchronizer
{
    AQS中封装了一个状态的设置和获得,方便子类的调用

    protected AbstractQueuedSynchronizer() {}

    protected final int getState() {
        return this.state;
    }

    protected final void setState(int var1) {
        this.state = var1;
    }

    protected final boolean compareAndSetState(int var1, int var2) {
        return unsafe.compareAndSwapInt(this, stateOffset, var1, var2);
    }
}

   //下面的方法是设置和获取当前持有锁的线程  
   protected final void setExclusiveOwnerThread(Thread var1) {
        this.exclusiveOwnerThread = var1;
    }

    protected final Thread getExclusiveOwnerThread() {
        return this.exclusiveOwnerThread;
    }


static final class NonfairSync extends ReentrantLock.Sync {
        private static final long serialVersionUID = 7316153563782823691L;

        NonfairSync() {
        }

        final void lock() {
            if(this.compareAndSetState(0, 1)) {//如果当前锁的数量为0,表示未被锁定则将当前线程持有锁并将state+1持有锁定
                this.setExclusiveOwnerThread(Thread.currentThread());
            } else {
                this.acquire(1);//如果当前锁被别的线程持有锁定则调用AQS中的acquire方法
            }

        }

        protected final boolean tryAcquire(int var1) {
            return this.nonfairTryAcquire(var1);// 调用nonfairTryAcquire
        }
    }

    final boolean nonfairTryAcquire(int var1) {
            Thread var2 = Thread.currentThread();
            int var3 = this.getState();
            if(var3 == 0) {
                if(this.compareAndSetState(0, var1)) {//如果当前没有持有锁,设置持有锁的线程并返回
                    this.setExclusiveOwnerThread(var2);
                    return true;
                }
            } else if(var2 == this.getExclusiveOwnerThread()) {
                int var4 = var3 + var1;//如果当前线程持有锁就将锁的数量加1并返回true
                if(var4 < 0) {
                    throw new Error("Maximum lock count exceeded");
                }

                this.setState(var4);
                return true;
            }

            return false;//否则返回false
        }

    AQS:
    public final void acquire(int var1) {
        if(!this.tryAcquire(var1) && //调用子类NonfairSync的tryAcquire获取锁
        this.acquireQueued(this.addWaiter(AbstractQueuedSynchronizer.Node.EXCLUSIVE), var1)) {//如何tryAcquire返回false执行acquireQueued,创建当前线程的node加入队列中,在队列中尝试获取锁
            selfInterrupt();
        }

    }

    final boolean acquireQueued(AbstractQueuedSynchronizer.Node var1, int var2) {
        boolean var3 = true;

        try {
            boolean var4 = false;

            while(true) {
                AbstractQueuedSynchronizer.Node var5 = var1.predecessor();
                if(var5 == this.head && this.tryAcquire(var2)) {
                    this.setHead(var1);
                    var5.next = null;
                    var3 = false;
                    boolean var6 = var4;
                    return var6;
                }

                if(shouldParkAfterFailedAcquire(var5, var1) && this.parkAndCheckInterrupt()) {
                    var4 = true;
                }
            }
        } finally {
            if(var3) {
                this.cancelAcquire(var1);
            }

        }
    }

    主体是一个死循环,在每次循环中,首先检查当前节点的前置节点是不是第一个等待的节点,
    如果是且能获得到锁,则将当前节点从等待队列中移除并返回,
    否则最终调用LockSupport.park放弃CPU,进入等待,
    被唤醒后,检查是否发生了中断,记录中断标志,
    在最终方法返回时返回中断标志。如果发生过中断,acquire方法最终会调用selfInterrupt方法设置中断标志位。   

ReentrantLock的unlock方法的代码为:

public void unlock() {
        this.sync.release(1);
    }

    public final boolean release(int var1) {
        if(this.tryRelease(var1)) {
            AbstractQueuedSynchronizer.Node var2 = this.head;
            if(var2 != null && var2.waitStatus != 0) {
                this.unparkSuccessor(var2);//调用unparkSuccessor方法释放锁.
            }

            return true;
        } else {
            return false;
        }
    }

上面的逻辑比较乱总结起来就是:

阻塞

if(尝试获取锁失败) {
创建node
使用CAS方式把node插入到队列尾部
while(true){
if(尝试获取锁成功 并且 node的前驱节点为头节点){
把当前节点设置为头节点
跳出循环
}else{
使用CAS方式修改node前驱节点的waitStatus标识为signal
if(修改成功)
LockSupport.park();
}
}
}

唤醒

if(尝试释放锁成功){
LockSupport.unpark(下一节点包含的线程);
}

假如一条线程参与锁竞争,首先先尝试获取锁,失败的话创建节点并插入队列尾部,然后再次尝试获取锁,如若成功则不做其他任务处理直接返回,否则设置节点状态为待运行状态,最后使用LockSupport的park阻塞当前线程。前驱节点运行完后将尝试唤醒后继节点,使用的即是LockSupport的unpark唤醒。

参考显示锁(Lock)及Condition的学习与使用

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值