一、前言
本文要介绍使用Java5中 Lock 对象,同样也能实现同步的效果,而且在使用上更加方便、灵活,主要包括 ReentrantLock 类的使用和ReentrantReadWriteLock 类的使用。
二、使用ReentrantLock 类
1、在java多线程中,可以使用synchronized关键字来实现线程之间同步互斥,但在JDK1.5中新增加的ReentrantLock也能达到同样的效果,并且在扩展功能上也更加强大,比如具有嗅探锁定、多路分支通知等功能,而且在使用上也比synchronized更加的灵活。
2、调用lock.lock()代码的线程就持有了“对象监视器”,即lock 持有的是对象锁,依赖于该类的实例存在。
public class MyService { private Lock lock=new ReentrantLock(); public void testMethod(){ lock.lock(); for(int i=0;i<5;i++){ System.out.println(Thread.currentThread().getName()+(i+1)); } lock.unlock(); } }
3、关键字synchronized 与wait() 和 notify()/notifyAll() 方法相结合可以实现等待/通知模式,类ReentrantLock 也可以实现同样的功能,但需要借助于Condition对象。
public class Myservice { private Lock lock=new ReentrantLock(); private Condition condition=lock.newCondition(); //等待 public void waitMethod(){ try { lock.lock(); System.out.println("A"); condition.await();//调用的Condition的await等待方法也需要在同步方法中,否则会报错 System.out.println("B"); } catch (InterruptedException e) { e.printStackTrace(); }finally { lock.unlock(); } } //唤醒 public void signal(){ try { lock.lock(); System.out.println("现在开始唤醒..."); condition.signal(); }finally { lock.unlock(); } } }
4、使用多个Condition对象 实现线程之间的选择性通知。
public class MyService { private Lock lock=new ReentrantLock(); //通过定义多个Condition实现选择性通知,可以唤醒指定种类的线程,这是 //控制部分线程行为的方便形式 private Condition conditionA=lock.newCondition(); private Condition conditionB=lock.newCondition(); public void awaitA(){ try { lock.lock(); System.out.println("awaitA begin"); conditionA.await(); System.out.println("awaitA end"); } catch (InterruptedException e) { e.printStackTrace(); }finally { lock.unlock(); } } public void awaitB(){ try { lock.lock(); System.out.println("awaitB begin"); conditionB.await(); System.out.println("awaitB end"); } catch (InterruptedException e) { e.printStackTrace(); }finally { lock.unlock(); } } public void signalA(){ try { lock.lock(); System.out.println("现在开始唤醒awaitA"); conditionA.signalAll(); }finally { lock.unlock(); } } public void signalB(){ try { lock.lock(); System.out.println("现在开始唤醒awaitB"); conditionB.signalAll(); }finally { lock.unlock(); } } }
public class Run { public static void main(String[] args) throws InterruptedException { MyService myService=new MyService(); Thread threadA=new Thread(){ @Override public void run() { super.run(); myService.awaitA(); } }; Thread threadB=new Thread(){ @Override public void run() { super.run(); myService.awaitB(); } }; threadA.start(); threadB.start(); Thread.sleep(1000); myService.signalA(); Thread.sleep(1000); myService.signalB(); } }
5、公平锁和非公平锁
public class Service { private Lock lock; public Service(boolean isFair) { //通过这种方式创建公平锁(true)和非公平锁(false) lock=new ReentrantLock(isFair); } public void methodA(){ try { lock.lock(); System.out.println(Thread.currentThread().getName()+"正在运行"); }finally { lock.unlock(); } } }
public class Run { public static void main(String[] args) { final Service service=new Service(true); Runnable runnable=new Runnable() { @Override public void run() { service.methodA(); } }; Thread[] threads=new Thread[10]; for (int i=0;i<10;i++){ threads[i]=new Thread(runnable); threads[i].setName("线程"+(i+1)); threads[i].start(); } } }
6、ReentrantLock 常用方法介绍
(1) int getHoldCount() 查询当前线程保持此锁定的个数,也就是线程中调用lock方法的次数。
(2) int getQueueLength() 返回正等待此锁定的线程估计数,比如有5个线程,1个线程正占用了这个Lock锁在执行,则调用此方法返回的就是4。该值仅是估计的数字,因为在此方法遍历内部数据结构的同时,线程的数目可能动态地变化。此方法用于监视系统状态,不用于同步控制。
(3) int getWaitQueueLength(Condition condition) 返回等待与此锁定相关的给定条件Condition的线程估计数,比如有五个线程,每个线程都执行了同一个condition对象的await()方法,则调用此方法返回的值就是5。
public class Service { private ReentrantLock lock=new ReentrantLock(); private Condition condition=lock.newCondition(); public void methodA(){ try { lock.lock(); System.out.println("A getHoldCount 调用lock的次数=>"+lock.getHoldCount()); Thread.sleep(2000); System.out.println("A getQueueLength 正在等待的线程数=>"+lock.getQueueLength()); condition.await(); } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.unlock(); } } //测试getWaitQueueLength方法 public Integer methodC(){ try { lock.lock(); return lock.getWaitQueueLength(condition); }finally { lock.unlock(); } } }
public class Run{ public static void main(String[] args) throws InterruptedException { Service service=new Service(); Runnable runnable=new Runnable() { @Override public void run() { service.methodA(); } }; Thread[] threads=new Thread[5]; for (int i=0;i<5;i++){ threads[i]=new Thread(runnable); threads[i].start(); } Thread.sleep(1000); System.out.println("执行了同一个Condition对象的的await()的线程有:"+service.methodC()); } }
(4) boolean hasQueuedThread(Thread thread) 查询指定的线程是否正在等待获取此锁定。
(5) boolean hasQueuedThreads() 查询是否有线程正在等待获取此锁定。
(6) boolean hasWaiters(Condition condition) 查询是否有线程正在等待与此锁定有关的condition条件
(7) boolean isFair() 判断是不是公平锁。
(8) boolean isHelpByCurrentThread() 查询当前线程是否保持此锁定。
(9) boolean isLocked() 查询此锁定是否由任意线程保持。
(10) void lockInterruptibly() 如果当前线程未被中断,则获取锁定,如果已经被中断,则出现异常。
(11) boolean tryLock() 仅在调用时锁定未被另一个线程锁定的情况下,才获得此锁定。
(12) boolean tryLock(long timeout,TimeUnit unit) 如果锁定在给定等待时间内没有被另一个线程保持,且当前线程未被中断,则获取该锁定。
public class Service { private ReentrantLock lock=new ReentrantLock(); private Condition condition=lock.newCondition(); //测试lockInterruptibly public void methodA(){ try { lock.lockInterruptibly(); System.out.println("methodA=》"+Thread.currentThread().getName()); } catch (InterruptedException e) { e.printStackTrace(); }finally { if (lock.isHeldByCurrentThread()){//如果当前线程依旧保持对此锁的锁定,则释放 lock.unlock(); } } } //测试tryLock public void methodB(){ if (lock.tryLock()){ System.out.println(Thread.currentThread().getName()+"获得锁"); }else{ System.out.println(Thread.currentThread().getName()+"未获得锁"); } } }
public class Run { public static void main(String[] args) throws InterruptedException { Service service=new Service(); Runnable runnable=new Runnable() { @Override public void run() { service.methodA(); service.methodB(); } }; Thread threadA=new Thread(runnable); threadA.setName("A"); threadA.start(); Thread.sleep(1000); Thread threadB=new Thread(runnable); threadB.setName("B"); threadB.start(); threadB.interrupt(); } }
(13) lock.awaitUninterruptibly():这个线程将不会被中断,一直睡眠直到其他线程调用signal()或signalAll()方法。
(14) lock.awaitUntil(Date date):这个线程将会一直睡眠直到:
- 它被中断
- 其他线程在这个condition上调用singal()或signalAll()方法
- 指定的日期已经到了
三、使用ReentrantReadWriteLock 类
public class Read { private ReentrantReadWriteLock lock=new ReentrantReadWriteLock(); public void read(){ try { lock.readLock().lock(); System.out.println(Thread.currentThread().getName()+"正在读"+System.currentTimeMillis()); Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.readLock().unlock(); } } }
public class Run { public static void main(String[] args) { Read read=new Read(); Runnable runnable=new Runnable() { @Override public void run() { read.read(); } }; Thread[] threads=new Thread[10]; for (int i=0;i<10;i++){ threads[i]=new Thread(runnable); threads[i].start(); //通过结果可以看到所有线程几乎同时进入lock()方法 //后面的代码,读读不互斥,可以提高程序运行效率,允许 //多个线程同时执行lock()方法后面的代码 } } }
public class Write { private ReentrantReadWriteLock lock=new ReentrantReadWriteLock(); public void write(){ try { lock.writeLock().lock(); System.out.println(Thread.currentThread().getName()+"正在写"+System.currentTimeMillis()); Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.writeLock().unlock(); } } }
public class Run { public static void main(String[] args) { Write write=new Write(); Runnable runnable=new Runnable() { @Override public void run() { write.write(); } }; Thread[] threads=new Thread[10]; for (int i=0;i<10;i++){ threads[i]=new Thread(runnable); threads[i].start(); //通过结果可以看到所有线程每隔两秒运行一次,写写互斥,线程之间是同步运行的 } } }
另外,写读、读写都是互斥的,就不举例了。总之,只要出现"写"操作,就是互斥的!