1.排他锁(互斥锁)的概念:
synchronized,ReentrantLock这些锁都是排他锁,这些锁同一时刻只允许一个线程进行访问。
2.读写锁的概念:
分为读锁和写锁,多个读锁不互斥,读锁和写锁互斥,写锁与写锁互斥。
3.读写锁的好处:
为了提高性能,Java提供了读写锁,在读的地方使用读锁,在写的地方使用写锁,灵活控制,如果没有写锁的情况下,读是无阻塞的,在一定程度上提高了程序的执行效率。
原来使用的互斥锁只能同时间有一个线程在运行,现在的读写锁同一时刻可以多个读锁同时运行,这样的效率比原来的排他锁(互斥锁)效率高。
4.读写锁的原理分析:
Java中读写锁有个接口java.util.concurrent.locks.ReadWriteLock,也有具体的实现ReentrantReadWriteLock,
lock方法 是基于CAS 来实现的
源码:ew plain
public interface ReadWriteLock {
/**
* Returns the lock used for reading.
*
* @return the lock used for reading.
*/
Lock readLock();
/**
* Returns the lock used for writing.
*
* @return the lock used for writing.
*/
Lock writeLock();
}
案例一:
package WriteReadLock;
import java.util.Random;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
public class ReadWriterLockTest {
public static void main(String[] args) {
final Queue q3 = new Queue();
for(int i=0;i<3;i++)
{
new Thread(){
public void run(){
while(true){
q3.get();
}
}
}.start();
}
for(int i=0;i<3;i++)
{
new Thread(){
public void run(){
while(true){
q3.put(new Random().nextInt(10000));
}
}
}.start();
}
}
}
class Queue{
//共享数据,只能有一个线程能写该数据,但可以有多个线程同时读该数据。
private Object data = null;
//得到读写锁
ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
/**
* 将用于读的get()和写的put()放在同一个类中这样是为了对同一个资源data进行操作,形成互斥
*/
/**
* 进行读操作
* 可以多个读线程同时进入,写线程不能执行
*/
public void get(){
//获取读锁,并加锁
Lock readLock = readWriteLock.readLock();
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 {
//!!!!!!注意:锁的释放一定要在trycatch的finally中,因为如果前面程序出现异常,锁就不能释放了
//释放读锁
readLock.unlock();
}
}
/**
* 进行写操作
* 只能一个写线程进入,读线程不能执行
*/
public void put(Object data){
//获取写锁,并加锁
Lock writeLock = readWriteLock.writeLock();
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 {
//释放写锁
writeLock.unlock();
}
}
}
在加入读写锁之后:读的过程中,不会有写
案例二:
package WriteReadLock;
import java.util.Random;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
public class ReadWriterLockTest {
public static void main(String[] args) {
final Queue q3 = new Queue();
for(int i=0;i<3;i++)
{
new Thread(){
public void run(){
while(true){
q3.get();
}
}
}.start();
}
for(int i=0;i<3;i++)
{
new Thread(){
public void run(){
while(true){
q3.put(new Random().nextInt(10000));
}
}
}.start();
}
}
}
class Queue{
//共享数据,只能有一个线程能写该数据,但可以有多个线程同时读该数据。
private Object data = null;
//得到读写锁
ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
/**
* 将用于读的get()和写的put()放在同一个类中这样是为了对同一个资源data进行操作,形成互斥
*/
/**
* 进行读操作
* 可以多个读线程同时进入,写线程不能执行
*/
public void get(){
//获取读锁,并加锁
Lock readLock = readWriteLock.readLock();
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 {
//!!!!!!注意:锁的释放一定要在trycatch的finally中,因为如果前面程序出现异常,锁就不能释放了
//释放读锁
readLock.unlock();
}
}
/**
* 进行写操作
* 只能一个写线程进入,读线程不能执行
*/
public void put(Object data){
//获取写锁,并加锁
Lock writeLock = readWriteLock.writeLock();
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 {
//释放写锁
writeLock.unlock();
}
}
}