Java多线程编程核心技术--Lock的使用(一)

使用ReentrantLock类

在Java多线程中,可以使用synchronized关键字来实现线程之间的同步互斥,但在JDK1.5中新增加了ReentrantLock类也能达到同样的效果,并且在扩展功能上也更加强大,比如具有嗅探锁定、多路分支通知等功能,而且在使用上也比synchronized更加灵活。

使用ReentrantLock实现同步:测试1
public class Service {
	private Lock lock = new ReentrantLock();
	public void testMethod() {
		lock.lock();
		for (int i = 0; i < 5; i++) {
			System.out.println(Thread.currentThread().getName() + " i=" + (i + 1));
		}
		lock.unlock();
	}
}

public class MyThread extends Thread {
	private Service service;
	public MyThread(Service service) {
		super();
		this.service = service;
	}
	
	@Override
	public void run() {
		service.testMethod();
	}
}

public class Main {
	public static void main(String[] args) {
		Service service = new Service();
		MyThread t1 = new MyThread(service);
		MyThread t2 = new MyThread(service);
		MyThread t3 = new MyThread(service);
		MyThread t4 = new MyThread(service);
		MyThread t5 = new MyThread(service);
		t1.start();
		t2.start();
		t3.start();
		t4.start();
		t5.start();
	}
}

控制台打印结果如下:

Thread-3 i=1
Thread-3 i=2
Thread-3 i=3
Thread-3 i=4
Thread-3 i=5
Thread-0 i=1
Thread-0 i=2
Thread-0 i=3
Thread-0 i=4
Thread-0 i=5
Thread-1 i=1
Thread-1 i=2
Thread-1 i=3
Thread-1 i=4
Thread-1 i=5
Thread-2 i=1
Thread-2 i=2
Thread-2 i=3
Thread-2 i=4
Thread-2 i=5
Thread-4 i=1
Thread-4 i=2
Thread-4 i=3
Thread-4 i=4
Thread-4 i=5

可见,当前线程打印完毕之后将锁进行释放,其他线程才可以继续打印。线程打印数据是分组打印,因为当前线程已经持有锁,但线程之间打印的顺序是随机的。


使用ReentrantLock实现同步:测试2
public class Service {
	private Lock lock = new ReentrantLock();
	public void methodA() {
		try {
			lock.lock();
			System.out.println("methodA begin " + Thread.currentThread().getName() + " time=" + System.currentTimeMillis());
			Thread.sleep(5000);
			System.out.println("methodA end " + Thread.currentThread().getName() + " time=" + System.currentTimeMillis());
		} catch (Exception e) {
			e.printStackTrace();
		}finally {
			lock.unlock();
		}
	}
	public void methodB() {
		try {
			lock.lock();
			System.out.println("methodB begin " + Thread.currentThread().getName() + " time=" + System.currentTimeMillis());
			Thread.sleep(5000);
			System.out.println("methodB end " + Thread.currentThread().getName() + " time=" + System.currentTimeMillis());
		} catch (Exception e) {
			e.printStackTrace();
		}finally {
			lock.unlock();
		}
	}
}

public class ThreadA extends Thread {
	private Service service;
	public ThreadA(Service service) {
		super();
		this.service = service;
	}
	@Override
	public void run() {
		service.methodA();
	}
}

public class ThreadAA extends Thread {
	private Service service;
	public ThreadAA(Service service) {
		super();
		this.service = service;
	}
	@Override
	public void run() {
		service.methodA();
	}
}

public class ThreadB extends Thread {
	private Service service;
	public ThreadB(Service service) {
		super();
		this.service = service;
	}
	@Override
	public void run() {
		service.methodA();
	}
}

public class ThreadBB extends Thread {
	private Service service;
	public ThreadBB(Service service) {
		super();
		this.service = service;
	}
	@Override
	public void run() {
		service.methodA();
	}
}

public class Main {
	public static void main(String[] args) {
		Service service = new Service();
		ThreadA a = new ThreadA(service);
		a.setName("A");
		a.start();
		ThreadAA aa = new ThreadAA(service);
		aa.setName("AA");
		aa.start();
		ThreadB b = new ThreadB(service);
		b.setName("B");
		b.start();
		ThreadBB bb = new ThreadBB(service);
		bb.setName("BB");
		bb.start();
	}
}

控制台打印结果如下:

methodA begin A time=1467162810185
methodA end A time=1467162815186
methodA begin AA time=1467162815186
methodA end AA time=1467162820187
methodA begin B time=1467162820187
methodA end B time=1467162825187
methodA begin BB time=1467162825187
methodA end BB time=1467162830188

可见,调用lock.lock()代码的线程就持有了“对象监视器”,其他线程只能等待锁被释放时再次争抢。效果和使用synchronized关键字一样,线程之间还是顺序执行的。


使用condition实现等待/通知:错误用法与解决

关键字synchronized与wait()和notify()/notifyAll()方法结合可以实现等待/通知模式,类ReentrantLock也可以实现同样的功能,但需要借助Condition对象。Condition类是在JDK5中出现的技术,使用它有更好的灵活性,比如可以实现多路通知功能,也就是在一个Lock对象里面可以创建多个Condition(即对象监视器)实例,线程对象可以注册在知道的Condition中,从而可以有选择性地进行线程通知,在调度线程上更加灵活。

在使用notify()/notifyAll()方法进行通知时,被通知的线程是由JVM随机选择的。但使用ReentrantLock结合Condition类是可以实现前面介绍过的“选择性通知”,这个功能是非常重要的,而且在Condition类中是默认提供的。

而synchronized就相当于整个Lock对象中只有一个单一的Condition对象,所有的线程都注册在它一个对象的身上。线程开始notifyAll()时,需要通知所有的WAITING线程,没有选择权,会出现相当大的效率问题。

看以下代码:

public class Service {
	private Lock lock = new ReentrantLock();
	private Condition condition = lock.newCondition();
	public void await() {
		try {
			condition.await();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

public class ThreadA extends Thread {
	private Service service;
	public ThreadA(Service service) {
		super();
		this.service = service;
	}
	@Override
	public void run() {
		service.await();
	}
}

public class Main {
	public static void main(String[] args) {
		Service service = new Service();
		ThreadA a = new ThreadA(service);
		a.start();
	}
}

运行以上代码,控制台抛出异常:

java.lang.IllegalMonitorStateException
	at java.util.concurrent.locks.ReentrantLock$Sync.tryRelease(Unknown Source)
	at java.util.concurrent.locks.AbstractQueuedSynchronizer.release(Unknown Source)
	at java.util.concurrent.locks.AbstractQueuedSynchronizer.fullyRelease(Unknown Source)
	at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(Unknown Source)
	at com.umgsai.thread.thread46.Service.await(Service.java:14)
	at com.umgsai.thread.thread46.ThreadA.run(ThreadA.java:11)

异常信息是监视器出错,解决的办法是必须在condition.await()方法调用之前调用lock.lock()代码获得同步监视器。将Service类做如下修改:

public class Service {
	private Lock lock = new ReentrantLock();
	private Condition condition = lock.newCondition();
	public void await() {
		try {
			lock.lock();
			System.out.println("A");
			condition.await();
			System.out.println("B");
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			lock.unlock();
			System.out.println("锁被释放了");
		}
	}
}

重新运行程序,控制台打印结果如下:

A

程序不结束,原因是调用了Condition对象的await()方法,使当前执行任务的线程进入了等待WAITING状态。


正确使用Condition实现等待/通知
public class Service {
	private Lock lock = new ReentrantLock();
	public Condition condition = lock.newCondition();
	public void await() {
		try {
			lock.lock();
			System.out.println(Thread.currentThread().getName() + " await时间为" + System.currentTimeMillis());
			condition.await();
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			lock.unlock();
		}
	}
	public void signal() {
		try {
			lock.lock();
			System.out.println(Thread.currentThread().getName() + " signal时间为" + System.currentTimeMillis());
			condition.signal();
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			lock.unlock();
		}
	}
}

public class ThreadA extends Thread {
	private Service service;
	public ThreadA(Service service) {
		super();
		this.service = service;
	}
	@Override
	public void run() {
		service.await();
	};
}

public class Main {
	public static void main(String[] args) throws InterruptedException {
		Service service = new Service();
		ThreadA a = new ThreadA(service);
		a.start();
		Thread.sleep(2000);
		service.signal();
	}
}

控制台打印结果如下:

Thread-0 await时间为1467293210228
main signal时间为1467293212227

成功实现等待/通知模式。

Object类中的wait()等待相当于Condition类中的await()方法。

Object类中的wait(long timeout)方法相当于Condition类中的await(long time, TimeUnit unit)方法。

Object类中的notify()方法相当于Condition类中的signal()方法。

Object类中的notifyAll()方法相当于Condition类中的signalAll()方法。


使用多个Condition实现通知部分线程:错误用法
public class Service {
	private Lock lock = new ReentrantLock();
	public Condition condition = lock.newCondition();
	public void awaitA() {
		try {
			lock.lock();
			Thread.sleep(500);
			System.out.println(Thread.currentThread().getName() + " begin awaitA时间为" + System.currentTimeMillis());
			condition.await();//释放锁
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			try {
				Thread.sleep(500);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			System.out.println(Thread.currentThread().getName() + " end awaitA时间为" + System.currentTimeMillis());
			lock.unlock();
		}
	}
	
	public void awaitB() {
		try {
			lock.lock();
			Thread.sleep(500);
			System.out.println(Thread.currentThread().getName() + " begin awaitB时间为" + System.currentTimeMillis());
			condition.await();//释放锁
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			try {
				Thread.sleep(500);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			System.out.println(Thread.currentThread().getName() + " end awaitB时间为" + System.currentTimeMillis());
			lock.unlock();
		}
	}
	
	public void signalAll() {
		try {
			lock.lock();
			System.out.println(Thread.currentThread().getName() + " end signalAll时间为" + System.currentTimeMillis());
			condition.signalAll();
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			lock.unlock();
		}
	}
}

public class ThreadA extends Thread {
	private Service service;
	public ThreadA(Service service) {
		super();
		this.service = service;
	}
	
	@Override
	public void run() {
		service.awaitA();
	}
}

public class ThreadB extends Thread {
	private Service service;
	public ThreadB(Service service) {
		super();
		this.service = service;
	}
	
	@Override
	public void run() {
		service.awaitB();
	}
}

public class Main {
	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时间为1467293693906
B begin awaitB时间为1467293694407
main end signalAll时间为1467293696406
A end awaitA时间为1467293696906
B end awaitB时间为1467293697407

程序运行后A和B都被唤醒了。


使用多个Condition实现通知部分线程:正确用法

将上面例子中Service作如下修改:

public class Service {
	private Lock lock = new ReentrantLock();
	public Condition conditionA = lock.newCondition();
	public Condition conditionB = lock.newCondition();
	public void awaitA() {
		try {
			lock.lock();
			Thread.sleep(500);
			System.out.println(Thread.currentThread().getName() + " begin awaitA时间为" + System.currentTimeMillis());
			conditionA.await();//释放锁
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			try {
				Thread.sleep(500);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			System.out.println(Thread.currentThread().getName() + " end awaitA时间为" + System.currentTimeMillis());
			lock.unlock();
		}
	}
	
	public void awaitB() {
		try {
			lock.lock();
			Thread.sleep(500);
			System.out.println(Thread.currentThread().getName() + " begin awaitB时间为" + System.currentTimeMillis());
			conditionB.await();//释放锁
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			try {
				Thread.sleep(500);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			System.out.println(Thread.currentThread().getName() + " end awaitB时间为" + System.currentTimeMillis());
			lock.unlock();
		}
	}
	
	public void signalAll_A() {
		try {
			lock.lock();
			System.out.println(Thread.currentThread().getName() + " end signalAll_A时间为" + System.currentTimeMillis());
			conditionA.signalAll();
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			lock.unlock();
		}
	}
	
	public void signalAll_B() {
		try {
			lock.lock();
			System.out.println(Thread.currentThread().getName() + " end signalAll_B时间为" + System.currentTimeMillis());
			conditionB.signalAll();
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			lock.unlock();
		}
	}
}

public class Main {
	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();//通知在conditionA上等待的线程
		Thread.sleep(2000);
		service.signalAll_B();//通知在conditionB上等待的线程
	}
}

控制台打印结果如下:

A begin awaitA时间为1467293966937
B begin awaitB时间为1467293967437
main end signalAll_A时间为1467293969438
A end awaitA时间为1467293969938
main end signalAll_B时间为1467293971438
B end awaitB时间为1467293971939

实现生产者/消费者模式:一对一交替打印
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) {
				condition.await();
			}
			System.out.println("★");
			hasValue = true;
			condition.signal();//通知在condition上等待的线程
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			lock.unlock();
		}
	}
	public void get() {
		try {
			lock.lock();
			while (!hasValue) {
				condition.await();
			}
			System.out.println("☆");
			hasValue = false;
			condition.signal();//通知在condition上等待的线程
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			lock.unlock();
		}
	}
}

public class ThreadA extends Thread {
	private Service service;
	public ThreadA(Service service) {
		super();
		this.service = service;
	}
	@Override
	public void run() {
		for (int i = 0; i < Integer.MAX_VALUE; i++) {
			service.set();	
		}
	}
}

public class ThreadB extends Thread {
	private Service service;
	public ThreadB(Service service) {
		super();
		this.service = service;
	}
	@Override
	public void run() {
		for (int i = 0; i < Integer.MAX_VALUE; i++) {
			service.get();	
		}
	}
}

public class Main {
	public static void main(String[] args) {
		Service service = new Service();
		ThreadA a = new ThreadA(service);
		ThreadB b = new ThreadB(service);
		a.start();
		b.start();
	}
}

控制台打印结果如下:

......
★
☆
★
☆
★
☆
......

将以上代码做如下修改:

public class Main {
	public static void main(String[] args) {
		Service service = new Service();
		ThreadA[] a = new ThreadA[10];
		ThreadB[] b = new ThreadB[10];
		for (int i = 0; i < 10; i++) {
			a[i] = new ThreadA(service);
			a[i].start();
			b[i] = new ThreadB(service);
			b[i].start();
		}
	}
}

此时控制台打印结果如下:

......
有可能★连续
☆
★
有可能★连续
☆
有可能☆连续
★
有可能★连续
☆
有可能☆连续
★
有可能★连续
☆
有可能☆连续
有可能☆连续
有可能☆连续
★
有可能★连续
有可能★连续
....

程序运行一段时间后出现假死现象。前面有程序和这个程序的原理一样。解决方法是修改Service里面的condition.signal()为condition.signalAll()。


公平锁与非公平锁
/**
* 公平锁
*/
public class Service {
	private ReentrantLock lock;
	public Service(boolean isFair) {
		super();
		lock = new ReentrantLock(isFair);
	}
	
	public void serviceMethod() {
		try {
			lock.lock();
			System.out.println(Thread.currentThread().getName() + "获得锁定");
		} finally{
			lock.unlock();
		}
	}
}

public class RunFair {
	public static void main(String[] args) {
		final Service service = new Service(true);
		Runnable runnable = new Runnable() {
			
			@Override
			public void run() {
				System.out.println(Thread.currentThread().getName() + "运行了");
				service.serviceMethod();
			}
		};
		Thread[] threads = new Thread[10];
		for (int i = 0; i < threads.length; i++) {
			threads[i] = new Thread(runnable);
		}
		for (int i = 0; i < threads.length; i++) {
			threads[i].start();;
		}
	}
}

控制台打印结果如下:

Thread-0运行了
Thread-1运行了
Thread-0获得锁定
Thread-1获得锁定
Thread-2运行了
Thread-2获得锁定
Thread-3运行了
Thread-4运行了
Thread-3获得锁定
Thread-4获得锁定
Thread-5运行了
Thread-5获得锁定
Thread-6运行了
Thread-8运行了
Thread-6获得锁定
Thread-7运行了
Thread-8获得锁定
Thread-7获得锁定
Thread-9运行了
Thread-9获得锁定

打印结果基本呈有序的状态,这就是公平锁的特点。


getHoldCount方法
/**
 * getHoldCount()方法查询当前线程保持此锁定的个数
 * 也就是调用()方法的次数
 */
public class Service {
	private ReentrantLock lock = new ReentrantLock();
	public void serviceMethod1() {
		try {
			lock.lock();
			System.out.println("serviceMethod1 getHoldCount=" + lock.getHoldCount());//1
			serviceMethod2();
		} finally{
			lock.unlock();
		}
	}
	public void serviceMethod2() {
		try {
			lock.lock();
			System.out.println("serviceMethod2 getHoldCount=" + lock.getHoldCount());//2
		} finally{
			lock.unlock();
		}
	}
	public static void main(String[] args) {
		Service service = new Service();
		service.serviceMethod1();
	}
}

getQueueLength()方法
//getQueueLength返回正在等待此锁定的线程估计数
public class MyService {
	public ReentrantLock lock = new ReentrantLock();
	public void serviceMethod1() {
		try {
			lock.lock();
			System.out.println(Thread.currentThread().getName() + "进入方法");
			Thread.sleep(Integer.MAX_VALUE);
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			lock.unlock();
		}
	}
	public static void main(String[] args) throws InterruptedException {
		final MyService myService = new MyService();
		Runnable runnable = new Runnable() {
			
			@Override
			public void run() {
				myService.serviceMethod1();
			}
		};
		Thread[] threads = new Thread[10];
		for (int i = 0; i < threads.length; i++) {
			threads[i] = new Thread(runnable);
		}
		for (int i = 0; i < threads.length; i++) {
			threads[i].start();
		}
		Thread.sleep(2000);
		System.out.println(myService.lock.getQueueLength() + "在等待获取锁");
	}
}

控制台打印结果如下:

Thread-0进入方法
9在等待获取锁
getWaitQueueLength()方法
public class MyService1 {
	private ReentrantLock lock = new ReentrantLock();
	private Condition condition = lock.newCondition();
	public void waitMethod() {
		try {
			lock.lock();
			condition.await();
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			lock.unlock();
		}
	}
	
	public void notifyMethod() {
		try {
			lock.lock();
			System.out.println(lock.getWaitQueueLength(condition) + "个线程在等待condition");//10
			condition.signal();
			System.out.println(lock.getWaitQueueLength(condition) + "个线程在等待condition");//9
			condition.signal();
			System.out.println(lock.getWaitQueueLength(condition) + "个线程在等待condition");//8
			condition.signalAll();
			System.out.println(lock.getWaitQueueLength(condition) + "个线程在等待condition");//7
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			lock.unlock();
		}
	}
	
	public static void main(String[] args) throws InterruptedException {
		final MyService1 service = new MyService1();
		Runnable runnable = new Runnable() {
			
			@Override
			public void run() {
				service.waitMethod();
			}
		};
		Thread[] threads = new Thread[10];
		for (int i = 0; i < threads.length; i++) {
			threads[i] = new Thread(runnable);
		}
		for (int i = 0; i < threads.length; i++) {
			threads[i].start();
		}
		Thread.sleep(2000);
		service.notifyMethod();
	}
}

控制台打印结果如下:

10个线程在等待condition
9个线程在等待condition
8个线程在等待condition
0个线程在等待condition

getWaitQueueLength(condition)方法返回等待与此锁定相关的给定条件condition的线程估计数。

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 (Exception e) {
			e.printStackTrace();
		} finally {
			lock.unlock();
		}
	}
	public static void main(String[] args) throws InterruptedException {
		final Service service = new Service();
		Runnable runnable = new Runnable() {
			
			@Override
			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));//false 查询指定的线程是否正在等待此锁定
		System.out.println(service.lock.hasQueuedThread(b));//true
		System.out.println(service.lock.hasQueuedThreads());//true 查询是否有线程正在等待此锁
	}
}

public class MyService {
	private ReentrantLock lock = new ReentrantLock();
	private Condition condition = lock.newCondition();
	public void waitMethod() {
		try {
			lock.lock();
			condition.await();//进入WAITTING状态,释放锁
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			lock.unlock();
		}
	}
	public void notifyMethod() {
		try {
			lock.lock();
			System.out.println("是否有线程正在等待condition:" + lock.hasWaiters(condition));
			System.out.println("正在等待condition的线程数是:" + lock.getWaitQueueLength(condition));//10个线程等待
			condition.signal();//唤醒一个线程
			System.out.println("正在等待condition的线程数是:" + lock.getWaitQueueLength(condition));//9个线程等待
			condition.signalAll();//剩下的个全部唤醒
			System.out.println("正在等待condition的线程数是:" + lock.getWaitQueueLength(condition));//0个线程等待
		} catch (Exception e) {
			e.printStackTrace();
		}finally {
			lock.unlock();
		}
	}
	public static void main(String[] args) throws InterruptedException {
		final MyService service = new MyService();
		Runnable runnable = new Runnable() {
			
			@Override
			public void run() {
				service.waitMethod();
			}
		};
		Thread[] threads = new Thread[10];
		for (int i = 0; i < threads.length; i++) {
			threads[i] = new Thread(runnable);
			threads[i].start();
		}
		Thread.sleep(2000);
		service.notifyMethod();
	}
}

使用Condition实现顺序执行
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();//唤醒B组
				} catch (Exception 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();//唤醒C组
				} catch (Exception 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) + " " + System.currentTimeMillis());
					}
					nextPrintWho = 1;
					conditionA.signalAll();//唤醒A组
				} catch (Exception e) {
					e.printStackTrace();
				} finally {
					lock.unlock();
				}
			}
		};
		Thread[] aThreads = new Thread[5];
		Thread[] bThreads = new Thread[5];
		Thread[] cThreads = new Thread[5];
		for (int i = 0; i < cThreads.length; i++) {
			aThreads[i] = new Thread(threadA);
			bThreads[i] = new Thread(threadB);
			cThreads[i] = new Thread(threadC);
			aThreads[i].start();
			bThreads[i].start();
			cThreads[i].start();
		}
	}
}

控制台输入结果如下:

ThreadA 1
ThreadA 2
ThreadA 3
ThreadB 1
ThreadB 2
ThreadB 3
ThreadC 1 1468067302315
ThreadC 2 1468067302315
ThreadC 3 1468067302315
ThreadA 1
ThreadA 2
ThreadA 3
ThreadB 1
ThreadB 2
ThreadB 3
ThreadC 1 1468067302315
ThreadC 2 1468067302315
ThreadC 3 1468067302315
ThreadA 1
ThreadA 2
ThreadA 3
ThreadB 1
ThreadB 2
ThreadB 3
ThreadC 1 1468067302315
ThreadC 2 1468067302315
ThreadC 3 1468067302315
ThreadA 1
ThreadA 2
ThreadA 3
ThreadB 1
ThreadB 2
ThreadB 3
ThreadC 1 1468067302316
ThreadC 2 1468067302316
ThreadC 3 1468067302316
ThreadA 1
ThreadA 2
ThreadA 3
ThreadB 1
ThreadB 2
ThreadB 3
ThreadC 1 1468067302316
ThreadC 2 1468067302316
ThreadC 3 1468067302316

使用ReentrantReadWriteLock类

ReentrantLock具有完全互斥排他的效果,同一时刻只有一个线程在执行ReentrantLock.lock()方法后面的任务。使用读写锁ReentrantReadWriteLock可以区分读写操作。读操作不互斥时,可以使用读锁让多个线程同时读。写锁互斥时,同一时刻值允许一个线程写。

读读共享
public class Service {
	private ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
	public void read() {
		try {
			try {
				lock.readLock().lock();
				System.out.println(Thread.currentThread().getName() + "获得读锁" + System.currentTimeMillis());
				Thread.sleep(1000);
			} catch (Exception e) {
				e.printStackTrace();
			} finally {
				lock.readLock().unlock();
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

public class ThreadA extends Thread {
	private Service service;
	public ThreadA(Service service) {
		super();
		this.service = service;
	}
	@Override
	public void run() {
		service.read();
	}
}

public class ThreadB extends Thread {
	private Service service;
	public ThreadB(Service service) {
		super();
		this.service = service;
	}
	@Override
	public void run() {
		service.read();
	}
}

public class Run {
	public static void main(String[] args) {
		Service service = new Service();
		ThreadA a = new ThreadA(service);
		ThreadB b = new ThreadB(service);
		a.start();
		b.start();
	}
}

程序运行结果如下:

Thread-0获得读锁1468068667767
Thread-1获得读锁1468068667767

可以看出,两个线程同时进入lock()方法后面的代码。


写写互斥

将以上代码的Service类做如下修改:

public class Service {
	private ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
	public void write() {
		try {
			try {
				lock.writeLock().lock();
				System.out.println(Thread.currentThread().getName() + "获得写锁" + System.currentTimeMillis());
				Thread.sleep(1000);
			} catch (Exception e) {
				e.printStackTrace();
			} finally {
				lock.writeLock().unlock();
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

程序运行结果如下:

Thread-0获得写锁1468069134317
Thread-1获得写锁1468069135322

可见此时两个线程以排队方式执行lock()后面的代码。


读写互斥
public class Service {
	private ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
	public void read() {
		try {
			try {
				lock.readLock().lock();
				System.out.println(Thread.currentThread().getName() + "获得读锁" + System.currentTimeMillis());
				Thread.sleep(1000);
			} catch (Exception e) {
				e.printStackTrace();
			} finally {
				lock.readLock().unlock();
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	public void write() {
		try {
			try {
				lock.writeLock().lock();
				System.out.println(Thread.currentThread().getName() + "获得写锁" + System.currentTimeMillis());
				Thread.sleep(1000);
			} catch (Exception e) {
				e.printStackTrace();
			} finally {
				lock.writeLock().unlock();
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

public class ThreadA extends Thread {
	private Service service;
	public ThreadA(Service service) {
		super();
		this.service = service;
	}
	@Override
	public void run() {
		service.read();
	}
}

public class ThreadB extends Thread {
	private Service service;
	public ThreadB(Service service) {
		super();
		this.service = service;
	}
	@Override
	public void run() {
		service.write();
	}
}

public class Run {
	public static void main(String[] args) {
		Service service = new Service();
		ThreadA a = new ThreadA(service);
		ThreadB b = new ThreadB(service);
		a.start();
		b.start();
	}
}

程序运行结果如下:

Thread-0获得读锁1468069343031
Thread-1获得写锁1468069344033

两个线程以排队方式执行lock()后面的代码,可见读写操作是互斥的。

将以上代码做如下修改:

public class Run {
	public static void main(String[] args) {
		Service service = new Service();
		ThreadA a = new ThreadA(service);
		ThreadB b = new ThreadB(service);
		b.start();//先启动写线程
		a.start();//后启动读线程
	}
}

此时程序运行结果如下:

Thread-1获得写锁1468069625161
Thread-0获得读锁1468069626166

可见,两个线程也是以排队方式执行lock()后面的代码的。

综合ReentrantReadWriteLock类的几个实体程序,“读写” “写写” “写读”都是互斥的。“读读”是异步的,非互斥的。

原文地址:https://www.cnblogs.com/umgsai/p/5600103.html