在多线程编程中,使用synchronized关键字实现线程的等待和通知,不能够通知特定的线程,所以不够灵活,所以java中还提供了另外一种锁
ReentrantLock(可重入锁),它配合Condition(对象监视器)使用可以实现特定线程的等待和唤醒。
package ReentrantLock;
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();
public void awaitMethod() {
try {
lock.lock();
System.out.println("A");
condition.await();
System.out.println("B");
}catch(InterruptedException e) {
e.printStackTrace();
}finally {
lock.unlock();
System.out.println("锁释放了");
}
}
}
package ReentrantLock;
public class ThreadA extends Thread{
private MyService service;
public ThreadA(MyService service) {
super();
this.service=service;
}
public void run() {
service.awaitMethod();
}
}
package ReentrantLock;
public class run {
public static void main(String[] args) {
MyService service=new MyService();
ThreadA a=new ThreadA(service);
a.start();
}
}
//控制台输出A,但是线程还在等待,不能够输出B
上面的这段代码是对ReentrantLock的简单测试,看ReentrantLock是否具有锁的功能。
继续看:
package ReentrantLock1;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
public class Service {
private ReentrantLock lock=new ReentrantLock();
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("sigal 的时间为:"+System.currentTimeMillis());
condition.signal();
}finally {
lock.unlock();
}
}
}
package ReentrantLock1;
public class ThreadA extends Thread{
Service service ;
public ThreadA(Service service) {
super();
this.service=service;
}
public void run() {
service.await();
}
}
package ReentrantLock1;
public class Run {
public static void main(String[] args) {
try {
Service service =new Service();
ThreadA a=new ThreadA(service);
a.start();
Thread.sleep(3000);
service.signal();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
//控制台输出:
//await 的时间为:1508549198801
//sigal 的时间为:1508549201808
//线程A进行等待,三秒过后主线程对其唤醒,线程结束。
以上代码测试的是线程等待与唤醒(和synchronized的关键字和wait,notify的用法基本一样)。
我们来看看如何利用Condition实现指定线程的唤醒,在这之前我们先来看一个错误的版本:
package ReentrantLock2;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class Service {
private Lock lock = new ReentrantLock();
public Condition condition = lock.newCondition();
public void awaitA() {
try {
lock.lock();
System.out.println("begin awaitA 时间为:"+System.currentTimeMillis());
condition.await();
System.out.println("end awaitA 时间为:"+System.currentTimeMillis());
}catch(InterruptedException e) {
e.printStackTrace();
}finally {
lock.unlock();
}
}
public void awaitB() {
try {
lock.lock();
System.out.println("begin awaitB 时间为:"+System.currentTimeMillis());
condition.await();
System.out.println("end awaitB 时间为:"+System.currentTimeMillis());
}catch(InterruptedException e) {
e.printStackTrace();
}finally {
lock.unlock();
}
}
public void signalAll() {
try {
lock.lock();
System.out.println("signalAll 时间为:"+System.currentTimeMillis());
condition.signalAll();
}finally {
lock.unlock();
}
}
}
package ReentrantLock2;
public class ThreadA extends Thread {
private Service service;
public ThreadA(Service service) {
super();
this.service=service;
}
public void run() {
service.awaitA();
}
}
package ReentrantLock2;
public class ThreadB extends Thread {
private Service service;
public ThreadB(Service service) {
super();
this.service=service;
}
public void run() {
service.awaitB();
}
}
package ReentrantLock2;
public class run {
public static void main(String[] args) {
try {
Service service = new Service();
ThreadA a = new ThreadA(service);
a.start();
Thread.sleep(1000);
ThreadB b = new ThreadB(service);
b.start();
Thread.sleep(3000);
service.signalAll();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
//控制台输出:
//begin awaitA 时间为:1508550421648
//begin awaitB 时间为:1508550422648
//signalAll 时间为:1508550425647
//end awaitA 时间为:1508550425647
//end awaitB 时间为:1508550425647
我们可以很容易看出,主线程调用signalAll将所有的线程唤醒了。
上例中只定义了一个Condition,其实我们只要定义多个Condition就可以实现指定线程的唤醒了:
package ReentrantLock3;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
public class Service {
private ReentrantLock 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()+"线程名:"+Thread.currentThread().getName());
conditionA.signalAll();
}finally {
lock.unlock();
}
}
public void signalAll_B() {
try {
lock.lock();
System.out.println("signalAll_B时间为:"+System.currentTimeMillis()+"线程名:"+Thread.currentThread().getName());
conditionA.signalAll();
}finally {
lock.unlock();
}
}
}
package ReentrantLock3;
public class ThreadA extends Thread{
private Service service;
public ThreadA(Service service) {
super();
this.service=service;
}
public void run() {
service.awaitA();
}
}
package ReentrantLock3;
public class ThreadB extends Thread{
private Service service;
public ThreadB(Service service) {
super();
this.service=service;
}
public void run() {
service.awaitB();
}
}
package ReentrantLock3;
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();
ThreadB b = new ThreadB(service);
b.setName("B");
b.start();
Thread.sleep(3000);
service.signalAll_A();
}
}
//控制台输出:
//begin awaitA 的时间为:1508550671233ThreadName=A
//begin awaitB 的时间为:1508550671233ThreadName=B
//signalAll_A时间为:1508550674233线程名:main
//end awaitA 的时间为:1508550674233ThreadName=A
上面代码Service中定义了conditionA和conditionB,这样我们分别调用Condition对象就可以对指定的线程唤醒。
使用ReentrantLock,condition可以实现synchronized完成的一切操作:
例如交替运行(生产者和消费者),
package ReentrantLock4;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
//一个生产者一消费者
public class Service {
private ReentrantLock lock = new ReentrantLock();
private Condition condition = lock.newCondition();
private boolean hasValue=false;
public void set() {
try {
lock.lock();
while(hasValue==true) {
condition.await();
}
System.out.println("打印☆");
hasValue=true;
condition.signal();
}catch(InterruptedException e) {
e.printStackTrace();
}finally {
lock.unlock();
}
}
public void get() {
try {
lock.lock();
while(hasValue==false) {
condition.await();
}
System.out.println("打印△");
hasValue=false;
condition.signal();
}catch(InterruptedException e) {
e.printStackTrace();
}finally {
lock.unlock();
}
}
}
//省略了线程A,B类,和主方法。
同时也可以实现多生产者多消费者,但是要将signal方法更改为signalAll方法,这样才不可能出现假死。
下面将公平锁和非公平锁,公平锁就是按队列排队,先到的线程先获得锁,非公平锁先到不一定获得锁。
package fairAndfairless;
import java.util.concurrent.locks.ReentrantLock;
public class Service {
private ReentrantLock lock;
public Service(boolean isfair) {
lock=new ReentrantLock(isfair);
}
public void ServiceMethod() {
try {
lock.lock();
System.out.println(Thread.currentThread().getName()+"线程获得锁定");
}finally {
lock.unlock();
}
}
}
package fairAndfairless;
public class test {
public static void main(String[] args) {
final Service service = new Service(true);
Runnable runnable = new Runnable() {
public void run() {
System.out.println("线程"+Thread.currentThread().getName()+"运行了");
service.ServiceMethod();
}
};
Thread[] threadArray = new Thread[10];
for(int i=0;i<10;i++) {
threadArray[i] = new Thread(runnable);
}
for(int i=0;i<10;i++) {
threadArray[i].start();
}
}
}
//控制台输出:
//线程Thread-0运行了
//线程Thread-4运行了
//线程Thread-3运行了
//Thread-4线程获得锁定
//Thread-0线程获得锁定
//线程Thread-7运行了
//线程Thread-1运行了
//线程Thread-2运行了
//线程Thread-8运行了
//线程Thread-5运行了
//线程Thread-6运行了
//Thread-3线程获得锁定
//线程Thread-9运行了
//Thread-7线程获得锁定
//Thread-1线程获得锁定
//Thread-2线程获得锁定
//Thread-8线程获得锁定
//Thread-5线程获得锁定
//Thread-6线程获得锁定
//Thread-9线程获得锁定
从控制台输出我们可以看出先启动的线程基本上先获得锁。
如果我们将test中代码改为:final Service service = new Service(false);
控制台输出:
线程Thread-0运行了
线程Thread-5运行了
Thread-0线程获得锁定
线程Thread-3运行了
线程Thread-2运行了
线程Thread-1运行了
线程Thread-4运行了
线程Thread-8运行了
Thread-5线程获得锁定
线程Thread-7运行了
Thread-3线程获得锁定
Thread-2线程获得锁定
线程Thread-6运行了
线程Thread-9运行了
Thread-6线程获得锁定
Thread-1线程获得锁定
Thread-4线程获得锁定
Thread-8线程获得锁定
Thread-7线程获得锁定
Thread-9线程获得锁定
观察我们发现好多线程并不按顺序获得锁。
下面我们来看看ReentrantLock对象的一些方法:
getHoldCount:返回当前线程保持此锁定的个数,也就是调用lock()方法的次数。
package getHoldCount;
//测试当前调用lock方法的次数
import java.util.concurrent.locks.ReentrantLock;
public class Service {
private ReentrantLock lock =new ReentrantLock();
public void ServiceMethod1() {
try {
lock.lock();
System.out.println("调用lock方法的次数:"+lock.getHoldCount());
ServiceMethod2();
}finally {
lock.unlock();
}
}
public void ServiceMethod2() {
try {
lock.lock();
System.out.println("调用lock的次数:"+lock.getHoldCount());
}finally {
lock.unlock();
}
}
}
package getHoldCount;
public class run {
public static void main(String[] args) {
Service service = new Service();
service.ServiceMethod1();
}
}
//控制台输出:
//调用lock方法的次数:1
//调用lock的次数:2
int getQueueLength(),返回正在等待获取此锁定的线程 估计数
package getQueueLength;
import java.util.concurrent.locks.ReentrantLock;
public class Service {
public ReentrantLock lock = new ReentrantLock();
public void serviceMethod() {
try {
lock.lock();
System.out.println("线程"+Thread.currentThread().getName()+"进入方法");
Thread.sleep(Integer.MAX_VALUE);
}catch(InterruptedException e){
e.printStackTrace();
}finally {
lock.unlock();
}
}
}
package getQueueLength;
//测试getQueueLength方法
public class run {
public static void main(String[] args) throws InterruptedException {
final Service service = new Service();
Runnable runnable = new Runnable() {
public void run() {
service.serviceMethod();
}
};
Thread[] threadArray = new Thread[10];
for(int i=0;i<10;i++) {
threadArray[i]=new Thread(runnable);
}
for(int i=0;i<10;i++) {
threadArray[i].start();
}
Thread.sleep(2000);
System.out.println("有"+service.lock.getQueueLength());
}
}
//控制台输出:
//线程Thread-0进入方法
//有9
int getWaitQueuelength(Condition condition),返回此锁定相关的给定条件Condition的线程估计数
package getWaitQueueLength;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
public class Service {
private ReentrantLock lock = new ReentrantLock();
Condition newCondition=lock.newCondition();
public void waitMethod() {
try {
lock.lock();
newCondition.await();
}catch(InterruptedException e) {
e.printStackTrace();
}finally {
lock.unlock();
}
}
public void nitifyMethod() {
try {
lock.lock();
System.out.println("有"+lock.getWaitQueueLength(newCondition)+"个线程在等待newCondition");
}finally {
lock.unlock();
}
}
}
package getWaitQueueLength;
//测试getWaitQueueLength(Condition condition)
public class run {
public static void main(String[] args) throws InterruptedException {
final Service service = new Service();
Runnable runnable = new Runnable() {
public void run() {
service.waitMethod();
}
};
Thread[] threadArray = new Thread[10];
for(int i=0;i<10;i++) {
threadArray[i]=new Thread(runnable);
}
for(int i=0;i<10;i++) {
threadArray[i].start();
}
Thread.sleep(2000);
service.nitifyMethod();
}
}
//控制台输出:
//有10个线程在等待newCondition
boolean hasQueueedThread(Thread thread):查询指定线程是否正在等待获取此锁定
boolean hasQueueThreads():查询是否有线程正在等待获取此锁定
package hasQueueedThread;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
public class Service {
public ReentrantLock lock = new ReentrantLock();
public Condition condition = lock.newCondition();
public void waitMethod() {
try {
lock.lock();
Thread.sleep(Integer.MAX_VALUE);
}catch(InterruptedException e) {
e.printStackTrace();
}finally {
lock.unlock();
}
}
}
package hasQueueedThread;
//测试hasQueueedThread和hasQueuedThread
public class run {
public static void main(String[] args) throws InterruptedException {
final Service service = new Service();
Runnable runnable = new Runnable() {
public void run() {
service.waitMethod();
}
};
Thread a = new Thread(runnable);
a.start();
Thread.sleep(500);
Thread b= new Thread(runnable);
b.start();
Thread.sleep(500);
System.out.println(service.lock.hasQueuedThread(a));
System.out.println(service.lock.hasQueuedThread(b));
System.out.println(service.lock.hasQueuedThreads());
}
}
//控制台输出:
//false
//true
//true
boolean has waiters(Condition condition):查询是否有线程正在等待与此锁定有关的condition条件。
package hasWaiters;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
public class Service {
public ReentrantLock lock = new ReentrantLock();
public Condition condition = lock.newCondition();
public void waitMethod() {
try {
lock.lock();
condition.await();
}catch(InterruptedException e) {
e.printStackTrace();
}finally {
lock.unlock();
}
}
public void notifyMethod() {
try {
lock.lock();
System.out.println("是否有线程正在等待condition"+lock.hasWaiters(condition)+"数量是多少?"+lock.getWaitQueueLength(condition));
condition.signal();;
}finally {
lock.unlock();
}
}
}
package hasWaiters;
//测试hasWaiters(Condition condition)方法
public class run {
public static void main(String[] args) throws InterruptedException {
final Service service = new Service();
Runnable runnable = new Runnable() {
public void run() {
service.waitMethod();
}
};
Thread[] threadArray = new Thread[10];
for(int i=0;i<10;i++) {
threadArray[i]=new Thread(runnable);
}
for(int i=0;i<10;i++) {
threadArray[i].start();
}
Thread.sleep(2000);
service.notifyMethod();
}
}
//控制台输出:
//是否有线程正在等待condition true 数量是多少?10
boolean isFair():判断是否是公平锁。
boolean isHeldByCurrentThread():查询当前线程是否保持此锁定。
boolean isLocked():查询此锁定是否由任意线程保持。
void lockInterruptibly():如果当前线程未中断则获取锁定,如果已经中断则出现异常。
package lockInterruptibly;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
public class Service {
ReentrantLock lock = new ReentrantLock();
Condition condition = lock.newCondition();
public void waitMethod(){
try{
lock.lockInterruptibly();;
System.out.println("lock begin"+Thread.currentThread().getName());
for(int i=0;i<Integer.MAX_VALUE/10;i++){
Math.random();
}
System.out.println("lock end"+Thread.currentThread().getName());
} catch (InterruptedException e) {
e.printStackTrace();
}finally{
if(lock.isHeldByCurrentThread())
lock.unlock();
}
}
}
package lockInterruptibly;
public class test {
public static void main(String[] args) throws InterruptedException {
final Service service = new Service();
Runnable runnable = new Runnable(){
public void run(){
service.waitMethod();
}
};
Thread a= new Thread(runnable);
a.setName("A");
a.start();
Thread.sleep(500);
Thread b = new Thread(runnable);
b.setName("B");
b.start();
b.interrupt();
System.out.println("main end");
}
}
boolean tryLock():仅在调用时,锁未被另一个线程保持的情况下,才获取该锁定。
package tryLock;
import java.util.concurrent.locks.ReentrantLock;
public class Service {
ReentrantLock lock = new ReentrantLock();
public void waitMethod(){
if(lock.tryLock()){
System.out.println("achieve lock");
}else{
System.out.println("not achieve lock");
}
}
}
package tryLock;
//验证tryLock
public class test {
public static void main(String[] args) {
final Service service = new Service();
Runnable runnable = new Runnable(){
public void run(){
service.waitMethod();
}
};
Thread a = new Thread(runnable);
a.setName("A");
a.start();
Thread b = new Thread(runnable);
b.setName("B");
b.start();
}
}
boolean tryLock(long timeout,TimeUnit unit):如果锁定在给定等待时间内没有被另一个线程保持,且当前线程未被中断,则获取该锁定。
package tryLock;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;
public class Service2 {
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();
}
}
}
package tryLock;
//验证tryLock(3, TimeUnit.SECONDS)
public class test2 {
public static void main(String[] args) {
final Service2 service = new Service2();
Runnable runnable = new Runnable(){
public void run(){
System.out.println(Thread.currentThread().getName()+"调用waitMethod的时间"+System.currentTimeMillis());
service.waitMethod();
}
};
Thread a = new Thread(runnable);
a.setName("A");
a.start();
Thread b = new Thread(runnable);
b.setName("B");
b.start();
}
}
awaitUninterruptibly():实现不可中断的条件等待。
package awaitUninterruptibly;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
public class Service {
private ReentrantLock lock = new ReentrantLock();
Condition condition = lock.newCondition();
public void waitMethod() {
try {
lock.lock();
System.out.println("进入等待");
condition.awaitUninterruptibly();
System.out.println("等待结束");
}finally {
lock.unlock();
}
}
}
package awaitUninterruptibly;
//测试awaitUninterruptibly()方法(实现不可中断的条件等待。 )
public class Run {
public static void main(String[] args) throws InterruptedException {
Service service = new Service();
Runnable runnable = new Runnable() {
public void run(){
service.waitMethod();
}
};
Thread thread = new Thread(runnable);
thread.start();
Thread.sleep(3000);
thread.interrupt();
}
}
awaitUntil(Date date),在等待指定时间后,自动唤醒。
package awaitUntil;
import java.util.Calendar;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
public class Service {
private ReentrantLock lock = new ReentrantLock();
private Condition condition = lock.newCondition();
public void waitMehod() {
try {
Calendar calendar= Calendar.getInstance();
calendar.add(Calendar.SECOND, 10);
lock.lock();
System.out.println("begin wait"+System.currentTimeMillis());
condition.awaitUntil(calendar.getTime());
System.out.println("end wait"+System.currentTimeMillis());
}catch(InterruptedException e){
e.printStackTrace();
}finally {
lock.lock();
}
}
public void notifyMethod() {
try {
Calendar calendar= Calendar.getInstance();
calendar.add(Calendar.SECOND, 10);
lock.lock();
System.out.println(" notify begin"+System.currentTimeMillis());
condition.signalAll();
System.out.println("notify end"+System.currentTimeMillis());
}finally {
lock.unlock();
}
}
}
package awaitUntil;
//测试awaitUntil(Date date),在等待指定时间后,自动唤醒。
public class Test {
public static void main(String[] args) {
Service service = new Service();
Runnable runnable = new Runnable() {
public void run() {
service.waitMehod();
}
};
Thread thread = new Thread(runnable);
thread.start();
}
}
package awaitUntil;
//测试awaitUntil(Date date),提前唤醒
public class Test2 {
public static void main(String[] args) throws InterruptedException {
Service service = new Service();
Runnable runnable = new Runnable() {
public void run() {
service.waitMehod();
}
};
Thread thread = new Thread(runnable);
thread.start();
Thread.sleep(2000);
Runnable runnable1 = new Runnable() {
public void run() {
service.notifyMethod();
}
};
Thread thread1 = new Thread(runnable1);
thread1.start();
}
}
下面用Condition实现顺序执行:
package ConditionSort;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
//用condition实现顺序打印
public class Run {
volatile private static int nextprintwho=1;
private static ReentrantLock lock = new ReentrantLock();
final private static Condition condition1 = lock.newCondition();
final private static Condition condition2 = lock.newCondition();
final private static Condition condition3 = lock.newCondition();
public static void main(String[] args) {
Thread ThreadA = new Thread(){
public void run() {
try {
lock.lock();
if(nextprintwho!=1) {
condition1.await();
}
for(int i=0;i<3;i++) {
System.out.println(Thread.currentThread().getName()+(i+1));
}
nextprintwho=2;
condition2.signalAll();
}catch(InterruptedException e) {
e.printStackTrace();
}finally {
lock.unlock();
}
}
};
Thread ThreadB = new Thread(){
public void run() {
try {
lock.lock();
if(nextprintwho!=2) {
condition1.await();
}
for(int i=0;i<3;i++) {
System.out.println(Thread.currentThread().getName()+(i+1));
}
nextprintwho=3;
condition3.signalAll();
}catch(InterruptedException e) {
e.printStackTrace();
}finally {
lock.unlock();
}
}
};
Thread ThreadC = new Thread(){
public void run() {
try {
lock.lock();
if(nextprintwho!=3) {
condition1.await();
}
for(int i=0;i<3;i++) {
System.out.println(Thread.currentThread().getName()+(i+1));
}
nextprintwho=1;
condition1.signalAll();
}catch(InterruptedException e) {
e.printStackTrace();
}finally {
lock.unlock();
}
}
};
ThreadA.setName("A");
ThreadA.start();
ThreadB.setName("B");
ThreadB.start();
ThreadC.setName("C");
ThreadC.start();
}
}
输出:
A1
A2
A3
B1
B2
B3
C1
C2
C3
上面是我看书的一些书中代码,仅供自己查阅,不喜勿碰