读写锁。我们上次温习了synchronized与Lock.我们遗留了一个问题,就是想让读读不互斥,因为多线程同时读取数据并不会破坏数据。如果能实现读读不用互斥,那将大大提升了多线程读性能。于是我们引入了读写锁ReentrantReadWriteLock。ReentrantReadWriteLock可以专门创建一个读锁和写锁。读锁:读读不互斥,但是读写互斥。 写锁:不但读写互斥,而且写写也互斥。
创建一个读写锁
ReadWriteLock rwl = new ReentrantReadWriteLock();
读锁: rwl.readLock().lock() 与 rwl.readLock().unlock(); 成对出现; 读完了公共变量,其它线程才能写这个公共变量(赋值),但是多个线程可以同时读。
写锁: rwl.writeLock().lock() 与 rwl.writeLock().unlock(); 成对出现;写完了公共变量,其它线程才能写或读这个公共变量。
以下示例代码展示了读锁与写锁。创建两个一个Queue自定义类,有一个get()和put()方法,在get()方法里使用读锁,在put()里使用写锁,它们都操作一个公共数据Object data。
package testFuture;
import java.util.Random;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
public class LockTest {
public static void main(String[] args) {
final Queue3 q3 = new Queue3();
for(int i=0;i<3;i++)
{
new Thread(){
public void run(){
while(true){
q3.get();
}
}
}.start();
new Thread(){
public void run(){
while(true){
q3.put(new Random().nextInt(10000));
}
}
}.start();
}
}
}
synchronized
class Queue3{
private Object data = null;//共享数据,只能有一个线程能写该数据,但可以有多个线程同时读该数据。
ReadWriteLock rwl = new ReentrantReadWriteLock();
public void get(){
rwl.readLock().lock();
try {
System.out.println(Thread.currentThread().getName() + " be ready to read data!");
Thread.sleep((long)(Math.random()*1000));
System.out.println(Thread.currentThread().getName() + "have read data :" + data);
} catch (InterruptedException e) {
e.printStackTrace();
}finally{
rwl.readLock().unlock();
}
}
public void put(Object data){
rwl.writeLock().lock();
try {
System.out.println(Thread.currentThread().getName() + " be ready to write data!");
Thread.sleep((long)(Math.random()*1000));
this.data = data;
System.out.println(Thread.currentThread().getName() + " have write data: " + data);
} catch (InterruptedException e) {
e.printStackTrace();
}finally{
rwl.writeLock().unlock();
}
}
}
打印日志如下:
Thread-2 be ready to read data!
Thread-0 be ready to read data!
Thread-4 be ready to read data!
Thread-0have read data :null
Thread-2have read data :null
Thread-4have read data :null
Thread-5 be ready to write data!
Thread-5 have write data: 8687
Thread-5 be ready to write data!
Thread-5 have write data: 8653
Thread-1 be ready to write data!
Thread-1 have write data: 7614
Thread-1 be ready to write data!
Thread-1 have write data: 4680
Thread-3 be ready to write data!
Thread-3 have write data: 473
Thread-0 be ready to read data!
Thread-2 be ready to read data!
Thread-4 be ready to read data!
Thread-0have read data :473
Thread-4have read data :473
Thread-2have read data :473
Thread-5 be ready to write data!
Thread-5 have write data: 7706
Thread-1 be ready to write data!
Thread-1 have write data: 7828
Thread-1 be ready to write data!
Thread-1 have write data: 6492
Thread-3 be ready to write data!
Thread-3 have write data: 6439
Thread-3 be ready to write data!
Thread-3 have write data: 4558
Thread-0 be ready to read data!
Thread-4 be ready to read data!
Thread-2 be ready to read data!
Thread-0have read data :4558
Thread-4have read data :4558
Thread-2have read data :4558
我们会发现总共有6个线程:0,2,4线程是读,1,3,5线程是写数据,当5在写数据的时候,put方法输出以下2条日志:
Thread-5 be ready to write data!
Thread-5 have write data: 8687
这2条日志之间,并没有其它线程打扰。5写完了,接着才是线程1写数据,输出日志:
Thread-1 be ready to write data!
Thread-1 have write data: 7614
同样,这2条日志之间,没有其它线程打扰。
假设线程5在执行put写数据的时候,输出日志如下,则是被线程1打扰了:
Thread-5 be ready to write data!
Thread-1 be ready to write data!
Thread-5 have write data: 8687
Thread-1 have write data: 7614
线程5的本意是让data变为8676,结果当线程5的put返回的时候,实际上data已被线程1修改成了7614,线程5还摸不着头脑,自己怎么就做错了,很简单的事情都能出错。