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

 

posted @ 2018-01-18 15:45  Mr.years  阅读(242)  评论(0编辑  收藏  举报