zoukankan      html  css  js  c++  java
  • 多线程---ReentrantLock

    package com.test;
    import java.util.Collection;
    import java.util.concurrent.locks.Lock;
    import java.util.concurrent.locks.ReentrantLock;
    
    // LockTest1.java
    // 仓库
    class Depot { 
        private int size;        // 仓库的实际数量
        private Lock lock;        // 独占锁
    
        public Depot() {
            this.size = 0;
            this.lock = new ReentrantLock();
        }
    
        public void produce(int val) {
            lock.lock();
            try {
                size += val;
                System.out.printf("%s produce(%d) --> size=%d
    ", 
                        Thread.currentThread().getName(), val, size);
            } finally {
                lock.unlock();
            }
        }
    
        public void consume(int val) {
            lock.lock();
            try {
                size -= val;
                System.out.printf("%s consume(%d) <-- size=%d
    ", 
                        Thread.currentThread().getName(), val, size);
            } finally {
                lock.unlock();
            }
        }
    }; 
    
    // 生产者
    class Producer {
        private Depot depot;
        
        public Producer(Depot depot) {
            this.depot = depot;
        }
    
        // 消费产品:新建一个线程向仓库中生产产品。
        public void produce(final int val) {
            new Thread() {
                public void run() {
                    depot.produce(val);
                }
            }.start();
        }
    }
    
    // 消费者
    class Customer {
        private Depot depot;
        
        public Customer(Depot depot) {
            this.depot = depot;
        }
    
        // 消费产品:新建一个线程从仓库中消费产品。
        public void consume(final int val) {
            new Thread() {
                public void run() {
                    depot.consume(val);
                }
            }.start();
        }
    }
    
    public class LockTest1 {  
        public static void main(String[] args) {  
            Depot mDepot = new Depot();
            Producer mPro = new Producer(mDepot);
            Customer mCus = new Customer(mDepot);
            //4个线程,2个线程操作produce方法,2个线程操作consume方法。4个线程使用同一个锁,每次只能由一个线程执行。
            mPro.produce(60);
            mPro.produce(120);
            mCus.consume(90);
            mCus.consume(150);
        }
    }
    ReentrantLock是一个可重入的互斥锁,又被称为“独占锁”。
    
    顾名思义,ReentrantLock锁在同一个时间点只能被一个线程锁持有;而可重入的意思是,ReentrantLock锁,可以被单个线程多次获取。
    ReentrantLock分为“公平锁”和“非公平锁”。它们的区别体现在获取锁的机制上是否公平。“锁”是为了保护竞争资源,防止多个线程同时操作线程而出错,ReentrantLock在同一个时间点只能被一个线程获取(当某线程获取到“锁”时,其它线程就必须等待);ReentraantLock是通过一个FIFO的等待队列来管理获取该锁所有线程的。在“公平锁”的机制下,线程依次排队获取锁;而“非公平锁”在锁是可获取状态时,不管自己是不是在队列的开头都会获取锁。
    Condition的作用是对锁进行更精确的控制。Condition中的await()方法相当于Object的wait()方法,Condition中的signal()方法相当于Object的notify()方法,Condition中的signalAll()相当于Object的notifyAll()方法。不同的是,Object中的wait(),notify(),notifyAll()方法是和"同步锁"(synchronized关键字)捆绑使用的;而Condition是需要与"互斥锁"/"共享锁"捆绑使用的
    Condition函数列表
    
    复制代码
    // 造成当前线程在接到信号或被中断之前一直处于等待状态。
    void await()
    // 造成当前线程在接到信号、被中断或到达指定等待时间之前一直处于等待状态。
    boolean await(long time, TimeUnit unit)
    // 造成当前线程在接到信号、被中断或到达指定等待时间之前一直处于等待状态。
    long awaitNanos(long nanosTimeout)
    // 造成当前线程在接到信号之前一直处于等待状态。
    void awaitUninterruptibly()
    // 造成当前线程在接到信号、被中断或到达指定最后期限之前一直处于等待状态。
    boolean awaitUntil(Date deadline)
    // 唤醒一个等待线程。
    void signal()
    // 唤醒所有等待线程。
    void signalAll()
    Condition示例
    
    示例1是通过Object的wait(), notify()来演示线程的休眠/唤醒功能。
    示例2是通过Condition的await(), signal()来演示线程的休眠/唤醒功能。
    示例3是通过Condition的高级功能。
    
    示例1
    
    复制代码
    public class WaitTest1 {
    
        public static void main(String[] args) {
    
            ThreadA ta = new ThreadA("ta");
    
            synchronized(ta) { // 通过synchronized(ta)获取“对象ta的同步锁”
                try {
                    System.out.println(Thread.currentThread().getName()+" start ta");
                    ta.start();
    
                    System.out.println(Thread.currentThread().getName()+" block");
                    ta.wait();    // 等待
    
                    System.out.println(Thread.currentThread().getName()+" continue");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    
        static class ThreadA extends Thread{
    
            public ThreadA(String name) {
                super(name);
            }
    
            public void run() {
                synchronized (this) { // 通过synchronized(this)获取“当前对象的同步锁”
                    System.out.println(Thread.currentThread().getName()+" wakup others");
                    notify();    // 唤醒“当前对象上的等待线程”
                }
            }
        }
    }
    复制代码
     
    
    示例2
    
    复制代码
    import java.util.concurrent.locks.Lock;
    import java.util.concurrent.locks.Condition;
    import java.util.concurrent.locks.ReentrantLock;
    
    public class ConditionTest1 {
            
        private static Lock lock = new ReentrantLock();
        private static Condition condition = lock.newCondition();
    
        public static void main(String[] args) {
    
            ThreadA ta = new ThreadA("ta");
    
            lock.lock(); // 获取锁
            try {
                System.out.println(Thread.currentThread().getName()+" start ta");
                ta.start();
    
                System.out.println(Thread.currentThread().getName()+" block");
                condition.await();    // 等待,会释放锁
    
                System.out.println(Thread.currentThread().getName()+" continue");
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                lock.unlock();    // 释放锁
            }
        }
    
        static class ThreadA extends Thread{
    
            public ThreadA(String name) {
                super(name);
            }
    
            public void run() {
                lock.lock();    // 获取锁
                try {
                    System.out.println(Thread.currentThread().getName()+" wakup others");
                    condition.signal();    // 唤醒“condition所在锁上的其它线程”
                } finally {
                    lock.unlock();    // 释放锁
                }
            }
        }
    }
    复制代码
    运行结果:
    
    main start ta
    main block
    ta wakup others
    main continue
    通过“示例1”和“示例2”,我们知道Condition和Object的方法有一下对应关系:
    
                  Object      Condition  
    休眠          wait        await
    唤醒个线程     notify      signal
    唤醒所有线程   notifyAll   signalAll
    Condition除了支持上面的功能之外,它更强大的地方在于:能够更加精细的控制多线程的休眠与唤醒。对于同一个锁,我们可以创建多个Condition,在不同的情况下使用不同的Condition。
    例如,假如多线程读/写同一个缓冲区:当向缓冲区中写入数据之后,唤醒"读线程";当从缓冲区读出数据之后,唤醒"写线程";并且当缓冲区满的时候,"写线程"需要等待;当缓冲区为空时,"读线程"需要等待。         如果采用Object类中的wait(), notify(), notifyAll()实现该缓冲区,当向缓冲区写入数据之后需要唤醒"读线程"时,不可能通过notify()或notifyAll()明确的指定唤醒"读线程"而只能通过notifyAll唤醒所有线程(但是notifyAll无法区分唤醒的线程是读线程,还是写线程)。  但是,通过Condition,就能明确的指定唤醒读线程。
    看看下面的示例3,可能对这个概念有更深刻的理解。

    满的时候只能减,空的时候只能加。加和减是2个线程组。

    package com.test;
    import java.util.concurrent.locks.Lock;
    import java.util.concurrent.locks.ReentrantLock;
    import java.util.concurrent.locks.Condition;
    
    // LockTest3.java
    // 仓库
    class Depot1 {
        private int capacity;    // 仓库的容量
        private int size;        // 仓库的实际数量
        private Lock lock;        // 独占锁
        private Condition fullCondtion;            // 生产条件
        private Condition emptyCondtion;        // 消费条件
    
        public Depot1(int capacity) {
            this.capacity = capacity;
            this.size = 0;
            this.lock = new ReentrantLock();
            this.fullCondtion = lock.newCondition();
            this.emptyCondtion = lock.newCondition();
        }
    
        public void produce(int val) {
            lock.lock();
            try {
                 // left 表示“想要生产的数量”(有可能生产量太多,需多此生产)
                while (val > 0) {
                    // 库存已满时,等待“消费者”消费产品。
                    while (size >= capacity)
                        fullCondtion.await();  //等待,释放锁
                    // 获取“实际生产的数量”(即库存中新增的数量)
                    // 如果“库存”+“想要生产的数量”>“总的容量”,则“实际增量”=“总的容量”-“当前容量”。(此时填满仓库)
                    // 否则“实际增量”=“想要生产的数量”
                    size += val;
                    System.out.printf("%s produce --> val=%3d,  size=%3d
    ", 
                            Thread.currentThread().getName(), val, size);
                    // 通知“消费者”可以消费了。
                    emptyCondtion.signal();
                }
            } catch (InterruptedException e) {
            } finally {
                lock.unlock();
            }
        }
    
        public void consume(int val) {
            lock.lock();
            try {
                // left 表示“客户要消费数量”(有可能消费量太大,库存不够,需多此消费)
                while (val > 0) {
                    // 库存为0时,等待“生产者”生产产品。
                    while (size <= 0)
                        emptyCondtion.await();
                    // 获取“实际消费的数量”(即库存中实际减少的数量)
                    // 如果“库存”<“客户要消费的数量”,则“实际消费量”=“库存”;
                    // 否则,“实际消费量”=“客户要消费的数量”。
                    size -= val;
                    System.out.printf("%s consume<-- val=%3d, size=%3d
    ", 
                            Thread.currentThread().getName(), val, size);
                    fullCondtion.signal();
                }
            } catch (InterruptedException e) {
            } finally {
                lock.unlock();
            }
        }
    
        public String toString() {
            return "capacity:"+capacity+", actual size:"+size;
        }
    }; 
    
    // 生产者
    class Producer1 {
        private Depot1 depot;
        
        public Producer1(Depot1 depot) {
            this.depot = depot;
        }
    
        // 消费产品:新建一个线程向仓库中生产产品。
        public void produce(final int val) {
            new Thread() {
                public void run() {
                    depot.produce(val);
                }
            }.start();
        }
    }
    
    // 消费者
    class Customer1 {
        private Depot1 depot;
        
        public Customer1(Depot1 depot) {
            this.depot = depot;
        }
    
        // 消费产品:新建一个线程从仓库中消费产品。
        public void consume(final int val) {
            new Thread() {
                public void run() {
                    depot.consume(val);
                }
            }.start();
        }
    }
    
    public class LockTest3 {  
        public static void main(String[] args) {  
            Depot1 mDepot = new Depot1(100);
            Producer1 mPro = new Producer1(mDepot);
            Customer1 mCus = new Customer1(mDepot);
    
            //5个线程共用同一把锁。2个线程减3个线程加。
            mPro.produce(60);
            mPro.produce(120);
            mCus.consume(90);
            mCus.consume(150);
            mPro.produce(110);
        }
    }
  • 相关阅读:
    js弹出遮罩层
    委托集合使用
    C#动态属性(.NET Framework4.5支持)
    ABP项目启动及源代码结构
    ABP项目概述
    Visual Studio 2017调试开源项目代码
    GIT的前世今生
    近期上海面试总结(二)
    近期上海面试总结(一)
    一个基于反射的消息发布订阅模型详解
  • 原文地址:https://www.cnblogs.com/yaowen/p/9317674.html
Copyright © 2011-2022 走看看