多线程与高并发(三)–基于CAS的新类型锁

ReentrantLock

  • 可重入锁(synchronized也是可重入锁)。
  • 必须手动释放锁,使用synchronized如果遇到异常会自动释放锁,但是reentrantLock必须手动释放,因此需要在finally中进行锁的释放。
  • 可以使用tryLock进行尝试锁定,不管锁定与否,方法都将继续运行。可以根据tryLock的返回值来判断是否锁定,也可以指定tryLock的时间。
  • 使用ReentrantLock还可以调用lockInterruptibly方法,可以对线程interrupt方法做出响应,在一个线程等待锁的过程中,可以被打断。
  • 可以使用多种不同的condition,代表不同的等待队列。
  • 可以指定为公平锁。

CountDownLatch

使一个线程等待其他线程各自执行完毕后再执行。

CyclicBarrier

  • 让所有线程都等待完成后才会继续下一步行动。
  • CyclicBarrier可以用于多线程计算数据,最后合并计算结果的应用场景。

Phaser

使用场景为N个线程分阶段并行的问题–多人闯关。

ReadWriteLock

  • 读锁:共享锁
  • 写锁:排他锁

Semaphore

控制同时访问特定资源的线程数量–限流

LockSupport

  • LockSupport.park():使当前线程阻塞。
  • LockSupport.unpark(Thread.name):唤醒阻塞线程。

公平锁

在发生线程争用,未获取到锁的线程会在等待队列中等待。如果是公平锁,新发出请求的线程会先判断等待队列中是否有线程在等待,如果有就进入等待队列,不会直接去争抢锁。如果是非公平锁,会直接尝试去获取锁。获取锁钱会判断队列中是否有线程在等待是判断公平锁与非公平锁的关键。

面试题

  1. 实现一个容器,提供add,size方法。写两个线程,线程1添加十个元素到容器中,线程2实现监控元素的个数,当个数到5个时,线程2给出提示并结束。

    1. wait-notify(notify不释放锁)
    package testThread;
    
    import java.util.ArrayList;
    import java.util.List;
    
    public class WaitNotify {
    
        private List<Integer> list = new ArrayList<>();
    
        public void add (int i) {
            list.add(i);
            System.out.println("add one!");
        }
    
        public int size () {
            return list.size();
        }
    
        public static void main(String[] args) {
            WaitNotify waitNotify = new WaitNotify();
    
            final Object lock = new Object();
    
            new Thread(() -> {
                synchronized (lock) {
                    for (int i = 0; i < 10; i++) {
                        waitNotify.add(i);
                        if (waitNotify.size() == 5) {
                            lock.notify();
                            try {
                                lock.wait();
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                        }
                    }
                }
            }, "t1").start();
    
            new Thread(() -> {
                synchronized (lock) {
                    if (waitNotify.size() != 5) {
                        try {
                            lock.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    System.out.println("t2 end! waitNotify's size is :" + waitNotify.size());
                    lock.notify();
                }
            }, "t2").start();
        }
    }
    复制代码
    1. CountDownLatch
    package testThread;
    
    import java.util.ArrayList;
    import java.util.List;
    import java.util.concurrent.CountDownLatch;
    
    public class CountDownLatchTest {
        private List<Integer> list = new ArrayList<>();
    
        public void add(int i) {
            list.add(i);
            System.out.println("add one!");
        }
    
        public int size() {
            return list.size();
        }
    
        public static void main(String[] args) {
            CountDownLatchTest countDownLatchTest = new CountDownLatchTest();
            CountDownLatch countDownLatch = new CountDownLatch(1);
            CountDownLatch otherLatch = new CountDownLatch(1);
            new Thread(() -> {
                for (int i = 0; i < 10; i++) {
                    countDownLatchTest.add(i);
                    if (countDownLatchTest.size() == 5) {
                        countDownLatch.countDown();
                        try {
                            otherLatch.await();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }, "t1").start();
    
            new Thread(() -> {
                try {
                    countDownLatch.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("t2 end! countDownLatchTest's size is :" + countDownLatchTest.size());
                otherLatch.countDown();
            }, "t2").start();
        }
    }
    复制代码
    1. LockSupport
    package testThread;
    
    import java.util.ArrayList;
    import java.util.List;
    import java.util.concurrent.locks.LockSupport;
    
    public class LockSupportTest {
    
        private List<Integer> list = new ArrayList<>();
    
        static Thread t1 = null;
    
        static Thread t2 = null;
    
        public void add(int i) {
            list.add(i);
            System.out.println("add one!");
        }
    
        public int size() {
            return list.size();
        }
    
        public static void main(String[] args) {
            LockSupportTest lockSupportTest = new LockSupportTest();
    
            t1 = new Thread(() -> {
                for (int i = 0; i < 10; i++) {
                    lockSupportTest.add(i);
                    if (lockSupportTest.size() == 5) {
                        LockSupport.unpark(t2);
                        LockSupport.park();
                    }
                }
            }, "t1");
    
            t2 = new Thread(() -> {
                LockSupport.park();
                System.out.println("t2 end! lockSupportTest's size is :" + lockSupportTest.size());
                LockSupport.unpark(t1);
            }, "t2");
    
            t1.start();
            t2.start();
        }
    }
    复制代码
    1. Semaphore
    package testThread;
    
    import java.util.ArrayList;
    import java.util.List;
    import java.util.concurrent.locks.LockSupport;
    
    public class LockSupportTest {
    
        private List<Integer> list = new ArrayList<>();
    
        static Thread t1 = null;
    
        static Thread t2 = null;
    
        public void add(int i) {
            list.add(i);
            System.out.println("add one!");
        }
    
        public int size() {
            return list.size();
        }
    
        public static void main(String[] args) {
            LockSupportTest lockSupportTest = new LockSupportTest();
    
            t1 = new Thread(() -> {
                for (int i = 0; i < 10; i++) {
                    lockSupportTest.add(i);
                    if (lockSupportTest.size() == 5) {
                        LockSupport.unpark(t2);
                        LockSupport.park();
                    }
                }
            }, "t1");
    
            t2 = new Thread(() -> {
                LockSupport.park();
                System.out.println("t2 end! lockSupportTest's size is :" + lockSupportTest.size());
                LockSupport.unpark(t1);
            }, "t2");
    
            t1.start();
            t2.start();
        }
    }
    复制代码
  2. 要求使用线程顺序打印A1B2C3…Z26

    1. wait-notify
    package testThread;
    
    public class AZWaitNotify {
    
        public static void main(String[] args) {
            Object lock = new Object();
    
            Thread t1 = new Thread(() -> {
                synchronized (lock) {
                    for (int i = 1; i <= 26; i++) {
                        System.out.print(i);
                        lock.notify();
                        try {
                            lock.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }, "t1");
    
            new Thread(() -> {
                synchronized (lock) {
                    t1.start();
                    for (char i = 'A'; i <= 'Z'; i++) {
                        System.out.print(i);
                        lock.notify();
                        try {
                            lock.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    // 必须加上notify,否则程序不会停止
                    lock.notify();
                }
            }, "t2").start();
        }
    }
    复制代码
    1. LockSupport
    package testThread;
    
    import java.util.concurrent.locks.LockSupport;
    
    public class AZLockSupport {
    
        static Thread t1 = null, t2 = null;
    
        public static void main(String[] args) {
            t1 = new Thread(() -> {
                for (int i = 1; i <= 26; i++) {
                    System.out.print(i);
                    LockSupport.unpark(t2);
                    LockSupport.park();
                }
            }, "t1");
    
            t2 = new Thread(() -> {
                t1.start();
                for (char i = 'A'; i <= 'Z'; i++) {
                    System.out.print(i);
                    LockSupport.unpark(t1);
                    LockSupport.park();
                }
            }, "t2");
    
            t2.start();
        }
    }
    复制代码
  3. 写一个固定容量同步容器,拥有put,get以及getCount方法,能够支持2个生产者线程以及10个消费者线程的阻塞调用。

    1. wait-notify
    package testThread;
    
    import java.util.LinkedList;
    
    public class PutGetWaitNotify {
    
        private final LinkedList<Integer> list = new LinkedList<>();
    
        private final Integer MAX = 10;
    
        public synchronized void put (int i) {
            while (list.size() == MAX) {
                try {
                    this.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            list.add(i);
            System.out.println(Thread.currentThread().getName() + ": put one! And size is:" + getCount());
            this.notifyAll();
        }
    
        public synchronized Integer get () {
            while (list.size() == 0) {
                try {
                    this.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            Integer ret = list.removeFirst();
            System.out.println(Thread.currentThread().getName() + ": get one! And size is:" + getCount());
            this.notifyAll();
            return ret;
        }
    
        public synchronized int getCount () {
            return list.size();
        }
    
        public static void main(String[] args) {
            PutGetWaitNotify putGetWaitNotify = new PutGetWaitNotify();
    
            Thread[] putThread = new Thread[2];
            Thread[] getThread = new Thread[10];
    
            for (int i = 0; i < putThread.length; i++) {
                putThread[i] = new Thread(() -> {
                    for (int j = 0; j < 25; j++) {
                        putGetWaitNotify.put(j);
                    }
                }, "put-" + (i + 1));
            }
    
            for (int i = 0; i < getThread.length; i++) {
                getThread[i] = new Thread(() -> {
                    for (int j = 0; j < 5; j++) {
                        putGetWaitNotify.get();
                    }
                }, "get-" + (i + 1));
            }
    
            for (int i = 0; i < putThread.length; i++) {
                putThread[i].start();
            }
    
            for (int i = 0; i < getThread.length; i++) {
                getThread[i].start();
            }
        }
    }
    复制代码
    1. ReentrantLock
    package testThread;
    
    import java.util.LinkedList;
    import java.util.concurrent.locks.Condition;
    import java.util.concurrent.locks.Lock;
    import java.util.concurrent.locks.ReentrantLock;
    
    public class PutGetReentrantLock {
        private final LinkedList<Integer> list = new LinkedList<>();
    
        private final Integer MAX = 10;
    
        ReentrantLock lock = new ReentrantLock();
    
        Condition putCondition = lock.newCondition();
    
        Condition getCondition = lock.newCondition();
    
        public void put (int i) {
            try {
                lock.lock();
                while (list.size() == MAX) {
                    putCondition.await();
                }
                list.add(i);
                System.out.println(Thread.currentThread().getName() + ": put one! And size is:" + getCount());
                getCondition.signalAll();
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }
    
        public Integer get () {
            Integer ret = null;
            try {
                lock.lock();
                while (list.size() == 0) {
                    getCondition.await();
                }
                ret = list.removeFirst();
                System.out.println(Thread.currentThread().getName() + ": get one! And size is:" + getCount());
                putCondition.signalAll();
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
            return ret;
        }
    
        public int getCount () {
            return list.size();
        }
    
        public static void main(String[] args) {
            PutGetReentrantLock putGetReentrantLock = new PutGetReentrantLock();
    
            Thread[] putThread = new Thread[2];
            Thread[] getThread = new Thread[10];
    
            for (int i = 0; i < putThread.length; i++) {
                putThread[i] = new Thread(() -> {
                    for (int j = 0; j < 25; j++) {
                        putGetReentrantLock.put(j);
                    }
                }, "put-" + (i + 1));
            }
    
            for (int i = 0; i < getThread.length; i++) {
                getThread[i] = new Thread(() -> {
                    for (int j = 0; j < 5; j++) {
                        putGetReentrantLock.get();
                    }
                }, "get-" + (i + 1));
            }
    
            for (int i = 0; i < putThread.length; i++) {
                putThread[i].start();
            }
    
            for (int i = 0; i < getThread.length; i++) {
                getThread[i].start();
            }
        }
    }
    复制代码
© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享