在程序运行过程中,如果有多个线程同时需要对共享资源进行访问,就需要在共享资源身上做同步操作以防止多线程读写导致的数据不一致。java可以通过synchronized关键字或者显式锁Lock对资源进行加锁来实现共享资源的同步。线程对资源的访问有两种分别是读和写,读是不会导致资源本身的更改的,所以读锁不是一种排它锁。如果有线程同时在读和写、或者多个线程同时写,这多个写操作会导致数据的不一致性,所以只要有写操作存在就需要加排他锁,以防止数据的不一致。
读写锁分离设计会涉及几个接口,分别是接口的定义、读接口、写接口、读写锁的工厂类、读和写的实现类几大部分。
package designpaten;
//Lock根接口
public interface Lock {
void lock() throws InterruptedException;
void unLock();
}
package designpaten;
//读写锁实现接口
public interface ReadWriteLock {
Lock readLock();
Lock writeLock();
int getWritingWriters();//获取当前有多少线程正在执行写操作
int getWaitingWriters();//获取当前有多少线程正在等待获取写入项
int getReadingReaders();//获取当前有多少线程正在等待获取reader锁
static ReadWriteLock readWriteLock() {
return new ReadWriterLockImpl(); //工厂方法创建ReadWriteLock
}
static ReadWriteLock readWriteLock(boolean preferWriter) {
return new ReadWriterLockImpl(preferWriter);
}
}
package designpaten;
class ReadWriterLockImpl implements ReadWriteLock {
private final Object MUTEX = new Object();//定义对象锁
private int writingWriters = 0;//定义有多少线程正在写入
private int waitingWriters = 0;//定义排队等待写锁的线程数
private int readingReaders = 0;//定义读锁线程数
private boolean preferWriter;//定义读和写的偏好设置
public ReadWriterLockImpl(boolean preferWriter) {
this.preferWriter = preferWriter;
}
public ReadWriterLockImpl() {
this(true);
}
@Override
public Lock readLock() {
// TODO Auto-generated method stub
return new ReadLock(this);
}
@Override
public Lock writeLock() {
// TODO Auto-generated method stub
return new WriteLock(this);
}
void incrementWritingWriters() {
this.writingWriters++; //写线程增加
}
void incrementWaitingWriters() {
this.waitingWriters++;//等待写线程增加
}
void incrementReadingReaders() {
this.readingReaders++;//读线程增加
}
void decrementWritingWriters() {
this.writingWriters--;//写线程减少
}
void decrementWaitingWriters() {
this.waitingWriters--;//等待写线程减少
}
void decrementReadingReaders() {
this.readingReaders--;//读线程减少
}
@Override
public int getWritingWriters() {
// TODO Auto-generated method stub
return this.writingWriters;//获取写线程数
}
@Override
public int getWaitingWriters() {
// TODO Auto-generated method stub
return this.waitingWriters;//获取等待写线程
}
@Override
public int getReadingReaders() {
// TODO Auto-generated method stub
return this.readingReaders;//获取读线程
}
Object getMutex() {
return this.MUTEX;//获取对象锁
}
boolean getPreferWriter() {
return this.preferWriter;//获取当前是否偏向写锁
}
void changePrefer(boolean preferWriter){
this.preferWriter = preferWriter; //设置写锁偏好
}
}
package designpaten;
//读锁具体实现
public class ReadLock implements Lock {
private final ReadWriterLockImpl readWriteLock;
ReadLock(ReadWriterLockImpl readWriterLockImpl) {
this.readWriteLock = readWriterLockImpl;
}
@Override
public void lock() throws InterruptedException {
synchronized(readWriteLock.getMutex()) {
while(readWriteLock.getWritingWriters()>0
||(readWriteLock.getPreferWriter() && readWriteLock.getWaitingWriters()>0)) {
readWriteLock.getMutex().wait();
}
readWriteLock.incrementReadingReaders();
}
}
@Override
public void unLock() {
synchronized(readWriteLock.getMutex()) {
readWriteLock.decrementReadingReaders();
readWriteLock.changePrefer(true);//可以使writer线程获得更多的机会
readWriteLock.getMutex().notifyAll();
}
}
}
package designpaten;
//写锁
class WriteLock implements Lock {
private final ReadWriterLockImpl readWriteLock;
public WriteLock(ReadWriterLockImpl readWriterLockImpl) {
this.readWriteLock = readWriterLockImpl;
}
@Override
public void lock() throws InterruptedException {
synchronized(readWriteLock.getMutex()) {
try{
readWriteLock.incrementWaitingWriters();
while(readWriteLock.getReadingReaders() >0
|| readWriteLock.getWritingWriters() > 0) {
readWriteLock.getMutex().wait();
}
}finally {
this.readWriteLock.decrementWaitingWriters();
}
readWriteLock.incrementWritingWriters();
}
}
@Override
public void unLock() {
synchronized(readWriteLock.getMutex()) {
readWriteLock.decrementWritingWriters();
readWriteLock.changePrefer(false);//使读锁能被快速获得
readWriteLock.getMutex().notifyAll();
}
}
}
package designpaten;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
public class ShareData {
private final List container = new ArrayList<>();
private final ReadWriteLock readWriteLock = ReadWriteLock.readWriteLock();
private final Lock readLock = readWriteLock.readLock();
private final Lock writeLock = readWriteLock.writeLock();
private final int length;
public ShareData(int length) {
this.length = length;
for(int i=0;i
package designpaten;
public class ReadWriteLockTest {
private final static String text = "thisistheexampleforreadandwritelock";
public static void main(String args[]) {
final ShareData shareData = new ShareData(50);
for(int i=0;i<2;i++) {
new Thread(()->{
for(int index = 0;index < text.length();index++) {
try {
char c = text.charAt(index);
shareData.write(c);
System.out.println(Thread.currentThread()+" write:"+c);
}catch(Exception e) {
e.printStackTrace();
}
}
}
).start();
}
//读线程
for(int i=0;i<10;i++){
new Thread(()->{
while(true) {
try {
System.out.println(Thread.currentThread() + " read:"+
new String(shareData.read()));
}catch(Exception e) {
e.printStackTrace();
}
}
}).start();
}
}
}
代码的具体逻辑就是如下:
定义Lock接口类,定义Lock类的两个方法lock和unLock方法。定义读写锁工厂类接口WriteReadLock接口,包括锁对象、读线程数、写线程数、等待写线程数等定义WriteReadLock实现类WriteReadLockImpl,定义锁对象,提供获取读线程数、写线程数、等待写线程数、读写偏好等参数入口定义Lock实现类ReadLock、WriteLock,实现lock和unLock方法实现逻辑。在unLock和lock方法中需要用synchronized修饰在步骤三中定义的对对象。定义数据对象ShareData类,在类中的read和write方法中要实现读和写,要先获取对应的读锁和写锁,其中读和写互斥。定义读写锁分析测试启动类,分别定义多个线程,同时对ShareData进行读和写,进行测试。
总之,在jdk环境环境下建议用StampleLock,该锁提供一种乐观机制。如果写锁比较多的化一般用synchronized。



