当前位置: 代码迷 >> 综合 >> java并发-锁-ReentrantLock(重入锁)和ReentrantReadWriteLock(读写锁)
  详细解决方案

java并发-锁-ReentrantLock(重入锁)和ReentrantReadWriteLock(读写锁)

热度:32   发布时间:2024-01-16 12:40:06.0

转载地址:https://www.cnblogs.com/whatadiors/p/8013086.html

  • 同步控制是并发程序必不可少的重要手段,synchronized关键字就是一种简单的控制方式,除此之外,JDK内部并发包中也也提供了Lock接口,该接口中提供了lock()方法和unLock()方法对显式加锁和显式释放锁操作进行支持。

ReentrantLock(重入锁)

重入锁可以完全替代synchronized关键字,在jdk5早期版本中重入锁的性能远远好于synchronized,但从JDK6开始JDK在synchronized中做了大量的优化,是的两者的性能差距不大,

复制代码

public class Test {public static ReentrantLock lock = new ReentrantLock();public static int i = 0;public static void increase() {try {lock.lock();i++;}finally {lock.unlock();}}public static void main(String[] args) throws Exception {Thread t1 = new TestThread();Thread t2 = new TestThread();t1.start();t2.start();t1.join();t2.join();System.out.println(i);}
}
class TestThread extends Thread {@Overridepublic void run() {for (int j = 0; j < 1000; j++) {Test.increase();}}
}

复制代码

从这段代码可以看到与synchronized相比,重入锁有着显示的操作过程,我们需要手动定义核实加锁,核实释放锁,但也就是因为这样,重入锁对逻辑的控制灵活性要好于synchronized。

公平锁

大多数情况下锁的申请都是非公平的。如一个线程1先请求了锁A,然后线程2页也请求了锁A,那么当锁A可用时,是线程1可以获得锁还是线程2是不一定的,系统只是会从这个锁的等待队列中随机挑选一个。

重入锁允许我们对其公平性进行设置。公平锁的一大特点是:它不会产生饥饿现象。只要排队,最终你就可以获得资源。可以使用如下构造函数创建公平锁:

public ReentrantLock(boolean fair)

当参数fair为true,表示锁的公平的,当然由于公平所需要维护有序队列,因此公平锁的实现成本比较高,性能相对也底下,所以默认都是非公平锁

复制代码

public class Test {public static ReentrantLock lock = new ReentrantLock(true);public static void main(String[] args) throws Exception {Thread t1 = new TestThread();Thread t2 = new TestThread();t1.start();t2.start();}
}
class TestThread extends Thread {@Overridepublic void run() {while(true)try {Test.lock.lock();System.out.println(Thread.currentThread().getName()+"获得锁");} finally{Test.lock.unlock();}}
}

复制代码

可以看到如上代码制定公平锁之后,两个线程交替获得锁

复制代码

Thread-1获得锁
Thread-0获得锁
Thread-1获得锁
Thread-0获得锁
Thread-1获得锁
Thread-0获得锁
Thread-1获得锁
Thread-0获得锁
Thread-1获得锁
Thread-0获得锁
Thread-1获得锁
Thread-0获得锁
Thread-1获得锁
...

复制代码

ReentrantLock的一些其他方法:

public boolean tryLock();
//使用此方法,当前线程会尝试获得锁,如果锁未被其他线程占用,则申请锁成功,返回true,否则会立即返回false.这种模式不会引起线程等待,因此也不会产生死锁。

 

public boolean tryLock(long timeout, TimeUnit unit)
//在这里tryLock接收两个参数,一个表示等待时长,一个表示计时单位,表示在一段时间范围内如果得到锁就返回true,否则直接返回false,不在继续等待锁。

复制代码

public class Test implements Runnable {public static ReentrantLock lock = new ReentrantLock();@Overridepublic void run() {try {if (lock.tryLock(5, TimeUnit.SECONDS)) {System.out.println(Thread.currentThread().getName());System.out.println("get lock success");Thread.sleep(6000);} else {System.out.println(Thread.currentThread().getName());System.out.println("get lock failed");}} catch (InterruptedException e) {e.printStackTrace();} finally {if (lock.isHeldByCurrentThread()) {lock.unlock();}}}public static void main(String args[]) {Test timeLock = new Test();Thread thread1 = new Thread(timeLock);Thread thread2 = new Thread(timeLock);thread1.start();thread2.start();}
}

复制代码

输出结果:

Thread-0
get lock success
Thread-1
get lock failed

 其他:

关于重入锁的具体原理及这部分源码的分析可以看下这篇文章http://www.cnblogs.com/xrq730/p/4979021.html

ReentrantReadWriteLock(读写锁)

ReadWriteLock是JDK5开始提供的读写分离锁。读写分离开有效的帮助减少锁的竞争,以提升系统性能。用锁分离的机制避免多个读操作线程之间的等待。

读写锁的访问约束:

  • 读-读不互斥:读读之间不阻塞
  • 读-写互斥:读堵塞写,写也阻塞读
  • 写-写互斥:写写阻塞

如果在一个系统中读的操作次数远远大于写操作,那么读写锁就可以发挥明显的作用,提升系统性能

复制代码

public class Test {private static Lock lock = new ReentrantLock();private static ReentrantReadWriteLock reentrantReadWriteLock = new ReentrantReadWriteLock();private static Lock readLock = reentrantReadWriteLock.readLock();private static Lock writeLock = reentrantReadWriteLock.writeLock();private static int value;public static Object handleRead(Lock lock) throws InterruptedException {try {lock.lock();Thread.sleep(1000);// 模拟读操作System.out.println("读操作:" + value);return value;} finally {lock.unlock();}}public static void handleWrite(Lock lock, int index)throws InterruptedException {try {lock.lock();Thread.sleep(1000);// 模拟写操作System.out.println("写操作:" + value);value = index;} finally {lock.unlock();}}public static void main(String[] args) throws Exception {TestReadThread testReadThread = new TestReadThread();TestWriteThread testWriteThread = new TestWriteThread();for (int i = 0; i < 18; i++) {new Thread(testReadThread).start();}for (int i = 18; i < 20; i++) {new Thread(testWriteThread).start();}}private static class TestReadThread extends Thread {@Overridepublic void run() {try {//Test.handleRead(lock);Test.handleRead(readLock);} catch (InterruptedException e) {e.printStackTrace();}}}private static class TestWriteThread extends Thread {@Overridepublic void run() {try {//Test.handleWrite(lock,new Random().nextInt(100));Test.handleWrite(writeLock,new Random().nextInt(100));} catch (InterruptedException e) {e.printStackTrace();}}}
}

复制代码

 

这一段代码可以清楚的表达读写锁的作用,如果不使用读写锁,那么整个程序执行时间大概是20s。换用读写锁后只需要2s多,这里读线程完全并行,节省了大部分时间。

  相关解决方案