Java基础教程:多线程基础(4)——锁机制
显示锁
ReentrantLock
ReentrantLock是一种标准的互斥锁,每次只允许一个线程持有ReentrantLock。
使用ReentrantLock来保护对象状态:
Lock lock = new ReentrantLock();
lock.lock();
try {
//更新对象状态
//捕获异常,并在必要时恢复不变性条件
}finally {
lock.unlock();
}
如果没有finally来释放Lock,相当于启动了一个定时炸弹。
ReentrantReadWriteLock
互斥锁是一种保守的枷锁策略,它虽然避免了“写/写”冲突,“写/读”冲突,但是同样也避免了“读/读”操作。在许多情况下,数据结构上的操作都是“读操作”。若放宽加锁需求,允许多个执行读操作的线程同时访问数据结构,那么将提高程序的性能。
我们引入读写锁,它允许一个资源可以被多个读操作访问,或者被一个写操作访问,但两者不能同时进行。也就是说当写操作进行时,所有读操作都进入阻塞状态,只有写操作完成后,读操作才能获取到最新状态,避免了脏读的发生。
在如下的代码中,我们使用读写锁封装一个Map,来使其线程安全。
public class ReadWriteMap<K,V> {
private final Map<K,V> map;
private final ReadWriteLock lock = new ReentrantReadWriteLock();
private final Lock r = lock.readLock();
private final Lock w = lock.writeLock();
public ReadWriteMap(Map<K,V> map){
this.map = map;
}
public V put(K key,V value){
w.lock();
try {
return map.put(key,value);
} finally {
w.unlock();
}
}
public V get(Object key){
r.lock();
try {
return map.get(key);
} finally {
r.unlock();
}
}
}
使用Condition实现等待/通知
ReentrantLock同样可以实现相同的功能,但是需要借助Condition。
一个Lock对象里可以创建多个Condition(即对象监视器)实例,线程对象可以注册在指定的Condition中,从而可以有选择性地进行线程通知,在调度线程上更加灵活。
在Condition.await()方法调用之前调用Lock.lock()方法获得同步监视器。
MyService类
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();
} catch (Exception e) {
e.printStackTrace();
}finally {
lock.unlock();
}
}
}
ThreadA类
public class ThreadA extends Thread {
private MyService myService;
public ThreadA(MyService myService) {
this.myService = myService;
}
@Override
public void run() {
myService.await();
}
}
Main类
public class Main {
public static void main(String[] args) throws InterruptedException {
MyService myService = new MyService();
ThreadA threadA = new ThreadA(myService);
threadA.start();
Thread.sleep(3000);
myService.signal();
}
}
多个Condition实现通知部分线程
MyService类
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class MySevice {
private Lock lock = new ReentrantLock();
public Condition conditionA = lock.newCondition();
public Condition conditionB = lock.newCondition();
public void awaitA()
{
try {
lock.lock();
System.out.println("awaitA的start时间是"+System.currentTimeMillis());
conditionA.await();
System.out.println("awaitA的end时间是"+System.currentTimeMillis());
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
lock.unlock();
}
}
public void awaitB()
{
try {
lock.lock();
System.out.println("awaitB的start时间是"+System.currentTimeMillis());
conditionB.await();
System.out.println("awaitB的end时间是"+System.currentTimeMillis());
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
lock.unlock();
}
}
public void signalA()
{
try {
lock.lock();
System.out.println("signalA的等待时间是"+System.currentTimeMillis());
conditionA.signalAll();
} catch (Exception e) {
e.printStackTrace();
}finally {
lock.unlock();
}
}
public void signalB()
{
try {
lock.lock();
System.out.println("signalB的等待时间是"+System.currentTimeMillis());
conditionB.signalAll();
} catch (Exception e) {
e.printStackTrace();
}finally {
lock.unlock();
}
}
}
ThreadA类
public class ThreadA extends Thread {
private MySevice myService;
public ThreadA(MySevice myService) {
this.myService = myService;
}
@Override
public void run() {
myService.awaitA();
}
}
ThreadB类
public class ThreadB extends Thread {
private MySevice myService;
public ThreadB(MySevice myService) {
this.myService = myService;
}
@Override
public void run() {
myService.awaitB();
}
}
Main类
public class Main {
public static void main(String[] args) throws InterruptedException {
MySevice mySevice =new MySevice();
ThreadA threadA = new ThreadA(mySevice);
ThreadB threadB = new ThreadB(mySevice);
threadA.start();
threadB.start();
Thread.sleep(3000);
mySevice.signalA();
}
}