【java多线程编程核心技术】4.Lock的使用-笔记总结

来源:互联网 发布:什么是qq群淘宝客 编辑:程序博客网 时间:2024/05/17 09:34

使用ReentrantLock类

一个可重入的互斥锁 Lock,它具有与使用 synchronized方法和语句所访问的隐式监视器锁相同的一些基本行为和语义,并且在扩充功能上也更强大,比如具有嗅探锁定多路分支通知等功能。

使用ReentranLock实现同步

package service;import java.util.concurrent.locks.Lock;import java.util.concurrent.locks.ReentrantLock;public class MyService {    private Lock lock = new ReentrantLock();    public void testMethod() {        lock.lock();        for (int i = 0; i < 5; i++) {            System.out.println("ThreadName=" + Thread.currentThread().getName()                    + (" " + (i + 1)));        }        lock.unlock();    }}......省略代码

与synchronized用法相似,只是多出来一个解锁(lock.unlock())的过程

使用Condition实现等待/通知

与上一章通过wait()和notify()/notifyAll()类似,ReentrantLock类也可以实现同样的功能,但需要借助于Condition对象(await()与signal/signalAll())
ReentrantLock类结合Condition类可以实现“选择性通知”(notify()进行通知时,被通知的线程是由JVM随机选择的)

package extthread;import service.MyService;public class ThreadA extends Thread {     private MyService service;     public ThreadA(MyService service) {          super();          this.service = service;     }     @Override     public void run() {          service.await();     }}package service;import java.util.concurrent.locks.Condition;import java.util.concurrent.locks.Lock;import java.util.concurrent.locks.ReentrantLock;public class MyService {    private Lock lock = new ReentrantLock();    public 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();        }    }}package test;import service.MyService;import extthread.ThreadA;public class Run {    public static void main(String[] args) throws InterruptedException {        MyService service = new MyService();        ThreadA a = new ThreadA(service);        a.start();        Thread.sleep(3000);        service.signal();    }}输出结果: await时间为1511258842692signal时间为1511258845691

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

package extthread;import service.MyService;public class ThreadA extends Thread {     private MyService service;     public ThreadA(MyService service) {          super();          this.service = service;     }     @Override     public void run() {          service.awaitA();     }}package extthread;import service.MyService;public class ThreadB extends Thread {     private MyService service;     public ThreadB(MyService service) {          super();          this.service = service;     }     @Override     public void run() {          service.awaitB();     }}package service;import java.util.concurrent.locks.Condition;import java.util.concurrent.locks.Lock;import java.util.concurrent.locks.ReentrantLock;public class MyService {    private Lock lock = new ReentrantLock();    public Condition conditionA = lock.newCondition();    public Condition conditionB = lock.newCondition();    public void awaitA() {        try {            lock.lock();            System.out.println("begin awaitA时间为" + System.currentTimeMillis()                    + " ThreadName=" + Thread.currentThread().getName());            conditionA.await();            System.out.println("  end awaitA时间为" + System.currentTimeMillis()                    + " ThreadName=" + Thread.currentThread().getName());        } catch (InterruptedException e) {            e.printStackTrace();        } finally {            lock.unlock();        }    }    public void awaitB() {        try {            lock.lock();            System.out.println("begin awaitB时间为" + System.currentTimeMillis()                    + " ThreadName=" + Thread.currentThread().getName());            conditionB.await();            System.out.println("  end awaitB时间为" + System.currentTimeMillis()                    + " ThreadName=" + Thread.currentThread().getName());        } catch (InterruptedException e) {            e.printStackTrace();        } finally {            lock.unlock();        }    }    public void signalAll_A() {        try {            lock.lock();            System.out.println("  signalAll_A时间为" + System.currentTimeMillis()                    + " ThreadName=" + Thread.currentThread().getName());            conditionA.signalAll();        } finally {            lock.unlock();        }    }    public void signalAll_B() {        try {            lock.lock();            System.out.println("  signalAll_B时间为" + System.currentTimeMillis()                    + " ThreadName=" + Thread.currentThread().getName());            conditionB.signalAll();        } finally {            lock.unlock();        }    }}package test;import service.MyService;import extthread.ThreadA;import extthread.ThreadB;public class Run {    public static void main(String[] args) throws InterruptedException {        MyService service = new MyService();        ThreadA a = new ThreadA(service);        a.setName("A");        a.start();        ThreadB b = new ThreadB(service);        b.setName("B");        b.start();        Thread.sleep(3000);        service.signalAll_A();    }}输出结果:(只通知了A)begin awaitA时间为1511258966784 ThreadName=Abegin awaitB时间为1511258966785 ThreadName=B  signalAll_A时间为1511258969785 ThreadName=main  end awaitA时间为1511258969785 ThreadName=A

使用ReentrantLock对象可以唤醒指定种类的线程,这是控制部分线程行为的方便方式。

实现生产者/消费者模式:多对多交替打印

package extthread;import service.MyService;public class MyThreadA extends Thread {     private MyService myService;     public MyThreadA(MyService myService) {          super();          this.myService = myService;     }     @Override     public void run() {          for (int i = 0; i < Integer.MAX_VALUE; i++) {              myService.set();          }     }}package extthread;import service.MyService;public class MyThreadB extends Thread {     private MyService myService;     public MyThreadB(MyService myService) {          super();          this.myService = myService;     }     @Override     public void run() {          for (int i = 0; i < Integer.MAX_VALUE; i++) {              myService.get();          }     }}package service;import java.util.concurrent.locks.Condition;import java.util.concurrent.locks.ReentrantLock;public class MyService {    private ReentrantLock lock = new ReentrantLock();    private Condition condition = lock.newCondition();    private boolean hasValue = false;    public void set() {        try {            lock.lock();            while (hasValue == true) {                System.out.println("有可能★★连续");                condition.await();            }            System.out.println("打印★");            hasValue = true;            condition.signalAll();        } catch (InterruptedException e) {            e.printStackTrace();        } finally {            lock.unlock();        }    }    public void get() {        try {            lock.lock();            while (hasValue == false) {                System.out.println("有可能☆☆连续");                condition.await();            }            System.out.println("打印☆");            hasValue = false;            condition.signalAll();        } catch (InterruptedException e) {            e.printStackTrace();        } finally {            lock.unlock();        }    }}package test;import service.MyService;import extthread.MyThreadA;import extthread.MyThreadB;public class Run {    public static void main(String[] args) throws InterruptedException {        MyService service = new MyService();        MyThreadA[] threadA = new MyThreadA[10];        MyThreadB[] threadB = new MyThreadB[10];        for (int i = 0; i < 10; i++) {            threadA[i] = new MyThreadA(service);            threadB[i] = new MyThreadB(service);            threadA[i].start();            threadB[i].start();        }    }}输出结果:有可能★★连续有可能★★连续打印☆有可能☆☆连续有可能☆☆连续有可能☆☆连续打印★有可能★★连续打印☆.......

出现连续★★或者☆☆是因为signalAll()唤醒的同类争抢到了锁,这也是**只使用一个**condition的缺点吧。

公平锁与非公平锁

锁Lock分为“公平锁”和“非公平锁”。
公平锁:表示线程获取锁的顺序是按照线程加锁的顺序来分配,即先来先得的FIFO顺序(也可以说成优先选择等待时间最长的线程)
非公平锁:是一种获取锁的抢占机制,是随机获取锁。

设置方法:lock=new ReentrantLock(true)/lock=new ReentrantLock(false) 默认是false,不公平

常用API介绍

getHoldCount() 查询当前线程保持此锁定的个数,也就是调用 lock() 的方法//lock.getHoldCount();     intgetQueueLength() 返回正等待获取此锁定的线程估计数//lock.getQueueLength();  intgetWaitQueueLength() 返回等待与此锁定相关的给定条件 Condition 的线程估计数//lock.getWaitQueueLength(newCondition)   int   个人感觉有点类似于上一章提到的“阻塞队列”hasQueuedThread() 查询指定的线程是否正在等待获取此锁定//lock.hasQueuedThread(threadA);   booleanhasQueuedThreads() 查询是否有线程正在等待获取此锁定//lock.hasQueuedThreads;       booleanhasWaiters() 查询是否有线程正在等待与此锁定有关的 condition 条件//lock.hasWaiters(newCondition);  booleanisFair() 判断是否是公平锁//lock.isFair();    booleanisHeldByCurrentThread() 查询当前线程是否保持此锁定//lock.isHeldByCurrentThread();    booleanisLocked() 查询此锁定是否由任意线程保持//lock.isLocked();    booleanlockInterruptibly() 如果当前线程未被中断,则获取锁定,如果已经被中断则出现异常//lock.lockInterruptibly();     voidtryLock() 仅在调用时锁定未被另一个线程保持的情况下,才获取该锁定(拿不到,立即返回)//lock.tryLock();   booleantryLock(long time, TimeUtil util) 如果锁定在给定的等待时间内没有被另一个线程保持,且当前线程未被中断,则获取该锁定。//lock.tryLock(3,TimeUtil.SECONDS);    booleanawaitUninterruptibly():等待释放锁而且在线程被打断时interrupt()不会抛出异常//condition.awaitUninterruptibly()  awaitUntil():如果在对应的时间内未被唤醒,则在对应时间结束后自动唤醒自己用法:           Calendar calendarRef = Calendar.getInstance();              calendarRef.add(Calendar.SECOND, 10);                        lock.lock();            condition.awaitUntil(calendarRef.getTime());

述API常用配套使用:

     public ReentrantLock lock = new ReentrantLock();     public void waitMethod() {          try {              if (lock.tryLock(3, TimeUnit.SECONDS)) { //用法                   System.out.println("      " + Thread.currentThread().getName()                             + "获得锁的时间:" + System.currentTimeMillis());                   Thread.sleep(10000);              } else {                   System.out.println("      " + Thread.currentThread().getName()                             + "没有获得锁");              }          } catch (InterruptedException e) {              e.printStackTrace();          } finally {              if (lock.isHeldByCurrentThread()) {   //用法:                   lock.unlock();              }          }     }}

使用Condition实现顺序执行

package finaltools;public class F {     volatile public static int nextPrintWho = 1;}package test.run;import java.util.concurrent.locks.Condition;import java.util.concurrent.locks.ReentrantLock;public class Run {     volatile private static int nextPrintWho = 1;     private static ReentrantLock lock = new ReentrantLock();     final private static Condition conditionA = lock.newCondition();     final private static Condition conditionB = lock.newCondition();     final private static Condition conditionC = lock.newCondition();     public static void main(String[] args) {          Thread threadA = new Thread() {              public void run() {                   try {                        lock.lock();                        while (nextPrintWho != 1) {                             conditionA.await();                        }                        for (int i = 0; i < 3; i++) {                             System.out.println("ThreadA " + (i + 1));                        }                        nextPrintWho = 2;                        conditionB.signalAll();                   } catch (InterruptedException e) {                        e.printStackTrace();                   } finally {                        lock.unlock();                   }              }          };          Thread threadB = new Thread() {              public void run() {                   try {                        lock.lock();                        while (nextPrintWho != 2) {                             conditionB.await();                        }                        for (int i = 0; i < 3; i++) {                             System.out.println("ThreadB " + (i + 1));                        }                        nextPrintWho = 3;                        conditionC.signalAll();                   } catch (InterruptedException e) {                        e.printStackTrace();                   } finally {                        lock.unlock();                   }              }          };          Thread threadC = new Thread() {              public void run() {                   try {                        lock.lock();                        while (nextPrintWho != 3) {                             conditionC.await();                        }                        for (int i = 0; i < 3; i++) {                             System.out.println("ThreadC " + (i + 1));                        }                        nextPrintWho = 1;                        conditionA.signalAll();                   } catch (InterruptedException e) {                        e.printStackTrace();                   } finally {                        lock.unlock();                   }              }          };          Thread[] aArray = new Thread[5];          Thread[] bArray = new Thread[5];          Thread[] cArray = new Thread[5];          for (int i = 0; i < 5; i++) {              aArray[i] = new Thread(threadA);              bArray[i] = new Thread(threadB);              cArray[i] = new Thread(threadC);              aArray[i].start();              bArray[i].start();              cArray[i].start();          }     }}输出结果:ThreadA 1ThreadA 2ThreadA 3ThreadB 1ThreadB 2ThreadB 3ThreadC 1ThreadC 2ThreadC 3.......

使用ReentrantReadWriteLock类

读写锁ReentrantReadWriteLock类,表示有2个锁,一个是读操作相关的锁,也称为共享锁。另一个是写操作相关的锁,也叫排他锁。多个读锁之间不互斥,写锁与写锁/写锁与读锁 互斥。

读写互斥demo代码:

package extthread;import service.Service;public class ThreadA extends Thread {     private Service service;     public ThreadA(Service service) {          super();          this.service = service;     }     @Override     public void run() {          service.read();     }}package extthread;import service.Service;public class ThreadB extends Thread {     private Service service;     public ThreadB(Service service) {          super();          this.service = service;     }     @Override     public void run() {          service.write();     }}package service;import java.util.concurrent.locks.ReentrantReadWriteLock;public class Service {     private ReentrantReadWriteLock lock = new ReentrantReadWriteLock();     public void read() {          try {              try {                   lock.readLock().lock();                   System.out.println("获得读锁A" + Thread.currentThread().getName()                             + " " + System.currentTimeMillis());                   Thread.sleep(10000);              } finally {                   lock.readLock().unlock();              }          } catch (InterruptedException e) {              e.printStackTrace();          }     }     public void write() {          try {              try {                   lock.writeLock().lock();                   System.out.println("获得写锁B" + Thread.currentThread().getName()                             + " " + System.currentTimeMillis());                   Thread.sleep(10000);              } finally {                   lock.writeLock().unlock();              }          } catch (InterruptedException e) {              e.printStackTrace();          }     }}}package test;import service.Service;import extthread.ThreadA;import extthread.ThreadB;public class Run {    public static void main(String[] args) throws InterruptedException {        Service service = new Service();        ThreadA a = new ThreadA(service);        a.setName("A");        a.start();        Thread.sleep(1000);        ThreadB b = new ThreadB(service);        b.setName("B");        b.start();    }}输出结果:获得读锁AA 1511263792194获得写锁BB 1511263802194

读写”、“写读”和“写写”都是互斥的;而“读读”是异步的,非互斥的。

阅读全文
0 0