在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(); } }
测试结果:调用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(); } }
相似处:
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(); } }
测试结果:线程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(); } } }
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(); } }
测试结果:几乎同时获得读锁,说明它们不互斥的
获得读锁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(); } }
测试结果:写操作需要等待读锁释放后才能获得
获得读锁Thread-0 1516246209130
获得写锁Thread-1 1516246219132