JAVA读写锁:前面讲到了可重入锁,虽然使用方面会比synchronized性能高,但是还是没有达到我们理想得要求,我们实际项目中,比如同时访问写的时候需要互斥,或者写的时候也读也需要等待,但是多个线程同时读得时候,不需要等待,这时候就要用到读写锁了ReadWriteLock,我们先看下ReadWriteLock接口:
public interface ReadWriteLock {
/**
* 返回一个读锁
*/
Lock readLock();
/**
* 返回一个写锁
*/
Lock writeLock();
}
我们看下读写锁得用法,首先同时进行写,我们看下账户类:
package com.ck.thread;
import java.math.BigDecimal;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
public class Account {
private ReadWriteLock lock = new ReentrantReadWriteLock();
private BigDecimal balnace = new BigDecimal("100");
//充值
public void add(BigDecimal amount ) throws InterruptedException {
//写锁
lock.writeLock().lock();
System.out.println(" 开始充值,线程名: " + Thread.currentThread().getName());
try {
Thread.sleep(2000);
balnace = balnace.add(amount);
System.out.println(" 结束充值,线程名: " + Thread.currentThread().getName());
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
lock.writeLock().unlock();
}
}
//查询余额
public void getBalace() {
//读锁
lock.readLock().lock();
try {
System.out.println("开始查询线程名: " + Thread.currentThread().getName() );
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("查询结果线程名: " + Thread.currentThread().getName() +", 余额:" + balnace);
}finally {
lock.readLock().unlock();
}
}
}
然后充值类:
package com.ck.thread;
import java.math.BigDecimal;
public class CzThread extends Thread{
private Account account;
public CzThread(Account account) {
this.account = account;
}
@Override
public void run() {
try {
account.add(new BigDecimal("100"));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public Account getAccount() {
return account;
}
public void setAccount(Account account) {
this.account = account;
}
}
获取余额线程:
package com.ck.thread;
public class GetBalaceThread extends Thread{
private Account account;
public GetBalaceThread(Account account) {
super();
this.account = account;
}
@Override
public void run() {
account.getBalace();
}
public Account getAccount() {
return account;
}
public void setAccount(Account account) {
this.account = account;
}
}
主线程方法:
package com.ck.thread;
public class MainThread {
public static void main(String[] args) throws InterruptedException {
Account account = new Account();
CzThread cz1 = new CzThread(account);
cz1.setName("thread1");
cz1.start();
CzThread cz2 = new CzThread(account);
cz2.setName("thread2");
cz2.start();
}
}
执行结果:
开始充值,线程名: thread2
结束充值,线程名: thread2
开始充值,线程名: thread1
结束充值,线程名: thread1
我们通过结果可以看到,因为充值得线程都调用了Account的add方法,该方法上加了写的锁,所以都需要等待,我们看到线程2执行完了才执行线程1,说明写锁是互斥的,我们继续看些读写的情况,修改主线程如下:
public class MainThread {
public static void main(String[] args) throws InterruptedException {
Account account = new Account();
CzThread cz1 = new CzThread(account);
cz1.setName("thread1");
cz1.start();
GetBalaceThread cz2 = new GetBalaceThread(account);
cz2.setName("thread2");
cz2.start();
}
}
执行结果:
开始充值,线程名: thread1
结束充值,线程名: thread1
开始查询线程名: thread2
查询结果线程名: thread2, 余额:200
通过结果我们可以看到,先执行充值的情况下,获取余额线程需要等待,说明读写锁也是相互互斥的,下面我们继续看下读读锁,修改主线程如下:
public class MainThread {
public static void main(String[] args) throws InterruptedException {
Account account = new Account();
GetBalaceThread cz1 = new GetBalaceThread(account);
cz1.setName("thread1");
cz1.start();
GetBalaceThread cz2 = new GetBalaceThread(account);
cz2.setName("thread2");
cz2.start();
}
}
执行结果:
开始查询线程名: thread1
开始查询线程名: thread2
查询结果线程名: thread1, 余额:100
查询结果线程名: thread2, 余额:100
通过结果可以看到,读锁直接不存在竞争锁的关系,可以同时访问一个方法。
总结:读写锁可以提供程序并发的性能,而且比较灵活,具体原理我们后续会继续介绍。