多线程之Lock接口

      之前写了一下synchronized关键字的一点东西,那么除了synchronized可以加锁外,JUC(java.util.concurrent)提供的Lock接口也可以实现加锁解锁的功能。

       看完本文,希望您可以了解或者掌握:

       1:Lock接口的实现

        2:Condition的原理和概念

        3:ReentrantLock的实现原理,可以手写一个简单的ReentrantLock

         4:ReadWriteLock的概念和实现原理,可以手写一个简单的ReadWriteLock

         5:能够了解到模板模型,AQS

        锁的本质:

         1:加锁,其实就是加了一种权限或者说是规则。

         2:获得锁,其实也就是获得了访问资源的权限。

一:Lock接口 

上图是Lock接口中的一些方法,下面说下每个方法的作用:

1:lock(),lock接口是对资源进行加锁,而且加锁的时候是不死不休的,就是我加不到锁,我就一直等待着,直到我加到锁为止。

2:tryLock(),tryLock接口是尝试加锁,它就是我尝试一下去加锁,若是锁已经被占用,就不会再去等了。

3:tryLock(long time, TimeUnit unit),这个接口有个超时限制,若是锁已经被占用,就等待一段时间,时间到了后,要是锁还是被占用,就放弃。

4:lockInterruptibly(),lockInterruptibly是任人摆布的,就是说当有别的线程调用了interrupt打断它之后,它就不会再去加锁了。

下面是一个测试例子:

    //定义一个锁
    public static volatile 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("try lock start......");
                lock.tryLock();
                System.out.println("try lock end......");


                System.out.println("lock start......");
                lock.lock();
                System.out.println("lock end......");

                System.out.println("try lock start......");
                try {
                    lock.tryLock(5, TimeUnit.SECONDS);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("try lock end......");

                System.out.println("try lock start......");
                try {
                    lock.lockInterruptibly();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("try lock end......");
            }
        });
        thread.start();

        Thread.sleep(5000);
        thread.interrupt();

        Thread.sleep(2000);
        lock.unlock();
    }

二:Condition

      之前说了wait、notify,挂起和唤醒线程,那么Lock接口中也提供了一个Condition,Condition的底层实现机制是park和unpark,我们知道park和unpark当先唤醒后挂起时不会发生死锁,那么Condition也是一样,而且Condition中的挂起也有释放锁的语义,所以Condition在加锁或者先唤醒后挂起两种情况下都不会死锁,下面看下栗子:

    //定义一个锁
    static Lock lock = new ReentrantLock();

    static Condition condition = lock.newCondition();

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

        test1();
    }

    public static void test1() throws InterruptedException {
        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                lock.lock();
                System.out.println("子线程获取锁。。。");
                try {
                    System.out.println("子线程挂起开始。。。");
                    condition.await();
                    System.out.println("子线程挂起结束。。。");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    lock.unlock();
                }

            }
        });
        thread.start();

        Thread.sleep(2000);
        lock.lock();
        System.out.println("主线程获取锁。。。");
        condition.signal();
        lock.unlock();
    }

    public static void test2() throws InterruptedException {
        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                lock.lock();
                System.out.println("子线程获取锁。。。");
                try {
                    Thread.sleep(8000);
                    System.out.println("子线程挂起开始。。。");
                    condition.await();
                    System.out.println("子线程挂起结束。。。");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    lock.unlock();
                }

            }
        });
        thread.start();

        Thread.sleep(1000);
        lock.lock();
        System.out.println("主线程获取锁。。。");
        condition.signal();
        lock.unlock();
    }

上面测试例子中,两种情况都会执行完毕,不会死锁。

wait/notify提供的等待集合是单个的,而Condition可以提供多个等待集,下面是测试例子:

public class ConditionDemo2 {

    public static void main(String[] args) throws InterruptedException {
        ThreadQueue queue = new ThreadQueue(5);

        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i=0;i<20;i++){
                    try {
                        queue.put("元素" + i);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        });
        thread.start();

        Thread.sleep(3000);
        System.out.println("循环的从队列拿元素。。。");
        for (int i=0;i<10;i++){
            queue.get();
            Thread.sleep(3000);
        }
    }

}

//定义一个阻塞队列
//put元素时,若队列已满,就阻塞,直到再有空间,未满就put元素进去
//take元素时,若队列中没有元素,就阻塞,直到再有元素,有元素就直接取
class ThreadQueue {

    //定义一个可重入锁
    Lock lock = new ReentrantLock();
    Condition putCondition = lock.newCondition();
    Condition getCondition = lock.newCondition();
    //队列长度
    private volatile int length;
    //用来存放元素的集合
    List<Object> list = new ArrayList<>();

    public ThreadQueue(int length) {
        this.length = length;
    }

    //往队列中放元素
    public void put(Object obj) throws InterruptedException {
        lock.lock();
        for (;;) {
            //若队列还有空间,就直接放入队列,并且唤醒拿元素的等待集合,可以去拿元素了
            //若队列空间已经满了,就直接阻塞
            if (list.size() < length) {
                list.add(obj);
                System.out.println("put: " + obj);
                getCondition.signal();
                break;
            } else {
                putCondition.await();
            }
        }
        lock.unlock();
    }

    //从队列中拿元素
    public Object get() throws InterruptedException {
        lock.lock();
        Object obj;
        for (;;) {
            //若队列中有元素就直接取,然后把取走后的元素从队列中移除,并且唤醒放元素的等待集 
             合,可以继续放元素了
            //若队列中没有元素,就阻塞等待元素
            if (list.size() > 0) {
                obj = list.get(0);
                list.remove(0);
                System.out.println("get: " + obj);
                putCondition.signal();
                break;
            } else {
                getCondition.await();
            }
        }
        lock.unlock();
        return obj;
    }
}

三:ReentrantLock

        ReentrantLock是Lock接口的一个实现,它是一个可重入锁,会有一个值去记录重入的次数。若在一个线程中加锁加了n次,那么解锁就要调用n次,如果加锁次数大于解锁次数,就不能完全释放锁,若加锁次数小于解锁次数,即解锁多调了几次,那么就会报错。下面是例子:

   /定义一个锁
    static Lock lock = new ReentrantLock();

    public static void main(String[] args) {

        System.out.println("here i am 1.......");
        lock.lock();

        System.out.println("here i am 2.......");
        lock.lock();

        System.out.println("here i am 3.......");
        lock.unlock();

        lock.unlock();

        //会报java.lang.IllegalMonitorStateException异常
        //lock.unlock();

        new Thread(new Runnable() {
            @Override
            public void run() {
                lock.lock();
                System.out.println("子线程获取锁.......");
                lock.unlock();
            }
        }).start();
    }

下面简单实现一个可重入锁:

思考:

1. 实现可重入锁需要哪些准备呢?

2. 需要实现哪些方法呢?

那么,实现一个可重入锁,需要以下内容:

1:需要知道那个线程获取到了锁。

2:如果获取不到锁,就放入等待队列,那么就需要一个队列存放挂起的线程。

3:需要知道线程重入的次数,即需要一个变量去记录线程重入的次数。

4:需要加锁,解锁的方法

下面提供一个简单的实现,有大量注释,可以方便查阅:

public class ThreadLock2 implements Lock {

    //用于显示那个线程获取到锁
    public volatile AtomicReference<Thread> owner = new AtomicReference<>();

    //用于存放没有获取到锁,挂起的线程
    public static BlockingDeque<Thread> waiter = new LinkedBlockingDeque<>();

    //锁重入的次数
    volatile AtomicInteger count = new AtomicInteger(0);

    //尝试加锁
    @Override
    public boolean tryLock() {
        int ct = count.get();
        //ct!=0,说明锁已被占用
        if (ct != 0) {
            //判断锁的拥有者是不是当前线程,若是,就把重入次数加一
            if (owner.get() == Thread.currentThread()) {
                count.set(ct + 1);
                return true;
            }
        //锁未被占用,就去抢锁
        } else {
            //CAS方式去抢锁
            if (count.compareAndSet(ct, ct+1)) {
                owner.set(Thread.currentThread());
                return true;
            }
        }
        return false;
    }

    //加锁
    @Override
    public void lock() {
        if (!tryLock()) {
            //抢锁失败,就加入等待队列
            waiter.offer(Thread.currentThread());
            //循环的去抢锁
            for (;;) {
                //取出队列头部的线程
                Thread head = waiter.peek();
                //若队列头部的元素是当前线程,就去抢锁
                if (head == Thread.currentThread()) {
                    if (tryLock()) {
                        //抢到锁就从等待队列中取出
                        waiter.poll();
                        return;
                    } else {
                        //抢不到锁就直接挂起
                        LockSupport.park();
                    }
                } else {
                    //不是队列头部的线程,就直接挂起
                    LockSupport.park();
                }
            }
        }
    }

    //解锁
    @Override
    public void unlock() {
        if (tryUnlock()) {
            //释放锁成功后,就从等待队列中唤醒线程
            Thread thread = waiter.peek();
            if (thread != null) {
                LockSupport.unpark(thread);
            }
        }

    }

    //尝试解锁
    public boolean tryUnlock() {
        //判断当前线程是不是锁拥有者
        //不是就抛出异常
        //是的话就释放锁
        if (owner.get() != Thread.currentThread()) {
            //测试类运行可能会进入这个错误,这和环境可能有关,但是代码的具体实现思路应该是没有 
            //问题的
            throw new IllegalMonitorStateException();
        } else {
            //释放锁,就把重入次数减一
            int ct = count.get();
            int nextCt = ct - 1;
            count.set(nextCt);
            //当重入次数为0,就完全释放锁,把锁拥有者置为null
            if (nextCt == 0) {
                owner.set(null);
                return true;
            } else {
                return false;
            }
        }
    }


    @Override
    public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
        return false;
    }

    @Override
    public void lockInterruptibly() throws InterruptedException {

    }

    @Override
    public Condition newCondition() {
        return null;
    }
}

测试类:

    static ThreadLock2 lock = new ThreadLock2();

    static volatile int i = 0;

    public static void main(String[] args) throws InterruptedException {
        for (int i=0;i<6;i++) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    for (int i=0;i<100000;i++) {
                        add();
                    }
                }
            }).start();
        }
        Thread.sleep(2000);
        System.out.println(i);
    }

    public static void add() {
        lock.lock();
        i++;
        lock.unlock();
    }

四:synchronized和Lock的区别

4.1 synchronized

优点:

1. synchronized使用起来比较简单,语义也比较清晰。

2. JVM为synchronized提供了很多的优化,如锁消除,锁粗化,偏向锁等。

3. synchronized可以自动释放锁,可以避免死锁发生

缺点:

无法实现锁的一些高级特性,如公平锁,共享锁等。

4.2 Lock接口

优点:

synchronized的缺点就是Lock的缺点,Lock接口可以实现一些锁的高级特性。

缺点:

Lock接口需要手动的去释放锁,使用中不注意的话可能会造成死锁。

五:ReadWriteLock

        ReadWriteLock读写锁,提供了一个读锁,一个写锁,读锁是共享锁,可以由多个线程持有,写锁是独占锁,只能有一个线程可以获得写锁,读写锁适用于读场景比较多的地方。

 

       读写锁,一个线程获取到了读锁后就不能再去获取写锁,读写是互斥的,一个线程获取到了写锁,可以锁降级为读锁,降级为读锁后,就获取不了写锁了;写锁只能由一个线程获取(写写互斥),读锁可以由多个线程共同获取,想要获取写锁,只能等到所有的读锁全部释放后,才可以去获取,想要获取读锁,也要等到写锁释放后才可以获取。

获取写锁的过程:

获取读锁的过程:

下面是实现ReadWriteLock的一个例子:

public class ReadWriteLock2 {

    //锁拥有者
    private Thread owner = null;
    //等待队列,存放阻塞挂起的线程
    private LinkedBlockingDeque<Node> waiters = new LinkedBlockingDeque<>();
    //读锁
    private AtomicInteger readCount = new AtomicInteger(0);
    //写锁
    private AtomicInteger writeCount = new AtomicInteger(0);
    class Node {
        //标识是读锁还是写锁,0为读锁,1为写锁
        int type = 0;
        //线程
        Thread thread = null;
        int arg = 0;

        public Node(int type, Thread thread, int arg) {
            this.type = type;
            this.thread = thread;
            this.arg = arg;
        }
    }

    //获取写锁,即独占锁
    public void lock() {
        int arg = 1;
        //尝试获取独占锁,若成功则退出,若失败继续抢锁
        if (!tryLock(arg)) {
            //抢锁失败,就放入等待队列
            Node node = new Node(0, Thread.currentThread(), arg);
            waiters.offer(node);
            //自旋抢锁
            for(;;) {
                Node head = waiters.peek();
                //判断当前线程是否在队列头部,若在就尝试抢锁,否则就挂起
                if (head != null && head.thread == Thread.currentThread()) {
                    if (tryLock(arg)) {
                        //抢锁成功就把线程从队列中移除,然后返回
                        waiters.poll();
                        return;
                    } else {
                        //抢锁失败就挂起
                        LockSupport.park();
                    }
                } else {
                    //不是队列头部就挂起
                    LockSupport.park();
                }
            }
        }

    }

    //尝试获取写锁,即独占锁
    public boolean tryLock(int arg) {
        //若读锁已被获取,就返回false,因为要获取写锁,要等到所有的读锁释放
        if (readCount.get() != 0) {
            return false;
        }
        int ct = writeCount.get();
        if (ct != 0) {
            //若写锁已被获取,且是当前线程获取,那么就把writeCount加1
            if (owner == Thread.currentThread()) {
                writeCount.set(ct + arg);
                return true;
            }
        } else {
            //若写锁没被获取,就用CAS方式去抢锁
            if (writeCount.compareAndSet(ct, ct + arg)){
                //抢锁成功就把当前线程赋予owner
                owner = Thread.currentThread();
                return true;
            }
        }
        return false;
    }

    //释放写锁,即独占锁
    public boolean unLock() {
        int arg = 1;
        if (tryUnLock(arg)) {
            //释放锁成功,取出队列头部线程,唤醒
            Node headNode = waiters.peek();
            if (headNode != null) {
                LockSupport.unpark(headNode.thread);
            }
            return true;
        }
        return false;
    }

    //尝试释放写锁,即独占锁
    public boolean tryUnLock(int arg) {
        //若锁的拥有者不是当前线程就抛异常
        if (owner != Thread.currentThread()) {
            throw new IllegalMonitorStateException();
        } else {
            //若是当前线程拥有锁,就把writeCount减一,直到writeCount为0时,完全释放锁
            int ct = writeCount.get();
            int next = ct - arg;
            if (next == 0) {
                //把owner置为null,释放锁成功
                owner = null;
                return true;
            } else {
                return false;
            }
        }
    }

    //获取读锁,即共享锁
    public void sharedLock() {
        int arg = 1;
        if (trySharedLock(arg) < 0) {
            //抢锁失败,就放入等待队列
            Node node = new Node(1, Thread.currentThread(), arg);
            waiters.offer(node);

            for (;;) {
                Node headNode = waiters.peek();
                //判断当前线程是否在队列头部,若在就尝试抢锁,否则就挂起
                if (headNode != null && headNode.thread == Thread.currentThread()) {
                    if (trySharedLock(arg) >= 0) {
                        //抢锁成功就把线程从队列中移除,然后返回
                        waiters.poll();
                        //若下一个线程是读锁,就把它唤醒
                        Node next = waiters.peek();
                        if (next != null && next.type == 0) {
                            LockSupport.unpark(next.thread);
                        }
                    } else {
                        //抢锁失败就挂起
                        LockSupport.park();
                    }
                } else {
                    //不是队列头部就挂起
                    LockSupport.park();
                }
            }
        }
    }

    //尝试获取读锁,即共享锁
    public int trySharedLock(int arg) {
        //自旋抢锁
        for (;;) {
            //若写锁没有释放,且不是当前线程持有,就返回错误值-1,因为写锁释放后,才可以获取读锁
            if (writeCount.get() != 0 && owner != Thread.currentThread()) {
                return -1;
            }
            int ct = readCount.get();
            if (readCount.compareAndSet(ct, ct + arg)) {
                return 1;
            }

        }

    }

    //尝试释放读锁,即共享锁
    public boolean tryUnSharedLock(int arg) {
        for (;;) {
            int ct = readCount.get();
            int nextCt = ct - arg;
            //直到readCount为0时才是完全释放锁
            if (readCount.compareAndSet(ct, nextCt)) {
                return nextCt == 0;
            }
        }
    }

    //释放读锁,即共享锁
    public boolean unSharedLock() {
        int arg = 1;
        if (tryUnSharedLock(arg)) {
            //释放锁成功,取出队列头部线程,唤醒
            Node headNode = waiters.peek();
            if (headNode != null) {
                LockSupport.unpark(headNode.thread);
            }
            return true;
        }
        return false;
    }
}

测试类:

    static ReadWriteLock2 lock = new ReadWriteLock2();

    volatile static int i = 0;

    static void add() {
        i++;
    }

    public static void main(String[] args) throws InterruptedException {
        long startTime = System.currentTimeMillis();


        for (int a=1; a<=20000; a++){
            final int n = a;
            new Thread(new Runnable() {
                @Override
                public void run() {
                    if (n%5 ==0){
                        lock.lock();
                        add();
                        lock.unLock();
                    }else{
                        lock.sharedLock();
                        System.out.println("i=" +i);
                        int a = i;
                        lock.unSharedLock();
                    }
                }
            }).start();
        }


        while (true){
            System.out.println("目前耗时:" + (System.currentTimeMillis()-startTime) /1000 + "s");
            Thread.sleep(1000L);
            System.out.println("i=" + i);

        }
    }

下面提供另外两个例子:改造hashMap为安全的,和实现模拟一个缓存:

public class MapDemo {
    //将hashMap改造成线程安全的
    private Map<String, Object> map = new HashMap<>();

    private ReadWriteLock lock = new ReentrantReadWriteLock();
    //初始化读锁和写锁
    private Lock writeLock = lock.writeLock();
    private Lock readLock = lock.readLock();

    //根据key值获取元素
    public Object get(String key) {
        readLock.lock();
        try {
           return map.get(key);
        } finally {
            readLock.unlock();
        }
    }

    //获取所有的key
    public Object[] allKeys() {
        readLock.lock();
        try {
            return map.keySet().toArray();
        } finally {
            readLock.unlock();
        }
    }

    //放入元素
    public Object put(String key, String value) {
        writeLock.lock();
        try {
            return map.put(key, value);
        } finally {
            writeLock.unlock();
        }
    }

    //清除所有元素
    public void clear() {
        writeLock.lock();
        try {
           map.clear();
        } finally {
            writeLock.unlock();
        }
    }
}
//做一个缓存,从缓存中取元素
public class CacheDemo {

    //初始化一个读写锁
    private static ReadWriteLock lock = new ReentrantReadWriteLock();
    //用于判断缓存是否可用,是否有元素
    private static volatile boolean isCache;

    static Object get(String key) {
        Object obj = null;
        //加读锁
        lock.readLock().lock();
        try {
            //若缓存可用,直接从缓存中取数据
            if(isCache) {
                obj = Cache.map.get(key);
            } else {
                //释放读锁
                lock.readLock().unlock();
                //加写锁,并不会马上获取到写锁,会等到所有读锁释放
                lock.writeLock().lock();
                try {
                    //若缓存不可用,从数据库取数据,然后放入缓存
                    if (!isCache) {
                        obj = dataBaseGetData.getData();
                        Cache.map.put(key, obj);
                        isCache = true;
                    }
                    //锁降级,将写锁降级为读锁
                    lock.readLock().lock();
                }  finally {
                    //释放写锁
                    lock.writeLock().unlock();
                }
            }
        } finally {
            //释放读锁
            lock.readLock().unlock();
        }
        return obj;
    }

}

//模拟从数据库获取元素
class dataBaseGetData {
    static String getData() {
        System.out.println("从数据库取元素。。。");
        return "name:hello,age:20";
    }
}

//模拟缓存
class Cache {
    static Map<String, Object> map = new HashMap<>();
}

六:模板模式

模板模式就是说,有一个母版,就像PPT一样,然后把母版复制过来自己去实现自己想要的就可以了,下面是个简单的例子:

//母版
class mb {
    void title() {
        throw new UnsupportedOperationException();
    }

    void content() {
        throw new UnsupportedOperationException();
    }

    void foot() {
        throw new UnsupportedOperationException();
    }

    public final void show() {

        System.out.println("这是一个标题");
        title();
        System.out.println("字体:微软雅黑");

        System.out.println("这是内容:");
        content();
        System.out.println("内容结束");

        System.out.println("这是底部:");
        foot();
    }
}

class PPT1 extends mb {
    @Override
    void title() {
        System.out.print("你好啊");
    }

    @Override
    void content() {
        System.out.print("java。。。");
        System.out.print("c++");
        System.out.print("中国");
    }

    @Override
    void foot() {
        System.out.print("结束了");
    }
}

根据上面的例子改造ReentrantLock和ReadWriteLock的实现方式,先做一个模板:

public class CommonLock {

    //锁拥有者
    Thread owner = null;
    //等待队列,存放阻塞挂起的线程
    LinkedBlockingDeque<Node> waiters = new LinkedBlockingDeque<>();
    //读锁
    AtomicInteger readCount = new AtomicInteger(0);
    //写锁
    AtomicInteger writeCount = new AtomicInteger(0);
    class Node {
        //标识是读锁还是写锁,0为读锁,1为写锁
        int type = 0;
        //线程
        Thread thread = null;
        int arg = 0;

        public Node(int type, Thread thread, int arg) {
            this.type = type;
            this.thread = thread;
            this.arg = arg;
        }
    }

    //获取读锁,即独占锁
    public void lock() {
        int arg = 1;
        //尝试获取独占锁,若成功则退出,若失败继续抢锁
        if (!tryLock(arg)) {
            //抢锁失败,就放入等待队列
            Node node = new Node(0, Thread.currentThread(), arg);
            waiters.offer(node);
            //自旋抢锁
            for(;;) {
                Node head = waiters.peek();
                //判断当前线程是否在队列头部,若在就尝试抢锁,否则就挂起
                if (head != null && head.thread == Thread.currentThread()) {
                    if (tryLock(arg)) {
                        //抢锁成功就把线程从队列中移除,然后返回
                        waiters.poll();
                        return;
                    } else {
                        //抢锁失败就挂起
                        LockSupport.park();
                    }
                } else {
                    //不是队列头部就挂起
                    LockSupport.park();
                }
            }
        }

    }


    //释放写锁,即独占锁
    public boolean unLock() {
        int arg = 1;
        if (tryUnLock(arg)) {
            //释放锁成功,取出队列头部线程,唤醒
            Node headNode = waiters.peek();
            if (headNode != null) {
                LockSupport.unpark(headNode.thread);
            }
            return true;
        }
        return false;
    }

    //获取读锁,即共享锁
    public void sharedLock() {
        int arg = 1;
        if (trySharedLock(arg) < 0) {
            //抢锁失败,就放入等待队列
            Node node = new Node(1, Thread.currentThread(), arg);
            waiters.offer(node);

            for (;;) {
                Node headNode = waiters.peek();
                //判断当前线程是否在队列头部,若在就尝试抢锁,否则就挂起
                if (headNode != null && headNode.thread == Thread.currentThread()) {
                    if (trySharedLock(arg) >= 0) {
                        //抢锁成功就把线程从队列中移除,然后返回
                        waiters.poll();
                        //若下一个线程是读锁,就把它唤醒
                        Node next = waiters.peek();
                        if (next != null && next.type == 1) {
                            LockSupport.unpark(next.thread);
                        }
                    } else {
                        //抢锁失败就挂起
                        LockSupport.park();
                    }
                } else {
                    //不是队列头部就挂起
                    LockSupport.park();
                }
            }
        }
    }

    //释放读锁,即共享锁
    public boolean unSharedLock() {
        int arg = 1;
        if (tryUnSharedLock(arg)) {
            //释放锁成功,取出队列头部线程,唤醒
            Node headNode = waiters.peek();
            if (headNode != null) {
                LockSupport.unpark(headNode.thread);
            }
            return true;
        }
        return false;
    }

    //尝试获取写锁,即独占锁
    public boolean tryLock(int arg) {
        throw new UnsupportedOperationException();
    }

    //尝试释放写锁,即独占锁
    public boolean tryUnLock(int arg) {
        throw new UnsupportedOperationException();
    }

    //尝试获取读锁,即共享锁
    public int trySharedLock(int arg) {
        throw new UnsupportedOperationException();

    }

    //尝试释放读锁,即共享锁
    public boolean tryUnSharedLock(int arg) {
        throw new UnsupportedOperationException();
    }

}

改造后的ReentrantLock:

 //private Thread owner = null;

    //锁拥有者
    volatile AtomicReference<Thread> owner = new AtomicReference<>();

    //等待队列
    private LinkedBlockingQueue<Thread> waiters = new LinkedBlockingQueue<>();

    //记录重入的次数
    volatile AtomicInteger count = new AtomicInteger(0);

    CommonLock lock = new CommonLock(){
        //尝试获取写锁,即独占锁
        @Override
        public boolean tryLock(int arg) {
            //若读锁已被获取,就返回false,因为要获取写锁,要等到所有的读锁释放
            if (lock.readCount.get() != 0) {
                return false;
            }
            int ct = lock.writeCount.get();
            if (ct != 0) {
                //若写锁已被获取,且是当前线程获取,那么就把writeCount加1
                if (lock.owner == Thread.currentThread()) {
                    lock.writeCount.set(ct + arg);
                    return true;
                }
            } else {
                //若写锁没被获取,就用CAS方式去抢锁
                if (lock.writeCount.compareAndSet(ct, ct + arg)){
                    //抢锁成功就把当前线程赋予owner
                    lock.owner = Thread.currentThread();
                    return true;
                }
            }
            return false;
        }

        //尝试释放写锁,即独占锁
        @Override
        public boolean tryUnLock(int arg) {
            //若锁的拥有者不是当前线程就抛异常
            if (lock.owner != Thread.currentThread()) {
                throw new IllegalMonitorStateException();
            } else {
                //若是当前线程拥有锁,就把writeCount减一,直到writeCount为0时,完全释放锁
                int ct = lock.writeCount.get();
                int next = ct - arg;
                if (next == 0) {
                    //把owner置为null,释放锁成功
                    lock.owner = null;
                    return true;
                } else {
                    return false;
                }
            }
        }
    };

    @Override
    public void lock() {
       lock.lock();
    }

    @Override
    public void unlock() {
       lock.unLock();
    }

    @Override
    public void lockInterruptibly() throws InterruptedException {

    }

    @Override
    public boolean tryLock() {
        return lock.tryLock(1);
    }

    public boolean tryUnlock() {
        return lock.tryUnLock(1);
    }

    @Override
    public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
        return false;
    }



    @Override
    public Condition newCondition() {
        return null;
    }

改造后的ReadWriteLock:

public class ReadWriteLock2 implements ReadWriteLock {

    CommonLock lock = new CommonLock(){
        //尝试获取写锁,即独占锁
        @Override
        public boolean tryLock(int arg) {
            //若读锁已被获取,就返回false,因为要获取写锁,要等到所有的读锁释放
            if (lock.readCount.get() != 0) {
                return false;
            }
            int ct = lock.writeCount.get();
            if (ct != 0) {
                //若写锁已被获取,且是当前线程获取,那么就把writeCount加1
                if (lock.owner == Thread.currentThread()) {
                    lock.writeCount.set(ct + arg);
                    return true;
                }
            } else {
                //若写锁没被获取,就用CAS方式去抢锁
                if (lock.writeCount.compareAndSet(ct, ct + arg)){
                    //抢锁成功就把当前线程赋予owner
                    lock.owner = Thread.currentThread();
                    return true;
                }
            }
            return false;
        }

        //尝试释放写锁,即独占锁
        @Override
        public boolean tryUnLock(int arg) {
            //若锁的拥有者不是当前线程就抛异常
            if (lock.owner != Thread.currentThread()) {
                throw new IllegalMonitorStateException();
            } else {
                //若是当前线程拥有锁,就把writeCount减一,直到writeCount为0时,完全释放锁
                int ct = lock.writeCount.get();
                int next = ct - arg;
                if (next == 0) {
                    //把owner置为null,释放锁成功
                    lock.owner = null;
                    return true;
                } else {
                    return false;
                }
            }
        }

        //尝试获取读锁,即共享锁
        @Override
        public int trySharedLock(int arg) {
            //自旋抢锁
            for (;;) {
                //若写锁没有释放,且不是当前线程持有,就返回错误值-1,因为写锁释放后,才可以获取读锁
                if (writeCount.get() != 0 && owner != Thread.currentThread()) {
                    return -1;
                }
                int ct = readCount.get();
                if (readCount.compareAndSet(ct, ct + arg)) {
                    return 1;
                }

            }

        }

        //尝试释放读锁,即共享锁
        @Override
        public boolean tryUnSharedLock(int arg) {
            for (;;) {
                int ct = readCount.get();
                int nextCt = ct - arg;
                //直到readCount为0时才是完全释放锁
                if (readCount.compareAndSet(ct, nextCt)) {
                    return nextCt == 0;
                }
            }
        }
    };

    @Override
    public Lock readLock() {
        return new Lock() {

            @Override
            public void lock() {
                lock.sharedLock();
            }

            @Override
            public void lockInterruptibly() throws InterruptedException {

            }

            @Override
            public boolean tryLock() {
                return lock.trySharedLock(0) == 1;
            }

            @Override
            public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
                return false;
            }

            @Override
            public void unlock() {
                lock.tryUnSharedLock(0);
            }

            @Override
            public Condition newCondition() {
                return null;
            }
        };
    }

    @Override
    public Lock writeLock() {
        return new Lock() {
            @Override
            public void lock() {
                lock.lock();
            }

            @Override
            public void lockInterruptibly() throws InterruptedException {

            }

            @Override
            public boolean tryLock() {
                return lock.tryLock(1);
            }

            @Override
            public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
                return false;
            }

            @Override
            public void unlock() {
                lock.unLock();
            }

            @Override
            public Condition newCondition() {
                return null;
            }
        };
    }
}

 七:AQS抽象队列同步器

       JUC包就是基于AQS实现的,AQS的设计模式就是模板模式,数据结构是双向链表和锁状态(state),底层实现是CAS。它的state不像上面读写锁说的是两个count,它用一个state实现,即我们知道int类型有8个字节,它的实现是前4个字节可以标识读锁,后四个字节可以标识写锁。

AQS本文不做详细解释。。。

好了到此,Lock接口的相关内容已经结束了。

发布了21 篇原创文章 · 获赞 8 · 访问量 4367

猜你喜欢

转载自blog.csdn.net/qq_34365173/article/details/105328278