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

相关链接:
【Java多线程编程核心技术】1.Java多线程技能-笔记总结
【Java多线程编程核心技术】2.对象及变量的并发访问(上)-笔记总结
【Java多线程编程核心技术】2.对象及变量的并发访问(下)-笔记总结
【Java多线程编程核心技术】3.线程间通信 -笔记总结
【Java多线程编程核心技术】4.Lock的使用-笔记总结
【Java多线程编程核心技术】5.定时器Timer-笔记总结
【Java多线程编程核心技术】6.单例模式与多线程-笔记总结
【Java多线程编程核心技术】7.拾遗增补-笔记总结

使用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时间为1511258842692
signal时间为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=A
begin 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();     int

getQueueLength() 返回正等待获取此锁定的线程估计数
//lock.getQueueLength();  int

getWaitQueueLength() 返回等待与此锁定相关的给定条件 Condition 的线程估计数
//lock.getWaitQueueLength(newCondition)   int   
个人感觉有点类似于上一章提到的“阻塞队列”

hasQueuedThread() 查询指定的线程是否正在等待获取此锁定
//lock.hasQueuedThread(threadA);   boolean

hasQueuedThreads() 查询是否有线程正在等待获取此锁定
//lock.hasQueuedThreads;       boolean

hasWaiters() 查询是否有线程正在等待与此锁定有关的 condition 条件
//lock.hasWaiters(newCondition);  boolean

isFair() 判断是否是公平锁
//lock.isFair();    boolean

isHeldByCurrentThread() 查询当前线程是否保持此锁定
//lock.isHeldByCurrentThread();    boolean

isLocked() 查询此锁定是否由任意线程保持
//lock.isLocked();    boolean

lockInterruptibly() 如果当前线程未被中断,则获取锁定,如果已经被中断则出现异常
//lock.lockInterruptibly();     void

tryLock() 仅在调用时锁定未被另一个线程保持的情况下,才获取该锁定(拿不到,立即返回)
//lock.tryLock();   boolean

tryLock(long time, TimeUtil util) 如果锁定在给定的等待时间内没有被另一个线程保持,且当前线程未被中断,则获取该锁定。
//lock.tryLock(3,TimeUtil.SECONDS);    boolean

awaitUninterruptibly():等待释放锁而且在线程被打断时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 1
ThreadA 2
ThreadA 3
ThreadB 1
ThreadB 2
ThreadB 3
ThreadC 1
ThreadC 2
ThreadC 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

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

你可能感兴趣的:(Java多线程,Java多线程编程核心技术)