zoukankan      html  css  js  c++  java
  • Java多线程技术-Lock/Condition

    在java1.5中Lock对象来实现同步的效果,而且使用上更方便。

    使用ReentrantLock实现同步

    public class MyService {
    
        private Lock lock = new ReentrantLock();
        
        public void methodA(){
            try {
                lock.lock();
                System.out.println("methodA begin threadName=" + Thread.currentThread().getName()+" time="+System.currentTimeMillis());
                Thread.sleep(5000);
                System.out.println("methodA end threadName=" + Thread.currentThread().getName()+" time="+System.currentTimeMillis());
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }
        
        public void methodB(){
            try {
                lock.lock();
                System.out.println("methodB begin threadName=" + Thread.currentThread().getName()+" time="+System.currentTimeMillis());
                Thread.sleep(5000);
                System.out.println("methodB end threadName=" + Thread.currentThread().getName()+" time="+System.currentTimeMillis());
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }
    }
    
    public class Test {
        public static void main(String[] args) throws InterruptedException {
            MyService service = new MyService();
            new Thread(()->service.methodA()).start();
            new Thread(()->service.methodA()).start();
            new Thread(()->service.methodB()).start();
            new Thread(()->service.methodB()).start();
    
        }
    }
    View Code

    测试结果:调用lock.lock()的线程就持有了“对象监视器”,其他线程只有等待锁被释放时再次争抢,效果和synchronized一样。

    methodA begin threadName=Thread-0 time=1516242293668
    methodA end threadName=Thread-0 time=1516242298669
    methodA begin threadName=Thread-1 time=1516242298669
    methodA end threadName=Thread-1 time=1516242303671
    methodB begin threadName=Thread-2 time=1516242303671
    methodB end threadName=Thread-2 time=1516242308672
    methodB begin threadName=Thread-3 time=1516242308672
    methodB end threadName=Thread-3 time=1516242313674

    使用Condition实现等待/通知

    public class MyService {
        private Lock lock = new ReentrantLock();
        private Condition condition = lock.newCondition();
        public void await(){
            try {
                lock.lock();
                System.out.println("await时间为"+System.currentTimeMillis());
                condition.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }
        
        public void signal(){
            try {
                lock.lock();
                System.out.println("signal时间为"+System.currentTimeMillis());
                condition.signal();
            } finally {
                lock.unlock();
            }
        }
    }
    
    public class Run {
    
        public static void main(String[] args) throws InterruptedException {
            MyService service = new MyService();
            new Thread(()->service.await()).start();
            Thread.sleep(3000);
            service.signal();
        }
    }
    View Code

    相似处:

    1. 在使用Condition的await方法和signal方法之前比较先调用lock.lock(),否则会抛出异常,跟wait和nofity一样,需在synchronized的代码里运行一样

    2. Object.wait() --> Condition.await(). Object.notify()--> Condition.signal(), Object.notifyAll() -->Condition.signalAll()

    优点:

    在一个Lock对象里面可以创建多个Condition实例,线程对象可以注册在指定的Condition中,从而可以有选择性的进行线程通知,在调度上更加灵活。

    使用多个Condition实现通知部分线程

    public class MyService {
        private Lock lock = new ReentrantLock();
        private Condition conditionA = lock.newCondition();
        private Condition conditionB = lock.newCondition();
        public void awaitA(){
            try {
                lock.lock();
                System.out.println("begin awaitA时间为"+System.currentTimeMillis() + "Thread name="+Thread.currentThread().getName());
                conditionA.await();
                System.out.println("end awaitA时间为"+System.currentTimeMillis() + "Thread name="+Thread.currentThread().getName());
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }
        
        public void awaitB(){
            try {
                lock.lock();
                System.out.println("begin awaitB时间为"+System.currentTimeMillis() + "Thread name="+Thread.currentThread().getName());
                conditionB.await();
                System.out.println("end awaitB时间为"+System.currentTimeMillis() + "Thread name="+Thread.currentThread().getName());
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }
        
        public void signalAll_A(){
            try {
                lock.lock();
                System.out.println("signalAll_B 时间为"+System.currentTimeMillis() + "Thread name="+Thread.currentThread().getName());
                conditionA.signalAll();
            } finally {
                lock.unlock();
            }
        }
        
        public void signalAll_B(){
            try {
                lock.lock();
                System.out.println("signalAll_B 时间为"+System.currentTimeMillis() + "Thread name="+Thread.currentThread().getName());
                conditionB.signalAll();
            } finally {
                lock.unlock();
            }
        }
    }
    
    public class Run {
    
        public static void main(String[] args) throws InterruptedException {
            MyService service = new MyService();
            new Thread(()->service.awaitA()).start();
            new Thread(()->service.awaitB()).start();
            Thread.sleep(3000);
            service.signalAll_A();
        }
    }
    View Code

    测试结果:线程B没有被唤醒

    begin awaitA时间为1516244219035Thread name=Thread-0
    begin awaitB时间为1516244219036Thread name=Thread-1
    signalAll_B 时间为1516244222035Thread name=main
    end awaitA时间为1516244222035Thread name=Thread-0

    生产者和消费者

    public class MyList {
        private Lock lock = new ReentrantLock();
        private Condition condition = lock.newCondition();
        private volatile List<String> list = new ArrayList<>(10);
    
        public void get() {
            try {
                System.out.println("get,listSize="+list.size());
                lock.lock();
                while (list.size() == 0) {
                    System.out.println("get 等待,ThreadName=" + Thread.currentThread().getName());
                    condition.await();
                }
                System.out.println("get -1");
                list.remove(0);
                condition.signalAll();
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally{
                lock.unlock();
            }
        }
        
        public void set() {
            try {
                System.out.println("set,listSize="+list.size());
                lock.lock();
                while (list.size() == 10) {
                    System.out.println("set 等待,ThreadName=" + Thread.currentThread().getName());
                    condition.await();
                }
                System.out.println("set+1");
                list.add("A");
                condition.signalAll();
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally{
                lock.unlock();
            }
        }
    }
    
    public class Run {
    
        public static void main(String[] args) throws InterruptedException {
            MyList consumer = new MyList();
            for(int i =0;i<10;i++){
                new Thread(()->{
                    while(true){ consumer.set();}
                }).start();
                new Thread(()->{
                    while(true){ consumer.get();}
                }).start();
            }
        }
    }
    View Code

    ReentrantReadWriteLock

    ReentrantLock具有完全互斥排他的效果,即在同一时间内只有一个线程在执行ReentrantLock.lock()方法后面的任务。但是效率相对低下,而是用ReentrantReadWriteLock读写锁,可以相对提高效率。读写锁也就是有两个锁,一个是和读相关的锁,也称共享锁,一个是和写相关的锁,也叫排它锁。只有读读锁之间不互斥,其他都互斥。

    读读锁不互斥的例子

    public class Service {
        
        private ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
        public void read(){
            try {
                lock.readLock().lock();
                System.out.println("获得读锁"+Thread.currentThread().getName()+" " + System.currentTimeMillis());
                Thread.sleep(10000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                lock.readLock().unlock();
            }
        }
    }
    
    public class Run {
    
        public static void main(String[] args) throws InterruptedException {
            Service service = new Service();
            new Thread(()->service.read()).start();
            new Thread(()->service.read()).start();
        }
    }
    View Code

    测试结果:几乎同时获得读锁,说明它们不互斥的

    获得读锁Thread-0 1516246020468
    获得读锁Thread-1 1516246020469

    读写互斥的例子

    public class Service {
        
        private ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
        public void read(){
            try {
                lock.readLock().lock();
                System.out.println("获得读锁"+Thread.currentThread().getName()+" " + System.currentTimeMillis());
                Thread.sleep(10000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                lock.readLock().unlock();
            }
        }
        
        public void write(){
            try {
                lock.writeLock().lock();
                System.out.println("获得写锁"+Thread.currentThread().getName()+" " + System.currentTimeMillis());
                Thread.sleep(10000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                lock.writeLock().unlock();
            }
        }
    }
    
    public class Run {
    
        public static void main(String[] args) throws InterruptedException {
            Service service = new Service();
            new Thread(()->service.read()).start();
            new Thread(()->service.write()).start();
        }
    }
    View Code

    测试结果:写操作需要等待读锁释放后才能获得

    获得读锁Thread-0 1516246209130
    获得写锁Thread-1 1516246219132
  • 相关阅读:
    第05组 Beta版本演示
    第05组 Beta冲刺(4/4)
    第05组 Beta冲刺(3/4)
    第05组 Beta冲刺(2/4)
    第05组 Beta冲刺(1/4)
    第05组 Alpha事后诸葛亮
    第05组 Alpha冲刺(4/4)
    第05组 Alpha冲刺(3/4)
    第05组 Alpha冲刺(2/4)
    300iq Contest 3 C. Cells Blocking
  • 原文地址:https://www.cnblogs.com/lostyears/p/8309335.html
Copyright © 2011-2022 走看看