Java读写锁分离设计模式

Java基础

浏览数:115

2019-8-20

AD:资源代下载服务

在多线程的环境下,对于共享资源的访问,为了防止资源出现不同步的情况出现,可以使用synchronized关键字对资源进行同步,使得同一时间只有一个线程能够访问共享资源。但synchronized是具有排他性质的锁,使用它会降低系统性能,而我们知道对于共享资源的操作,可以归为两类:读和写。而读操作并不会影响共享资源,而写操作才需要进行同步,对此可以设计出一种读写分离模式的锁,在同一时间可以要么只能读,要么只能写,而读线程可以有多个,写线程只能有一个。

定义锁的接口,解锁和加锁方法

public interface Lock {
    void lock() throws InterruptedException;
    void unlock();
}

定义读写锁的接口

public interface ReadWriteLock {
    Lock readLock();
    Lock writeLock();
    int getWritingWriters();
    int getWaitingWriters();
    int getReadingReaders();

    //工厂方法,创建ReadWriteLock,下面是带构造参数传     static ReadWriteLock readWriterLock(){
        return new ReadWriteLockImp();
    }
    static ReadWriteLock readWriterLock(boolean preferWriter){
        return new ReadWriteLockImp(preferWriter);
    }
}

3个get方法分别是:

  1. 获取正在写入中的线程数(只能为0和1)
  2. 获取等待写入的线程数
  3. 获取正在读取的线程数

读写锁实现:

public class ReadWriteLockImp implements ReadWriteLock {
    //定义对象锁     private final Object MUTEX = new Object();
    //当前有多少个线程写入     private int writingWriters = 0;
    //当前多少个线程正在等待写入     private int waitingWriters = 0;
    //当前多少个线程正在读     private int readingReaders = 0;
    //是否偏好于写     private boolean preferWriter;

    //不填默认为true     public ReadWriteLockImp() {
        this(true);
    }

    public ReadWriteLockImp(boolean preferWriter) {
        this.preferWriter = preferWriter;
    }

    @Override
    public Lock readLock() {
        return new ReadLock(this);
    }

    @Override
    public Lock writeLock() {
        return new WriteLock(this);
    }

    //正在写的线程增加1     public void incrementWritingWriters() {
        this.writingWriters++;
    }

    //等待写的线程增加1     public void incrementWaitingWriters() {
        this.waitingWriters++;
    }

    //正在读的线程增加1     public void incrementReadingReaders() {
        this.readingReaders++;
    }

    //反之     public void decrementWritingWriters() {
        this.writingWriters--;
    }

    public void decrementWaitingWriters() {
        this.waitingWriters--;
    }

    public void decrementReadingReaders() {
        this.readingReaders--;
    }

    @Override
    public int getWritingWriters() {
        return this.writingWriters;
    }

    @Override
    public int getWaitingWriters() {
        return this.waitingWriters;
    }

    @Override
    public int getReadingReaders() {
        return this.readingReaders;
    }

    //获取对象锁     public Object getMUTEX() {
        return MUTEX;
    }

    //获取是否偏向写锁     public boolean getPreferWriter() {
        return preferWriter;
    }

    //设置偏好锁     public void setPreferWriter(boolean preferWriter) {
        this.preferWriter = preferWriter;
    }
}

读锁的实现:

public class ReadLock implements Lock {
    private final ReadWriteLockImp readWriteLockImp;

    public ReadLock(ReadWriteLockImp readWriteLockImp) {
        this.readWriteLockImp = readWriteLockImp;
    }

    @Override
    public void lock() throws InterruptedException {
        //使用对象锁         synchronized (readWriteLockImp.getMUTEX()) {
            //如果现在有写操作,或者有写操作正在等待写入且偏好写,则只能挂起等待             while (readWriteLockImp.getWritingWriters() > 0 ||
                    (readWriteLockImp.getPreferWriter()
                            && readWriteLockImp.getWaitingWriters() > 0)) {
                readWriteLockImp.getMUTEX().wait();
            }
            //获取锁成功             readWriteLockImp.incrementReadingReaders();
        }
    }

    @Override
    public void unlock() {
        synchronized (readWriteLockImp.getMUTEX()) {
            //读线程数量减一             readWriteLockImp.decrementReadingReaders();
            //将偏好设置为true,让写线程获取更多机会             readWriteLockImp.setPreferWriter(true);
            //唤醒挂起中的writer线程             readWriteLockImp.getMUTEX().notifyAll();
        }
    }
}

写锁的实现:

public class WriteLock implements Lock {
    private ReadWriteLockImp readWriteLockImp;

    public WriteLock(ReadWriteLockImp readWriteLockImp){
        this.readWriteLockImp = readWriteLockImp;
    }
    @Override
    public void lock() throws InterruptedException {
        synchronized (readWriteLockImp.getMUTEX()){
            //此时让等待写锁的线程数量+1             try {
                readWriteLockImp.incrementWaitingWriters();
                //如果当前正有读线程在运行,或者有写线程运行,则挂起                 while (readWriteLockImp.getReadingReaders()>0
                        || readWriteLockImp.getWritingWriters()>0){
                    readWriteLockImp.getMUTEX().wait();
                }
            }finally {
                //获取了写锁,等待-1                 readWriteLockImp.decrementWaitingWriters();
            }
            //正在写入加1             readWriteLockImp.incrementWritingWriters();
        }
    }

    @Override
    public void unlock() {
        synchronized (readWriteLockImp.getMUTEX()){
            //写入线程数量-1             readWriteLockImp.decrementWritingWriters();
            //写偏好改为false,让读线程更多机会             readWriteLockImp.setPreferWriter(false);
            readWriteLockImp.getMUTEX().notifyAll();
        }
    }
}

读写锁实现成功,来看一看具体使用

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.stream.IntStream;

//读写锁的使用 public class ShareData {
    //定义共享数据源     private final List<Character> container = new ArrayList<>();
    //构造ReadWriteLock     private final ReadWriteLock readWriteLock = ReadWriteLock.readWriterLock();
    //创建读锁     private final Lock readLock = readWriteLock.readLock();
    //创建写锁     private final Lock writeLock = readWriteLock.writeLock();
    private final int length;

    public ShareData(int length) {
        this.length = length;
        IntStream.range(0, length).forEach(i -> {
            container.add(i, 'a');
        });
    }

    public char[] read() throws InterruptedException {
        try {
            //创建读锁             readLock.lock();
            char[] newBuffer = new char[length];
            IntStream.range(0, length).forEach(i -> {
                newBuffer[i] = container.get(i);
            });
            slowly();
            return newBuffer;
        } finally {
            //解锁             readLock.unlock();
        }
    }

    public void write(char a) throws InterruptedException {
        try {
            writeLock.lock();
            IntStream.range(0, length).forEach(i -> {
                container.add(i, a);
            });
            slowly();
        } finally {
            writeLock.unlock();
        }
    }

    //模拟读取时间     private void slowly() {

        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }
}

作者:不减商山(作者)