背景
当多线程访问共享数据时,为保证线程安全,通常情况下会对共享数据加锁。最简单的方式在读或者写共享数据时加锁。但是这样会影响到效率。比如,多个线程只对数据做读操作而没有其他线程对其写操作时,读线程没有必要被阻塞,这时不存在线程安全问题。
总结一下
是否加锁 | read | write |
---|---|---|
read | F | T |
write | T | T |
也就是说,当只有度线程时,才无需加锁。
首先定义锁
public class ReadWriteLock {
private int readingReaders = 0;
private int waitingReaders = 0;
private int writingWriters = 0;
private int waitingWriters = 0;
public synchronized void readLock() throws InterruptedException {
waitingReaders++;
try {
while (writingWriters > 0) {
this.wait();
}
readingReaders++;
} finally {
waitingReaders--;
}
}
public synchronized void readUnlock() {
readingReaders--;
this.notifyAll();
}
public synchronized void writeLock() throws InterruptedException {
waitingWriters++;
try {
while (readingReaders > 0 || writingWriters > 0) {
this.wait();
}
writingWriters++;
} finally {
waitingWriters--;
}
}
public synchronized void writeUnlock() {
writingWriters--;
this.notifyAll();
}
}
首先定义四个变量,用于计数当前读写,等待读写的线程数量
private int readingReaders = 0;
private int waitingReaders = 0;
private int writeWriters = 0;
private int waitingWriters = 0;
根据表格,读线程等待的条件是有活跃的读线程,因此
public synchronized void readLock() throws InterruptedException {
waitingReaders++;
try {
while (writingWriters > 0) {
this.wait();
}
readingReaders++;
} finally {
waitingReaders--;
}
}
只判断writeWriters是否大于0.
同理,写锁是否wait的条件,是当前有线程正在读数据或者当前有线程在写数据
public synchronized void writeLock() throws InterruptedException {
waitingWriters++;
try {
while (readingReaders > 0 || waitingWriters > 0) {
this.wait();
}
writingWriters++;
} finally {
waitingWriters--;
}
}
这样,一个读写分离锁便定义好了。现在定义共享资源,模拟读写
public class SharedData {
private char[] data = new char[10];
public SharedData(char c) {
for (int i = 0; i < data.length; i++) {
data[i] = c;
}
}
public String readData() {
StringBuffer s = new StringBuffer();
for (int i = 0; i < data.length; i++) {
s.append(data[i]);
}
return s.toString();
}
public void writeData(char c) {
System.out.println(Thread.currentThread().getName() + " write date char[" + c + "]");
for (int i = 0; i < data.length; i++) {
data[i] = c;
}
}
}
测试类
public class ReadWriteLockTest {
public static void main(String[] args) {
ReadWriteLock lock = new ReadWriteLock();
SharedData data = new SharedData('*');
IntStream.range(0, 10).forEach(i -> {
Thread t = new Thread(() -> {
try {
while (true) {
lock.readLock();
System.out.println(Thread.currentThread().getName() + " " + data.readData());
lock.readUnlock();
Thread.sleep(10);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}, "Thread read" + i);
t.start();
});
IntStream.range(0, 10).forEach(i -> {
Thread t = new Thread(() -> {
try {
while (true) {
lock.writeLock();
data.writeData(randomChar());
lock.writeUnlock();
Thread.sleep(10);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}, "Thread write" + i);
t.start();
});
}
private synchronized static char randomChar() {
Random r = new Random();
int i = r.nextInt(10);
return (char) (i + 118);
}
}
结果
Thread read0 **********
Thread read4 **********
Thread read3 **********
Thread read2 **********
Thread read1 **********
Thread read6 **********
Thread read5 **********
Thread read7 **********
Thread read8 **********
Thread read9 **********
Thread write0 write date char[}]
Thread write5 write date char[v]
Thread write1 write date char[y]
Thread write4 write date char[]
Thread write6 write date char[~]
Thread write3 write date char[z]
Thread write2 write date char[v]
Thread write8 write date char[|]
Thread write9 write date char[z]
Thread write7 write date char[v]
Thread read4 vvvvvvvvvv
Thread read1 vvvvvvvvvv
Thread read7 vvvvvvvvvv
Thread read3 vvvvvvvvvv
Thread read0 vvvvvvvvvv
Thread read6 vvvvvvvvvv
Thread read2 vvvvvvvvvv
Thread write4 write date char[{]
Thread write2 write date char[y]
Thread read8 yyyyyyyyyy
Thread read9 yyyyyyyyyy
Thread read5 yyyyyyyyyy
Thread write5 write date char[v]
Thread write0 write date char[v]
Thread write1 write date char[v]
Thread write7 write date char[~]
Thread write9 write date char[w]
Thread write8 write date char[}]
Thread write6 write date char[v]
Thread write3 write date char[~]
Thread read1 ~~~~~~~~~~
Thread read7 ~~~~~~~~~~
Thread read3 ~~~~~~~~~~
Thread read4 ~~~~~~~~~~
Thread read6 ~~~~~~~~~~
Thread read2 ~~~~~~~~~~
Thread read8 ~~~~~~~~~~
Thread read0 ~~~~~~~~~~