zoukankan      html  css  js  c++  java
  • Lock类-ReentrantLock的使用

      在Java多线程中可以使用synchronized隐式锁实现线程之间同步互斥,Java5中提供了Lock类(显示锁)也可以实现线程间的同步,而且在使用上更加方便。本文主要研究 ReentrantLock的使用。

      公平锁与非公平锁:公平锁表示线程获取锁的顺序是按照线程加锁的顺序来分配的,即先来先得的FIFO顺序。而非公平锁就是一种获取锁的抢占机制,是随机获得锁的,和公平锁不一样的就是先来的不一定先得到锁,这个方式可能造成某些线程一直拿不到锁。从这个角度讲,synchronized其实就是一种非公平锁。

      ReentrantLock也是一种可重入锁,类似于synchronized,也就是在拥有锁的情况下可以调用其它需要本锁的方法或者代码块。lock.getHoldCount()可以获得当前线程拥有锁的层数,可以理解为重入了几层。当为0的时候代表当前线程没有占用锁,每重入一次count就加1.

      ReentrantLock具有嗅探锁定、多线路分路通知等功能,而且在使用上比synchronized更加灵活。功能上与synchronized一样实现了线程的互斥性与内存的可见性。

    1 ReentrantLock的基本使用方法

      调用其lock()方法会占用锁,调用unlock()会释放锁,但是需要注意必须手动unlock释放锁,否则其他线程会永远阻塞。而且发生异常不会自动释放锁,所以编写程序的时候需要在finally中手动释放锁。

    package cn.qlq.thread.eleven;
    
    import java.util.concurrent.locks.Lock;
    import java.util.concurrent.locks.ReentrantLock;
    
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    
    /**
     * ReentrantLock的基本使用方法
     * @author Administrator
     *
     */
    public class Demo1 {
        private static final Logger LOGGER = LoggerFactory.getLogger(Demo1.class);
        private Lock lock = new ReentrantLock();
        
        public void testMethod(){
            try {
                LOGGER.info("threadName -> {} enter testMethod",Thread.currentThread().getName());
                lock.lock();
                LOGGER.info("threadName -> {} lock",Thread.currentThread().getName());
                Thread.sleep(2*1000);
                LOGGER.info("threadName -> {} unlock",Thread.currentThread().getName());
                lock.unlock();
                LOGGER.info("threadName -> {} exit testMethod",Thread.currentThread().getName());
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        
        public static void main(String[] args) {
            final Demo1 demo1 = new Demo1();
            new Thread(new Runnable() {
                @Override
                public void run() {
                    demo1.testMethod();
                }
            },"threadA").start();
            new Thread(new Runnable() {
                @Override
                public void run() {
                    demo1.testMethod();
                }
            },"threadB").start();
        }
    }

    结果:(实现了线程之间的互斥同步,threadB释放锁之后threadA才进入Lock,类似于synchronized同步锁的执行效果)

    10:10:37 [cn.qlq.thread.eleven.Demo1]-[INFO] threadName -> threadB enter testMethod
    10:10:37 [cn.qlq.thread.eleven.Demo1]-[INFO] threadName -> threadA enter testMethod
    10:10:37 [cn.qlq.thread.eleven.Demo1]-[INFO] threadName -> threadB lock
    10:10:39 [cn.qlq.thread.eleven.Demo1]-[INFO] threadName -> threadB unlock
    10:10:39 [cn.qlq.thread.eleven.Demo1]-[INFO] threadName -> threadB exit testMethod
    10:10:39 [cn.qlq.thread.eleven.Demo1]-[INFO] threadName -> threadA lock
    10:10:41 [cn.qlq.thread.eleven.Demo1]-[INFO] threadName -> threadA unlock
    10:10:41 [cn.qlq.thread.eleven.Demo1]-[INFO] threadName -> threadA exit testMethod

    测试异常发生不会释放锁:修改上面占用锁的方法

        public void testMethod(){
            try {
                LOGGER.info("threadName -> {} enter testMethod",Thread.currentThread().getName());
                lock.lock();
                int i =1/0;
                LOGGER.info("threadName -> {} lock",Thread.currentThread().getName());
                Thread.sleep(2*1000);
                LOGGER.info("threadName -> {} unlock",Thread.currentThread().getName());
                lock.unlock();
                LOGGER.info("threadName -> {} exit testMethod",Thread.currentThread().getName());
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

    结果:(线程B中执行int i=1/0发生算数异常,但是没有释放锁,所以threadA也一直处于阻塞状态。)

    正确的用法:finally中释放锁

        public void testMethod(){
            try {
                LOGGER.info("threadName -> {} enter testMethod",Thread.currentThread().getName());
                lock.lock();
                int i =1/0;
                LOGGER.info("threadName -> {} lock",Thread.currentThread().getName());
                Thread.sleep(2*1000);
    
            } catch (InterruptedException e) {
                e.printStackTrace();
            }finally {
                LOGGER.info("threadName -> {} unlock",Thread.currentThread().getName());
                lock.unlock();
            }
            LOGGER.info("threadName -> {} exit testMethod",Thread.currentThread().getName());
        }

    结果: (两个线程都发生算数异常,证明两个线程都可以占用锁,也就是锁被成功的释放)

     

    2 使用Condition实现等待/通知

       关键字synchronized与wait()/notify()、notifyAll()方法相结合可以实现等待/通知模式,类ReentrantLock也可以实现类似的功能,但需要借助于Condition对象。Condition类是JDK5中出现的类,使用它有更好的灵活性,比如可以实现多路通知功能,也就是在一个Lock对象里面可以创建多个Condition(即对象监视器实例),线程对象可以注册在指定的Condition中,从而可以有选择性地进行线程通知,在调度线程上更加灵活。

      在使用notify()/notifyAll()方法进行通知时,被通知的线程却是由JVM随机选择的。但使用ReentrantLock结合Condition类是可以实现前面介绍过的"选择性通知",这个功能是非常重要的,而且在Condition类中是默认提供的。

      而synchronized就相当于整个Lock对象中只有一个单一的Condition对象,所有的线程都注册在它一个对象的身上。线程开始notifyAll()时,需要通知所有的WATING线程,没有选择权,会出现相当大的效率问题。

    例如:一个简单的等待/通知的例子

    package cn.qlq.thread.eleven;
    
    import java.util.concurrent.locks.Condition;
    import java.util.concurrent.locks.Lock;
    import java.util.concurrent.locks.ReentrantLock;
    
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    
    /**
     * ReentrantLock結合Condition实现等待/通知
     * 
     * @author Administrator
     *
     */
    public class Demo2 {
        private static final Logger LOGGER = LoggerFactory.getLogger(Demo2.class);
        private Lock lock = new ReentrantLock();
        private Condition condition1 = lock.newCondition();
    
        public void await() {
            try {
                lock.lock();
                LOGGER.info("threadName -> {} start await", Thread.currentThread().getName());
                condition1.await();
                LOGGER.info("threadName -> {} end await", Thread.currentThread().getName());
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                LOGGER.info("threadName -> {} unlock", Thread.currentThread().getName());
                lock.unlock();
            }
            LOGGER.info("threadName -> {} exit await 退出await方法", Thread.currentThread().getName());
        }
    
        public void signal() {
            try {
                lock.lock();
                LOGGER.info("threadName -> {} start signal", Thread.currentThread().getName());
                condition1.signal();
                LOGGER.info("threadName -> {} end signal", Thread.currentThread().getName());
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                LOGGER.info("threadName -> {} unlock", Thread.currentThread().getName());
                lock.unlock();
            }
            LOGGER.info("exit signal 退出signal方法");
        }
    
        public static void main(String[] args) {
            final Demo2 demo2 = new Demo2();
            new Thread(new Runnable() {
                @Override
                public void run() {
                    demo2.await();
                }
            }, "threadA").start();
            new Thread(new Runnable() {
                @Override
                public void run() {
                    demo2.signal();
                }
            }, "threadB").start();
        }
    }

    结果:

    11:00:12 [cn.qlq.thread.eleven.Demo2]-[INFO] threadName -> threadA start await
    11:00:12 [cn.qlq.thread.eleven.Demo2]-[INFO] threadName -> threadB start signal
    11:00:12 [cn.qlq.thread.eleven.Demo2]-[INFO] threadName -> threadB end signal
    11:00:12 [cn.qlq.thread.eleven.Demo2]-[INFO] threadName -> threadB unlock
    11:00:12 [cn.qlq.thread.eleven.Demo2]-[INFO] exit signal 退出signal方法
    11:00:12 [cn.qlq.thread.eleven.Demo2]-[INFO] threadName -> threadA end await
    11:00:12 [cn.qlq.thread.eleven.Demo2]-[INFO] threadName -> threadA unlock
    11:00:12 [cn.qlq.thread.eleven.Demo2]-[INFO] threadName -> threadA exit await 退出await方法

    注意:

      condition对象的await()signal()signalAll()必须在获得lock.lock()占用锁之后调用,而且最后必须手动释放锁。

      Object的wait()方法相当于Condition的await()方法,会释放锁;

      Object的wait(long)方法相当于Condition类的await(long)方法,可以指定多少秒后自动唤醒转入对象监视器的就绪队列;

      Object类的notify()方法相当于Condition的signal()方法,Object的notifyAll()方法相当于Condition类的signalAll()方法。

    3 使用多个Condition实现等待/通知部分线程

       使用ReentrantLock创建多个Condition对象之后可以实现唤醒指定的线程,这是控制部分线程行为的方便方式。可以理解为将线程分组,每一组对应一个condition对象。

    package cn.qlq.thread.eleven;
    
    import java.util.concurrent.locks.Condition;
    import java.util.concurrent.locks.Lock;
    import java.util.concurrent.locks.ReentrantLock;
    
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    
    /**
     * ReentrantLock結合Condition实现等待/通知,唤醒和等待部分线程
     * 
     * @author Administrator
     *
     */
    public class Demo3 {
        private static final Logger LOGGER = LoggerFactory.getLogger(Demo3.class);
        private Lock lock = new ReentrantLock();
        private Condition conditionA = lock.newCondition();
        private Condition conditionB = lock.newCondition();
    
        public void awaitA() {
            try {
                lock.lock();
                LOGGER.info("threadName -> {} start await", Thread.currentThread().getName());
                Thread.sleep(1 * 1000);
                conditionA.await();
                LOGGER.info("threadName -> {} end await", Thread.currentThread().getName());
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }
    
        public void signalA() {
            try {
                lock.lock();
                LOGGER.info("threadName -> {} start signal", Thread.currentThread().getName());
                Thread.sleep(1 * 1000);
                conditionA.signal();
                LOGGER.info("threadName -> {} end signal", Thread.currentThread().getName());
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }
    
        public void awaitB() {
            try {
                lock.lock();
                LOGGER.info("threadName -> {} start await", Thread.currentThread().getName());
                Thread.sleep(1 * 1000);
                conditionB.await();
                LOGGER.info("threadName -> {} end await", Thread.currentThread().getName());
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }
    
        public void signalB() {
            try {
                lock.lock();
                LOGGER.info("threadName -> {} start signal", Thread.currentThread().getName());
                Thread.sleep(1 * 1000);
                conditionB.signal();
                LOGGER.info("threadName -> {} end signal", Thread.currentThread().getName());
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }
    
        public static void main(String[] args) {
            final Demo3 demo3 = new Demo3();
            new Thread(new Runnable() {
                @Override
                public void run() {
                    demo3.awaitA();
                }
            }, "threadA").start();
            new Thread(new Runnable() {
                @Override
                public void run() {
                    demo3.signalA();
                }
            }, "threadA1").start();
            // 访问conditionB
            new Thread(new Runnable() {
                @Override
                public void run() {
                    demo3.awaitB();
                }
            }, "threadB").start();
            new Thread(new Runnable() {
                @Override
                public void run() {
                    demo3.signalB();
                }
            }, "threadB1").start();
        }
    }

    结果: (多个线程还是公用一个锁,但是可以用多个Condition实现阻塞与唤醒部分线程。也就是多个Condition将对象阻塞到多个队列中)

    11:18:31 [cn.qlq.thread.eleven.Demo3]-[INFO] threadName -> threadA start await
    11:18:32 [cn.qlq.thread.eleven.Demo3]-[INFO] threadName -> threadA1 start signal
    11:18:33 [cn.qlq.thread.eleven.Demo3]-[INFO] threadName -> threadA1 end signal
    11:18:33 [cn.qlq.thread.eleven.Demo3]-[INFO] threadName -> threadB start await
    11:18:34 [cn.qlq.thread.eleven.Demo3]-[INFO] threadName -> threadB1 start signal
    11:18:35 [cn.qlq.thread.eleven.Demo3]-[INFO] threadName -> threadB1 end signal
    11:18:35 [cn.qlq.thread.eleven.Demo3]-[INFO] threadName -> threadA end await
    11:18:35 [cn.qlq.thread.eleven.Demo3]-[INFO] threadName -> threadB end await

    4 公平锁与非公平锁

      公平锁与非公平锁:公平锁表示线程获取锁的顺序是按照线程加锁的顺序来分配的,即先来先得的FIFO顺序。而非公平锁就是一种获取锁的抢占机制,是随机获得锁的,和公平锁不一样的就是先来的不一定先得到锁,这个方式可能造成某些线程一直拿不到锁。从这个角度讲,synchronized其实就是一种非公平锁。

      ReentrantLock类有一个单一参数的构造方法,接受boolean类型的数据,传入true表示创建的是公平锁,传入false创建的是非公平锁(不带参数的默认创建非公平锁)

        public ReentrantLock() {
            sync = new NonfairSync();
        }
    
        /**
         * Creates an instance of {@code ReentrantLock} with the
         * given fairness policy.
         *
         * @param fair {@code true} if this lock should use a fair ordering policy
         */
        public ReentrantLock(boolean fair) {
            sync = fair ? new FairSync() : new NonfairSync();
        }

    (1)公平锁的测试:

    package cn.qlq.thread.eleven;
    
    import java.util.concurrent.locks.Lock;
    import java.util.concurrent.locks.ReentrantLock;
    
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    
    /**
     * 公平锁与非公平锁
     * 
     * @author Administrator
     *
     */
    public class Demo5 {
        private static final Logger LOGGER = LoggerFactory.getLogger(Demo5.class);
    
        private Lock lock = new ReentrantLock(true);
    
        public void testMethod() {
            try {
                lock.lock();
                System.out.println("★ThreadName" + Thread.currentThread().getName() + "获得锁");
            } finally {
                lock.unlock();
            }
        }
    
        public static void main(String[] args) throws InterruptedException {
            final Demo5 demo5 = new Demo5();
            Runnable runnable = new Runnable() {
                public void run() {
                    System.out.println("☆线程" + Thread.currentThread().getName() + "运行了");
                    demo5.testMethod();
                }
            };
            Thread[] threads = new Thread[5];
            for (int i = 0; i < 5; i++)
                threads[i] = new Thread(runnable);
            for (int i = 0; i < 5; i++)
                threads[i].start();
    
        }
    }

    结果: (先运行的先获得锁---只能说是基本上是FIFO,也并不是绝对的)

    ☆线程Thread-0运行了
    ☆线程Thread-4运行了
    ☆线程Thread-3运行了
    ☆线程Thread-2运行了
    ☆线程Thread-1运行了
    ★ThreadNameThread-0获得锁
    ★ThreadNameThread-4获得锁
    ★ThreadNameThread-3获得锁
    ★ThreadNameThread-2获得锁
    ★ThreadNameThread-1获得锁

    (2)非公平锁的测试:

    package cn.qlq.thread.eleven;
    
    import java.util.concurrent.locks.Lock;
    import java.util.concurrent.locks.ReentrantLock;
    
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    
    /**
     * 公平锁与非公平锁
     * 
     * @author Administrator
     *
     */
    public class Demo5 {
        private static final Logger LOGGER = LoggerFactory.getLogger(Demo5.class);
    
        private Lock lock = new ReentrantLock(false);
    
        public void testMethod() {
            try {
                lock.lock();
                System.out.println("★ThreadName" + Thread.currentThread().getName() + "获得锁");
            } finally {
                lock.unlock();
            }
        }
    
        public static void main(String[] args) throws InterruptedException {
            final Demo5 demo5 = new Demo5();
            Runnable runnable = new Runnable() {
                public void run() {
                    System.out.println("☆线程" + Thread.currentThread().getName() + "运行了");
                    demo5.testMethod();
                }
            };
            Thread[] threads = new Thread[5];
            for (int i = 0; i < 5; i++)
                threads[i] = new Thread(runnable);
            for (int i = 0; i < 5; i++)
                threads[i].start();
    
        }
    }

    结果: (不一定先运行的先获得锁)

    ☆线程Thread-1运行了
    ☆线程Thread-2运行了
    ☆线程Thread-3运行了
    ☆线程Thread-0运行了
    ☆线程Thread-4运行了
    ★ThreadNameThread-2获得锁
    ★ThreadNameThread-3获得锁
    ★ThreadNameThread-0获得锁
    ★ThreadNameThread-4获得锁
    ★ThreadNameThread-1获得锁

    5  使用condition实现线程按顺序执行

       使用condition实现线程按顺序执行(比如创建10个线程,每个线程打印自己的名字,按照1-10打印)

    package cn.qlq.thread.eleven;
    
    import java.util.concurrent.locks.Condition;
    import java.util.concurrent.locks.ReentrantLock;
    
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    
    /**
     * 使用condition实现线程按顺序执行(比如创建10个线程,每个线程打印自己的名字,按照1-10打印)
     * 
     * @author Administrator
     *
     */
    public class Demo14 {
        private ReentrantLock lock = new ReentrantLock();
        private Condition newCondition = lock.newCondition();
        private static final Logger LOGGER = LoggerFactory.getLogger(Demo14.class);
    
        private volatile int currentNum = 1;// 标记当前线程执行到第几个线程
    
        public void printName() {
            try {
                lock.lock();
                while (!String.valueOf(currentNum).equals(Thread.currentThread().getName())) {
                    newCondition.await();
                }
                LOGGER.info("threadName - > {} ", Thread.currentThread().getName());
                newCondition.signalAll();
                currentNum++;
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }
    
        public static void main(String[] args) throws InterruptedException {
            final Demo14 demo8 = new Demo14();
            Runnable runnable = new Runnable() {
                @Override
                public void run() {
                    demo8.printName();
                }
            };
    
            Thread[] threads = new Thread[10];
            for (int i = 0; i < 10; i++) {
                threads[i] = new Thread(runnable, (i + 1) + "");
            }
            for (int i = 0; i < 10; i++) {
                threads[i].start();
            }
    
        }
    }

    结果:

    6 其他方法研究

    1. getHoldCount()、getQueueLength()方法、getWaitQueueLength(condition)方法

    • getHoldCount()  返回当前线程保持此锁定的个数,也就是调用lock方法的此时,可以理解为重入锁的次数
    • getQueueLength()方法  返回正等待此获取此锁定的线程的估计数,比如有5个线程,1个线程首先执行await(),那么在调用getQueueLength()方法后返回值是4,说明有4个线程等待lock的释放。(可以理解为等待锁的线程数)
    package cn.qlq.thread.eleven;
    
    import java.util.concurrent.locks.ReentrantLock;
    
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    
    /**
     * 其他方法研究 getQueueLength--返回正等待此获取此锁定的线程的估计数
     * 
     * @author Administrator
     *
     */
    public class Demo6 {
        private static final Logger LOGGER = LoggerFactory.getLogger(Demo6.class);
    
        private ReentrantLock lock = new ReentrantLock(true);
    
        public void testMethod() {
            try {
                lock.lock();
                LOGGER.debug("testMethod lock,getHoldCount()->{},getQueueLength->{}", lock.getHoldCount(),
                        lock.getQueueLength());
                // 调用testMethod2(),模拟锁重入
                testMethod2();
            } finally {
                lock.unlock();
                LOGGER.debug("testMethod unlock,getHoldCount()->{},getQueueLength->{}", lock.getHoldCount(),
                        lock.getQueueLength());
            }
        }
    
        public void testMethod2() {
            try {
                lock.lock();
                LOGGER.debug("testMethod2 lock,getHoldCount()->{},getQueueLength->{}", lock.getHoldCount(),
                        lock.getQueueLength());
            } finally {
                lock.unlock();
                LOGGER.debug("testMethod2 unlock,getHoldCount()->{},getQueueLength->{}", lock.getHoldCount(),
                        lock.getQueueLength());
            }
        }
    
        public void testMethod3() {
            try {
                lock.lock();
                LOGGER.debug("testMethod3 lock,getHoldCount()->{},getQueueLength->{}", lock.getHoldCount(),
                        lock.getQueueLength());
            } finally {
                lock.unlock();
                LOGGER.debug("testMethod3 unlock,getHoldCount()->{},getQueueLength->{}", lock.getHoldCount(),
                        lock.getQueueLength());
            }
        }
    
        public static void main(String[] args) throws InterruptedException {
            final Demo6 demo6 = new Demo6();
            new Thread(new Runnable() {
                public void run() {
                    demo6.testMethod();
                }
            }, "thread--t1").start();
    
            new Thread(new Runnable() {
                @Override
                public void run() {
                    demo6.testMethod3();
                }
            }, "thread--t2").start();
        }
    }

    结果:

    15:22:47 [cn.qlq.thread.eleven.Demo6]-[DEBUG] testMethod lock,getHoldCount()->1,getQueueLength->1
    15:22:47 [cn.qlq.thread.eleven.Demo6]-[DEBUG] testMethod2 lock,getHoldCount()->2,getQueueLength->1
    15:22:47 [cn.qlq.thread.eleven.Demo6]-[DEBUG] testMethod2 unlock,getHoldCount()->1,getQueueLength->1
    15:22:47 [cn.qlq.thread.eleven.Demo6]-[DEBUG] testMethod unlock,getHoldCount()->0,getQueueLength->1
    15:22:47 [cn.qlq.thread.eleven.Demo6]-[DEBUG] testMethod3 lock,getHoldCount()->1,getQueueLength->0
    15:22:47 [cn.qlq.thread.eleven.Demo6]-[DEBUG] testMethod3 unlock,getHoldCount()->0,getQueueLength->0

    • getWaitQueueLength(condition)方法返回此Condition对象阻塞队列的数量

    package cn.qlq.thread.eleven;
    
    import java.util.concurrent.locks.Condition;
    import java.util.concurrent.locks.ReentrantLock;
    
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    
    /**
     * 
     * @author Administrator
     *
     */
    public class Demo7 {
        private static final Logger LOGGER = LoggerFactory.getLogger(Demo7.class);
        private ReentrantLock lock = new ReentrantLock();
        private Condition conditionA = lock.newCondition();
    
        public void awaitA() {
            try {
                lock.lock();
                Thread.sleep(1 * 1000);
                LOGGER.info("threadName -> {},getWaitQueueLength(conditionA)->{} ", Thread.currentThread().getName(),
                        lock.getWaitQueueLength(conditionA));
                conditionA.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }
    
        public void signalA() {
            try {
                lock.lock();
                Thread.sleep(1 * 1000);
                LOGGER.info("threadName -> {},getWaitQueueLength(conditionA)->{} ", Thread.currentThread().getName(),
                        lock.getWaitQueueLength(conditionA));
                conditionA.signal();
                LOGGER.info("threadName -> {},getWaitQueueLength(conditionA)->{} ", Thread.currentThread().getName(),
                        lock.getWaitQueueLength(conditionA));
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }
    
        public static void main(String[] args) {
            final Demo7 demo7 = new Demo7();
            Runnable await = new Runnable() {
                public void run() {
                    demo7.awaitA();
                }
            };
            new Thread(await, "threadA1").start();
            new Thread(await, "threadA2").start();
            new Thread(await, "threadA3").start();
    
            // 访问signal
            new Thread(new Runnable() {
                @Override
                public void run() {
                    demo7.signalA();
                }
            }, "threadS1").start();
        }
    }

    结果:

    15:40:27 [cn.qlq.thread.eleven.Demo7]-[INFO] threadName -> threadA2,getWaitQueueLength(conditionA)->0
    15:40:28 [cn.qlq.thread.eleven.Demo7]-[INFO] threadName -> threadA3,getWaitQueueLength(conditionA)->1
    15:40:29 [cn.qlq.thread.eleven.Demo7]-[INFO] threadName -> threadA1,getWaitQueueLength(conditionA)->2
    15:40:30 [cn.qlq.thread.eleven.Demo7]-[INFO] threadName -> threadS1,getWaitQueueLength(conditionA)->3
    15:40:30 [cn.qlq.thread.eleven.Demo7]-[INFO] threadName -> threadS1,getWaitQueueLength(conditionA)->2

    修改上面signalA的方法唤醒所有:

        public void signalA() {
            try {
                lock.lock();
                Thread.sleep(1 * 1000);
                LOGGER.info("threadName -> {},getWaitQueueLength(conditionA)->{} ", Thread.currentThread().getName(),
                        lock.getWaitQueueLength(conditionA));
                conditionA.signalAll();
                LOGGER.info("threadName -> {},getWaitQueueLength(conditionA)->{} ", Thread.currentThread().getName(),
                        lock.getWaitQueueLength(conditionA));
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }

    结果:

    15:41:58 [cn.qlq.thread.eleven.Demo7]-[INFO] threadName -> threadA1,getWaitQueueLength(conditionA)->0
    15:41:59 [cn.qlq.thread.eleven.Demo7]-[INFO] threadName -> threadA2,getWaitQueueLength(conditionA)->1
    15:42:00 [cn.qlq.thread.eleven.Demo7]-[INFO] threadName -> threadA3,getWaitQueueLength(conditionA)->2
    15:42:01 [cn.qlq.thread.eleven.Demo7]-[INFO] threadName -> threadS1,getWaitQueueLength(conditionA)->3
    15:42:01 [cn.qlq.thread.eleven.Demo7]-[INFO] threadName -> threadS1,getWaitQueueLength(conditionA)->0

    2.hasQueuedThreads()、 hasQueuedThread(thread)、lock.hasWaiters(conditionA)方法

    • hasQueuedThreads()  方法返回此锁是否有线程在等待获取此锁
    • hasQueuedThread(thread)  查询指定的线程是否正在等待获取此锁
    • lock.hasWaiters(condition)  此方法必须在获取到lock(lock.lock())之后调用,查询指定的condition是否有等待的对象
    package cn.qlq.thread.eleven;
    
    import java.util.concurrent.locks.Condition;
    import java.util.concurrent.locks.ReentrantLock;
    
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    
    /**
     * 
     * @author Administrator
     *
     */
    public class Demo8 {
        private static final Logger LOGGER = LoggerFactory.getLogger(Demo8.class);
        private ReentrantLock lock = new ReentrantLock();
        private Condition conditionA = lock.newCondition();
    
        public void awaitA() {
            try {
                lock.lock();
                Thread.sleep(1 * 1000);
                conditionA.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }
    
        public void signalA() {
            try {
                lock.lock();
                Thread.sleep(1 * 1000);
                conditionA.signal();
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }
    
        public static void main(String[] args) {
            final Demo8 demo8 = new Demo8();
            Runnable await = new Runnable() {
                public void run() {
                    demo8.awaitA();
                }
            };
            new Thread(await, "threadA2").start();
            new Thread(await, "threadA3").start();
            Thread thread = new Thread(await, "threadA1");
            thread.start();
    
            // 访问signal
            new Thread(new Runnable() {
                @Override
                public void run() {
                    demo8.signalA();
                }
            }, "threadS1").start();
    
            // 返回thread是否在等待获取此锁
            System.out.println(demo8.getLock().hasQueuedThread(thread));
            // 获取是否有等待线程
            System.out.println(demo8.getLock().hasQueuedThreads());
        }
    
        public ReentrantLock getLock() {
            return lock;
        }
    
        public void setLock(ReentrantLock lock) {
            this.lock = lock;
        }
    }

    结果:

    false
    true

    package cn.qlq.thread.eleven;
    
    import java.util.concurrent.locks.Condition;
    import java.util.concurrent.locks.ReentrantLock;
    
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    
    /**
     * 
     * @author Administrator
     *
     */
    public class Demo9 {
        private static final Logger LOGGER = LoggerFactory.getLogger(Demo9.class);
        private ReentrantLock lock = new ReentrantLock();
        private Condition conditionA = lock.newCondition();
    
        public void awaitA() {
            try {
                lock.lock();
                Thread.sleep(1 * 1000);
                conditionA.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }
    
        public void signalA() {
            try {
                lock.lock();
                Thread.sleep(1 * 1000);
                System.out.println(lock.hasWaiters(conditionA));
                conditionA.signalAll();
                System.out.println(lock.hasWaiters(conditionA));
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }
    
        public static void main(String[] args) {
            final Demo9 demo8 = new Demo9();
            Runnable await = new Runnable() {
                public void run() {
                    demo8.awaitA();
                }
            };
            Thread thread = new Thread(await, "threadA1");
            thread.start();
            new Thread(await, "threadA2").start();
            new Thread(await, "threadA3").start();
    
            // 访问signal
            new Thread(new Runnable() {
                @Override
                public void run() {
                    demo8.signalA();
                }
            }, "threadS1").start();
        }
    }

    结果:

    true
    false

    3.   isFair(),isLocked(),isHeldByCurrentThread()方法

    •   isFair()  判断一个锁是否是公平锁
    •   isLocked()  判断一个锁是否已经锁住,也就是判断是否被任意线程锁定
    •   isHeldByCurrentThread()  判断当前线程是否拥有指定的锁
    package cn.qlq.thread.eleven;
    
    import java.util.concurrent.locks.ReentrantLock;
    
    /**
     * 
     * @author Administrator
     *
     */
    public class Demo10 {
        private ReentrantLock lock = new ReentrantLock();
    
        public void awaitA() {
            lock.lock();
            System.out.println("isFair -> " + lock.isFair());
            System.out.println("isLocked -> " + lock.isLocked());
            System.out.println("isHeldByCurrentThread -> " + lock.isHeldByCurrentThread());
            lock.unlock();
        }
    
        public ReentrantLock getLock() {
            return lock;
        }
    
        public void setLock(ReentrantLock lock) {
            this.lock = lock;
        }
    
        public static void main(String[] args) {
            final Demo10 demo8 = new Demo10();
            demo8.awaitA();
        }
    }

    结果:

    isFair -> false
    isLocked -> true
    isHeldByCurrentThread -> true

    4. lockInterruptibly()、tryLock()、tryLock(long, TimeUnit)---轮询锁与定时锁

    • lockInterruptibly()方法如果在获取锁的情况下如果收到中断信号会进入中断异常
    package cn.qlq.thread.eleven;
    
    import java.util.concurrent.locks.ReentrantLock;
    
    /**
     * 
     * @author Administrator
     *
     */
    public class Demo11 {
        private ReentrantLock lock = new ReentrantLock();
    
        public void awaitA() {
            try {
                lock.lockInterruptibly();
                System.out.println("isFair -> " + lock.isFair());
                System.out.println("isLocked -> " + lock.isLocked());
                System.out.println("isHeldByCurrentThread -> " + lock.isHeldByCurrentThread());
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }
    
        public static void main(String[] args) {
            final Demo11 demo8 = new Demo11();
            Thread thread = new Thread(new Runnable() {
                @Override
                public void run() {
                    demo8.awaitA();
                }
            });
            thread.start();
            // 发出中断信号
            thread.interrupt();
        }
    }

    结果:

    java.lang.InterruptedException
    at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireInterruptibly(AbstractQueuedSynchronizer.java:1219)
    at java.util.concurrent.locks.ReentrantLock.lockInterruptibly(ReentrantLock.java:340)
    at cn.qlq.thread.eleven.Demo11.awaitA(Demo11.java:15)
    at cn.qlq.thread.eleven.Demo11$1.run(Demo11.java:31)
    at java.lang.Thread.run(Thread.java:745)
    Exception in thread "Thread-0" java.lang.IllegalMonitorStateException
    at java.util.concurrent.locks.ReentrantLock$Sync.tryRelease(ReentrantLock.java:155)
    at java.util.concurrent.locks.AbstractQueuedSynchronizer.release(AbstractQueuedSynchronizer.java:1260)
    at java.util.concurrent.locks.ReentrantLock.unlock(ReentrantLock.java:460)
    at cn.qlq.thread.eleven.Demo11.awaitA(Demo11.java:22)
    at cn.qlq.thread.eleven.Demo11$1.run(Demo11.java:31)
    at java.lang.Thread.run(Thread.java:745)

    如果不发出中断信号:

        public static void main(String[] args) {
            final Demo11 demo8 = new Demo11();
            Thread thread = new Thread(new Runnable() {
                @Override
                public void run() {
                    demo8.awaitA();
                }
            });
            thread.start();
        }

    结果:

    isFair -> false
    isLocked -> true
    isHeldByCurrentThread -> true

    • tryLock可以获取有个仅仅没有被其他线程占用的锁,返回一个boolean类型的值代表是否获取锁成功
    package cn.qlq.thread.eleven;
    
    import java.util.concurrent.locks.ReentrantLock;
    
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    
    /**
     * 
     * @author Administrator
     *
     */
    public class Demo11 {
        private ReentrantLock lock = new ReentrantLock();
        private static final Logger LOGGER = LoggerFactory.getLogger(Demo11.class);
    
        public void awaitA() {
            if (lock.tryLock()) {
                LOGGER.info("threadName -> {} , isFair -> " + lock.isFair(), Thread.currentThread().getName());
                LOGGER.info("threadName -> {} ,isLocked -> " + lock.isLocked(), Thread.currentThread().getName());
                LOGGER.info("threadName -> {} ,isHeldByCurrentThread -> " + lock.isHeldByCurrentThread(),
                        Thread.currentThread().getName());
                lock.unlock();
            } else {
                LOGGER.info("threadName -> {} 没有获得锁 ", Thread.currentThread().getName());
            }
        }
    
        public static void main(String[] args) {
            final Demo11 demo8 = new Demo11();
            Runnable runnable = new Runnable() {
                @Override
                public void run() {
                    demo8.awaitA();
                }
            };
            Thread thread = new Thread(runnable, "thread");
            Thread thread2 = new Thread(runnable, "thread2");
            thread.start();
            thread2.start();
        }
    }

    结果:

    16:53:03 [cn.qlq.thread.eleven.Demo11]-[INFO] threadName -> thread 没有获得锁
    16:53:03 [cn.qlq.thread.eleven.Demo11]-[INFO] threadName -> thread2 , isFair -> false
    16:53:03 [cn.qlq.thread.eleven.Demo11]-[INFO] threadName -> thread2 ,isLocked -> true
    16:53:03 [cn.qlq.thread.eleven.Demo11]-[INFO] threadName -> thread2 ,isHeldByCurrentThread -> true

    • lock.tryLock(long, TimeUnit.MILLISECONDS) 如果锁定在给定等待时间内没有被另一个线程保持,且当前线程未被中断,则获取该锁定
    package cn.qlq.thread.eleven;
    
    import java.util.concurrent.TimeUnit;
    import java.util.concurrent.locks.ReentrantLock;
    
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    
    /**
     * 
     * @author Administrator
     *
     */
    public class Demo11 {
        private ReentrantLock lock = new ReentrantLock();
        private static final Logger LOGGER = LoggerFactory.getLogger(Demo11.class);
    
        public void awaitA() {
            try {
                if (lock.tryLock(2000, TimeUnit.MILLISECONDS)) {
                    LOGGER.info("threadName -> {} , isFair -> " + lock.isFair(), Thread.currentThread().getName());
                    LOGGER.info("threadName -> {} ,isLocked -> " + lock.isLocked(), Thread.currentThread().getName());
                    LOGGER.info("threadName -> {} ,isHeldByCurrentThread -> " + lock.isHeldByCurrentThread(),
                            Thread.currentThread().getName());
                    // 释放锁
                    lock.unlock();
                } else {
                    LOGGER.info("threadName -> {} 没有获得锁 ", Thread.currentThread().getName());
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    
        public static void main(String[] args) {
            final Demo11 demo8 = new Demo11();
            Runnable runnable = new Runnable() {
                @Override
                public void run() {
                    demo8.awaitA();
                }
            };
            Thread thread = new Thread(runnable, "thread");
            Thread thread2 = new Thread(runnable, "thread2");
            thread.start();
            thread2.start();
        }
    }

    结果:

    16:56:14 [cn.qlq.thread.eleven.Demo11]-[INFO] threadName -> thread2 , isFair -> false
    16:56:14 [cn.qlq.thread.eleven.Demo11]-[INFO] threadName -> thread2 ,isLocked -> true
    16:56:14 [cn.qlq.thread.eleven.Demo11]-[INFO] threadName -> thread2 ,isHeldByCurrentThread -> true
    16:56:14 [cn.qlq.thread.eleven.Demo11]-[INFO] threadName -> thread , isFair -> false
    16:56:14 [cn.qlq.thread.eleven.Demo11]-[INFO] threadName -> thread ,isLocked -> true
    16:56:14 [cn.qlq.thread.eleven.Demo11]-[INFO] threadName -> thread ,isHeldByCurrentThread -> true

    5. Condition.awaitUninterruptibly()、Condition.awaitUntil(date)方法

    • Condition.awaitUninterruptibly()是在await的过程中如果线程收到中断信号不会抛出异常(可中断的锁获取)
    package cn.qlq.thread.eleven;
    
    import java.util.concurrent.locks.Condition;
    import java.util.concurrent.locks.ReentrantLock;
    
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    
    /**
     * 
     * @author Administrator
     *
     */
    public class Demo12 {
        private ReentrantLock lock = new ReentrantLock();
        private Condition newCondition = lock.newCondition();
        private static final Logger LOGGER = LoggerFactory.getLogger(Demo12.class);
    
        public void awaitA() {
            LOGGER.info("threadName -> {} 进入方法,等待锁 ", Thread.currentThread().getName());
            try {
                lock.lock();
                LOGGER.info("threadName -> {} begain await ", Thread.currentThread().getName());
                newCondition.awaitUninterruptibly();
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }
    
        public static void main(String[] args) throws InterruptedException {
            final Demo12 demo8 = new Demo12();
            Runnable runnable = new Runnable() {
                @Override
                public void run() {
                    demo8.awaitA();
                }
            };
            Thread thread = new Thread(runnable, "thread");
    
            Thread.sleep(1 * 1000);
            thread.interrupt();
    
        }
    }

    结果:

    如果修改为await之后再次中断:

        public void awaitA() {
            LOGGER.info("threadName -> {} 进入方法,等待锁 ", Thread.currentThread().getName());
            try {
                lock.lock();
                LOGGER.info("threadName -> {} begain await ", Thread.currentThread().getName());
                newCondition.await();
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }

    结果:

    •  Condition.awaitUntil(date)是停止到指定时间如果没有被唤醒自动唤醒
    package cn.qlq.thread.eleven;
    
    import java.util.Date;
    import java.util.concurrent.locks.Condition;
    import java.util.concurrent.locks.ReentrantLock;
    
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    
    /**
     * 
     * @author Administrator
     *
     */
    public class Demo13 {
        private ReentrantLock lock = new ReentrantLock();
        private Condition newCondition = lock.newCondition();
        private static final Logger LOGGER = LoggerFactory.getLogger(Demo13.class);
    
        public void awaitA() {
            LOGGER.info("threadName -> {} 进入方法,等待锁 ", Thread.currentThread().getName());
            try {
                lock.lock();
                LOGGER.info("threadName -> {} begain await ", Thread.currentThread().getName());
                Date deadline = new Date();
                deadline.setSeconds(deadline.getSeconds() + 3);
                newCondition.awaitUntil(deadline);
                LOGGER.info("threadName -> {} end await ", Thread.currentThread().getName());
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }
    
        public static void main(String[] args) throws InterruptedException {
            final Demo13 demo8 = new Demo13();
            Runnable runnable = new Runnable() {
                @Override
                public void run() {
                    demo8.awaitA();
                }
            };
            Thread thread = new Thread(runnable, "thread");
            thread.start();
        }
    }

    结果: (3秒钟后自己唤醒,这个方法是停止到某一时间点)

    17:16:18 [cn.qlq.thread.eleven.Demo13]-[INFO] threadName -> thread 进入方法,等待锁
    17:16:18 [cn.qlq.thread.eleven.Demo13]-[INFO] threadName -> thread begain await
    17:16:21 [cn.qlq.thread.eleven.Demo13]-[INFO] threadName -> thread end await

    总结: 关于Lock和Synchronized两种同步方式的比较:

    1.性能方面,两者实际是差不多的,JVM不断的对synchronized进行优化,所以性能基本没多大差别
    2.synchronized是关键字,就和if...else...一样,是语法层面的实现,因此synchronized获取锁以及释放锁都是Java虚拟机帮助用户完成的;ReentrantLock是类层面的实现,因此锁的获取以及锁的释放都需要用户自己去操作。而且synchronized遇到错误会释放锁,而ReentrantLock不会自动释放。类和关键字最大的区别就是类使用更加灵活。

    3.synchronized是不公平锁,而ReentrantLock可以指定锁是公平的还是非公平的。公平和非公平体现的就是获取锁的顺序是否是FIFO的顺序获取。

    4.synchronized实现等待/通知机制通知的线程是随机的,ReentrantLock结合Condition可以实现等待/通知机制可以有选择性地通知,这点有时候便于理解。

    5.和synchronized相比,ReentrantLock提供给用户多种方法用于锁信息的获取,比如可以获取是否有线程在等待锁、锁重入的层数等信息;而且显示锁增加了可中断的锁获取方式,以及tryLock轮询锁或者定时锁等方法

      学习完ReentrantLock+Condition的使用之后可以完成一个简单的生产者消费者的例子,参考我的另一篇:https://www.cnblogs.com/qlqwjy/p/10115756.html

    补充:重入锁也可能造成死锁:

    package Thread;
    
    import java.util.concurrent.TimeUnit;
    import java.util.concurrent.locks.Lock;
    import java.util.concurrent.locks.ReentrantLock;
    
    /**
     * 测试死锁 思路:两个线程,每个线程占有不同的资源,等待其他资源
     * 
     * @author: qlq
     * @date : 2018年6月14日上午10:37:24
     */
    public class DeadLockTest1 {
    
        public static void main(String[] args) {
            Lock lock1 = new ReentrantLock();
            Lock lock2 = new ReentrantLock();
            MyThread1 t1 = new MyThread1(true,lock1,lock2);
            MyThread1 t2 = new MyThread1(false,lock1,lock2);
            t1.start();
            t2.start();
        }
    
    }
    
    class MyThread1 extends Thread {
        private boolean flag;// 标记走哪个线路
        private Lock lock1;//第一把锁
        private Lock lock2;//第二把锁
    
        public boolean isFlag() {
            return flag;
        }
    
        public void setFlag(boolean flag) {
            this.flag = flag;
        }
    
        protected MyThread1(boolean flag, Lock lock1, Lock lock2) {
            super();
            this.flag = flag;
            this.lock1 = lock1;
            this.lock2 = lock2;
        }
    
        @Override
        public void run() {
            if (flag) {// 占有资源A,等待资源B
                if(lock1.tryLock()){
                    try {
                        Thread.sleep(2*1000);
                    } catch (InterruptedException e1) {
                        // TODO Auto-generated catch block
                        e1.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName()+"占有第一把锁,等待第二把锁");
                    try {
                        if(lock2.tryLock(50*1000,TimeUnit.SECONDS)){
                            try {
                                Thread.sleep(2*1000);
                                System.out.println(Thread.currentThread().getName()+"占有第二把锁");
                            } catch (InterruptedException e) {
                                // TODO Auto-generated catch block
                                e.printStackTrace();
                            }
                            System.out.println(Thread.currentThread().getName()+"释放第二把锁");
                            lock2.unlock();
                            System.out.println(Thread.currentThread().getName()+"释放第一把锁");
                            lock1.unlock();
                        }
                    } catch (InterruptedException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
    
                }
            } else {// 占有第二把锁,等待第一把锁
                if(lock2.tryLock()){
                    try {
                        Thread.sleep(2*1000);
                    } catch (InterruptedException e1) {
                        // TODO Auto-generated catch block
                        e1.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName()+"占有第二把锁,等待第一把锁");
                    try {
                        if(lock1.tryLock(50*1000,TimeUnit.SECONDS)){
                            try {
                                Thread.sleep(2*1000);
                                System.out.println(Thread.currentThread().getName()+"占有第一把锁");
                            } catch (InterruptedException e) {
                                // TODO Auto-generated catch block
                                e.printStackTrace();
                            }
                            System.out.println(Thread.currentThread().getName()+"释放第一把锁");
                            lock1.unlock();
                            System.out.println(Thread.currentThread().getName()+"释放第二把锁");
                            lock2.unlock();
                        }
                    } catch (InterruptedException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
    
                }
            }
        }
    }

     结果:

    Thread-0占有第一把锁,等待第二把锁
    Thread-1占有第二把锁,等待第一把锁

    解决办法:在正确的地方释放锁。。。。

    注意:锁必须是同一把锁才会生效,如果锁作为局部变量是不会生效的,局部变量是每个线程一把锁。。。。。

  • 相关阅读:
    序列JSON数据和四种AJAX操作方式
    jquery.validate和jquery.form.js实现表单提交
    JQuery Validate使用总结1:
    HOWTO: Include Base64 Encoded Binary Image Data (data URI scheme) in Inline Cascading Style Sheets (CSS)(转)
    SharePoint 2007 使用4.0 .Net
    动态IP解决方案
    取MS CRM表单的URL
    从Iframe或新开的窗口访问MS CRM 2011(转)
    Toggle or Hidden MS CRM Tab
    Windows 2008下修改域用户密码
  • 原文地址:https://www.cnblogs.com/qlqwjy/p/10130454.html
Copyright © 2011-2022 走看看