亲宝软件园·资讯

展开

Android Lock锁实现原理详细分析

守住Android最后的光 人气:0

Lock简介

Lock接口位于J.U.C下locks包内,其定义了Lock应该具备的方法。

Lock 方法签名:

synchronized和lock的区别

写个Demo

static Lock lock = new ReentrantLock();
public static void main(String[] args) throws InterruptedException {         lock.lock();//其他没拿到锁的卡住不动         Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("start to get lock Interruptibly");
                lock.unlock(); //看看会发生什么,注释掉再看看
                lock.lock();
                System.out.println("拿到锁");
                lock.unlock();
                System.out.println("释放锁");
            }
        });
        thread.start();         Thread.sleep(3000);
        lock.unlock();
    }

我们自己来手写一下lock接口的tryLock()、lock()和unLock()方法,实现我们自己的myLock。

public class MyLock implements Lock {
    //多并发调用  0-未占用 大于0-占用
    AtomicInteger state = new AtomicInteger();     Thread ownerThread = new Thread();     //等待锁的队列
    LinkedBlockingQueue<Thread> waiters = new LinkedBlockingQueue();     @Override
    public void lock() {
        if (!tryLock()) {  //先抢锁,所以是非公平锁
            //没拿到锁,放到队列中去进行排队
            waiters.add(Thread.currentThread());
            //等待被唤醒
            for (; ; ) {
                if (tryLock()) {  //非公平锁情况下,唤醒过来继续获取锁
                    waiters.poll(); //获取锁成功把自己从队列中取出来
                    return;
                } else    //获取锁失败
                    LockSupport.park();  //线程阻塞
            }
        }
    }     @Override
    public boolean tryLock() {
        if (state.get() == 0) { //如果锁没被占用
            if (state.compareAndSet(0, 1)) {  //如果成功拿到锁
                ownerThread = Thread.currentThread();   //占用锁线程改为当前线程
                return true;
            }
        }
        return false;
    }     @Override
    public void unlock() {         if (ownerThread != Thread.currentThread())  //占用锁线程不是当前线程无法释放锁
            throw new RuntimeException("非法调用,当前锁不属于你");         if (state.decrementAndGet() == 0)  //如果成功释放锁
            ownerThread = null;  //占用锁线程置空
        //通知其他线程
//        Thread thread = null;
//
//        while ((thread = waiters.peek()) != null)
//            LockSupport.unpark(thread);
        Thread thread = waiters.peek(); //获取队列头部线程,线程还留在队列中
        if (thread != null) {
            LockSupport.unpark(thread); //取消阻塞
        }
    }     @Override
    public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
        return false;
    }     @Override
    public Condition newCondition() {
        return null;
    }     @Override
    public void lockInterruptibly() throws InterruptedException {     }
}

几个注意点:

Lock默认是非公平锁,上面实现的也是非公平锁,小伙伴们可以试一试。

公平锁和非公平锁区别:

先等待先获取锁是公平锁;先等待也不一定先获取锁,可能被突然到来的线程获取到是非公平锁;

公平锁的实现:

  @Override
    public void lock() {
       checkQueue();//线程来的时候先不获取锁,而是先检查队列中有没有等待的线程,如果有,直接放入队列,如果没有,再去获取锁
        if (!tryLock()) {  //先抢锁,所以是非公平锁
            //没拿到锁,放到队列中去进行排队
            waiters.add(Thread.currentThread());
            //等待被唤醒
            for (; ; ) {
                if (tryLock()) {  //非公平锁情况下,唤醒过来继续获取锁
                    waiters.poll(); //获取锁成功把自己从队列中取出来
                    return;
                } else    //获取锁失败
                    LockSupport.park();  //线程阻塞
            }
        }
    }

lock源码

在阅读源码的成长的过程中,有很多人会遇到很多困难,一个是源码太多,另一方面是源码看不懂。在阅读源码方面,我提供一些个人的建议:

接下来进入阅读lock的源码部分,在lock的接口中,主要的方法如下:

public interface Lock {
    // 加锁
    void lock();
    // 尝试获取锁
    boolean tryLock();
    boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
    // 解锁
    void unlock();
}

在lock接口的实现类中,最主要的就是ReentrantLock,来看看ReentrantLocklock()方法的源码:

    // 默认构造方法,非公平锁
    public ReentrantLock() {
        sync = new NonfairSync();
    }
    // 构造方法,公平锁
    public ReentrantLock(boolean fair) {
        sync = fair ? new FairSync() : new NonfairSync();
    }
    // 加锁
    public void lock() {
        sync.lock();
    }

在初始化lock实例对象的时候,可以提供一个boolean的参数,也可以不提供该参数。提供该参数就是公平锁,不提供该参数就是非公平锁。

总结

加载全部内容

相关教程
猜你喜欢
用户评论