在Java多线程编程中,可以使用Synchronized来实现同步,可以synchronized类,方法或代码块,另外还可以使用ReentrantLock来实现同样的功能,而且功能更加强大。
1. 使用ReentrantLock实现同步
public class ReentrantLockTest {
private Lock lock = new ReentrantLock();
public void testLock(){
//获取锁
lock.lock();
try {
for (int i = 0; i < 10; i++){
System.out.println("Thread - " + Thread.currentThread().getName() + "===" + i);
Thread.sleep(100);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
// 解锁
lock.unlock();
}
}
public class ReentrantLockThread extends Thread {
private ReentrantLockTest reentrantLockTest;
public ReentrantLockThread(ReentrantLockTest reentrantLockTest){
super();
this.reentrantLockTest = reentrantLockTest;
}
@Override
public void run(){
reentrantLockTest.testLock();
}
}
public class TestLock {
public static void main(String[] args){
ReentrantLockTest reentrantLockTest = new ReentrantLockTest();
Thread threadA = new ReentrantLockThread(reentrantLockTest);
threadA.setName("A");
Thread threadB = new ReentrantLockThread(reentrantLockTest);
threadB.setName("B");
threadA.start();
threadB.start();
}
}
运行结果
加锁结果:
未加锁结果
从运行结果可以看出,使用了ReentrantLock的lock()方法和unlock()方法后,代码也达到了同步的效果。
接下来继续验证ReentrantLock实现同步是否持有对象锁
继续给添加新的方法X和Y,然后不同的线程调用不同的方法
public class ReentrantLockTest {
private Lock lock = new ReentrantLock();
public void testLockX(){
try {
//获取锁
lock.lock();
System.out.println("Thread --- " + Thread.currentThread().getName() + "====Method X");
Thread.sleep(1000);
System.out.println("Thread --- " + Thread.currentThread().getName() + "====Method X");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
// 解锁
lock.unlock();
}
}
public void testLockY(){
try {
//获取锁
lock.lock();
System.out.println("Thread --- " + Thread.currentThread().getName() + "====Method Y");
Thread.sleep(1000);
System.out.println("Thread --- " + Thread.currentThread().getName() + "====Method Y");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
// 解锁
lock.unlock();
}
}
}
调用X方法的线程、
public class ReentrantLockThreadX extends Thread {
private ReentrantLockTest reentrantLockTest;
public ReentrantLockThreadX(ReentrantLockTest reentrantLockTest){
super();
this.reentrantLockTest = reentrantLockTest;
}
@Override
public void run(){
reentrantLockTest.testLockX();
}
}
调用Y方法的线程
public class ReentrantLockThreadY extends Thread{
private ReentrantLockTest reentrantLockTest;
public ReentrantLockThreadY(ReentrantLockTest reentrantLockTest){
super();
this.reentrantLockTest = reentrantLockTest;
}
@Override
public void run(){
reentrantLockTest.testLockY();
}
}
测试类
public class TestLock {
public static void main(String[] args){
ReentrantLockTest reentrantLockTest = new ReentrantLockTest();
Thread threadA = new ReentrantLockThreadX(reentrantLockTest);
threadA.setName("A");
Thread threadB = new ReentrantLockThreadX(reentrantLockTest);
threadB.setName("B");
Thread threadC = new ReentrantLockThreadY(reentrantLockTest);
threadC.setName("C");
Thread threadD = new ReentrantLockThreadY(reentrantLockTest);
threadD.setName("D");
threadA.start();
threadB.start();
threadC.start();
threadD.start();
}
}
运行结果
从结果可以看出,ReentrantLock获取的是对象锁,即持有“对象监听器”,当某线程持有了对象锁后,其他的线程只能等待锁释放是争抢锁。线程执行的顺序是随机的。
2. 使用ReentrantLock和Condition实现等待/通知
Synchronized和wait()+notify()/notifyAll()可以实现等待/通知模式,那么ReentrantLock怎样实现呢?
ReentrantLock可以和Condition对象可以实现等待/通知的效果
下面通过代码看效果
public class LockAndCondition {
private Lock lock = new ReentrantLock();
private Condition condition = lock.newCondition();
public void mockWait(){
try {
// 加锁
lock.lock();
System.out.println("已经加锁");
condition.await();
System.out.println("已经await");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
// 释放锁
lock.unlock();
System.out.println("已经释放锁");
}
}
}
public class TestLockAndCondition {
public static void main(String[] args){
LockAndCondition lockAndCondition = new LockAndCondition();
Thread thread = new LockAndConditionThread(lockAndCondition);
thread.start();
}
}
public class LockAndConditionThread extends Thread {
private LockAndCondition lockAndCondition;
public LockAndConditionThread(LockAndCondition lockAndCondition){
super();
this.lockAndCondition = lockAndCondition;
}
@Override
public void run(){
lockAndCondition.mockWait();
}
}
运行结果
从运行的结果我们可以看出,代码执行到“已经加锁”的下面await方法处,线程处于等待状态了没有继续向下执行,接下来我们继续测试唤醒该线程
package thread.reentrantlock;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class LockAndCondition {
private Lock lock = new ReentrantLock();
private Condition condition = lock.newCondition();
public void mockWait(){
try {
// 加锁
lock.lock();
System.out.println("In mockWait 已经加锁");
condition.await();
System.out.println("已经await");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
// 释放锁
lock.unlock();
System.out.println("In mockWait 已经释放锁");
}
}
/**
* 新加唤醒方法
*/
public void mockNotify(){
try {
// 加锁
lock.lock();
System.out.println("In mockNotify 已经加锁");
condition.signal();
System.out.println("已经signal");
} finally {
// 释放锁
lock.unlock();
System.out.println("In mockNotify 已经释放锁");
}
}
}
测试类
public class TestLockAndCondition {
public static void main(String[] args) throws InterruptedException {
LockAndCondition lockAndCondition = new LockAndCondition();
Thread thread = new LockAndConditionThread(lockAndCondition);
thread.start();
lockAndCondition.mockNotify();
}
}
运行结果
通过condition的signle()方法唤醒等待线程。
同样的,condition也有方法可以唤醒全部等待线程,使用signalAll()方法,而且,condition有更加强大的用户,就是唤醒指定线程。下面继续测试。
3. 使用多个condition唤醒指定线程
首先,定义多个condition
package thread.reentrantlock;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class MoreCondition {
private Lock lock = new ReentrantLock();
private Condition conditionA = lock.newCondition();
private Condition conditionB = lock.newCondition();
public void testMoreConditionAwaitA(){
try{
lock.lock();
System.out.println("Thread --- " + Thread.currentThread().getName() + "Method --- A --- Lock");
conditionA.await();
System.out.println("Thread --- " + Thread.currentThread().getName() + "Method --- A --- 被唤醒");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void testMoreConditionSignalAllA(){
try {
lock.lock();
//唤醒所有使用conditionA等待的线程
conditionA.signalAll();
System.out.println("Thread --- " + Thread.currentThread().getName() + "Method --- A --- SignalAll");
} finally {
lock.unlock();
}
}
public void testMoreConditionAwaitB(){
try{
lock.lock();
System.out.println("Thread --- " + Thread.currentThread().getName() + "Method --- B --- Lock");
conditionB.await();
System.out.println("Thread --- " + Thread.currentThread().getName() + "Method --- B --- 被唤醒");
} catch (InterruptedException e){
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void testMoreConditionSignalAllB(){
try {
lock.lock();
conditionB.signalAll();
System.out.println("Thread --- " + Thread.currentThread().getName() + "Method --- B --- SignalAll");
} finally {
lock.unlock();
}
}
}
调用A方法的线程
package thread.reentrantlock;
public class MoreConditionThreadA extends Thread{
private MoreCondition moreCondition;
public MoreConditionThreadA(MoreCondition moreCondition){
super();
this.moreCondition = moreCondition;
}
@Override
public void run(){
moreCondition.testMoreConditionAwaitA();
}
}
调用B方法的线程
package thread.reentrantlock;
public class MoreConditionThreadB extends Thread {
private MoreCondition moreCondition;
public MoreConditionThreadB(MoreCondition moreCondition){
super();
this.moreCondition = moreCondition;
}
@Override
public void run(){
this.moreCondition.testMoreConditionAwaitB();
}
}
测试类
package thread.reentrantlock;
public class MoreConditionTest {
public static void main(String[] args) throws InterruptedException {
MoreCondition moreCondition = new MoreCondition();
Thread threadA1 = new MoreConditionThreadA(moreCondition);
Thread threadA2 = new MoreConditionThreadA(moreCondition);
Thread threadA3 = new MoreConditionThreadA(moreCondition);
Thread threadB1 = new MoreConditionThreadB(moreCondition);
Thread threadB2 = new MoreConditionThreadB(moreCondition);
Thread threadB3 = new MoreConditionThreadB(moreCondition);
threadA1.setName("A1");
threadA2.setName("A2");
threadA3.setName("A3");
threadB1.setName("B1");
threadB2.setName("B2");
threadB3.setName("B3");
threadA1.start();
threadA2.start();
threadA3.start();
threadB1.start();
threadB2.start();
threadB3.start();
Thread.sleep(2000);
// 唤醒A线程
moreCondition.testMoreConditionSignalAllA();
// 唤醒B线程
// moreCondition.testMoreConditionSignalAllB();
}
}
运行结果
所有调用A方法的线程全部别唤醒,调用B方法的线程依然在等待。
通过这个例子可以看出,使用condition+signal/signalAll方法可以唤醒指定的线程