Synchronized和ReentrantLock的区别
2017-12-08 19:28
726 查看
昨天面试,面试官问了自己一个synchronized和ReentrantLock的区别,感觉自己回答的并不是特别好,今天在翻书学习总结一下,毕竟书读百遍其义自见。
开始进入正题
两者的共同点:
1)协调多线程对共享对象、变量的访问
2)可重入,同一线程可以多次获得同一个锁
3)都保证了可见性和互斥性
两者的不同点:
1)ReentrantLock显示获得、释放锁,synchronized隐式获得释放锁
2)ReentrantLock可响应中断、可轮回,synchronized是不可以响应中断的,为处理锁的不可用性提供了更高的灵活性
3)ReentrantLock是API级别的,synchronized是JVM级别的
4)ReentrantLock可以实现公平锁
5)ReentrantLock通过Condition可以绑定多个条件
6)底层实现不一样, synchronized是同步阻塞,使用的是悲观并发策略,lock是同步非阻塞,采用的是乐观并发策略
虽然ReentrantLock可以提供比synchronized更高级的功能,但是仍不能替换synchronized
为什么呢?
《java并发编程实战》上说是因为如果使用reentrantlock时,你没有释放锁,很难追踪到最初发生错误的位置,因为没有记录应该释放锁的位置和时间。网上找了一下,没有找到其他比较合理的答案,先暂且记住吧
几个方法:
1) boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
time:等待锁定的最长时间
unit: 时间单位
这个方法起到了定时锁的作用,如果在指定时间内没有获取到锁,将会返回false
应用:具有时间限制的操作时使用
一个简单例子:
运行结果:
此时线程2在1秒之内没有获得到锁
boolean tryLock();
只有在获得锁的情况下才会返回true,可以通过使用这个方法先判断一下能否获得锁,避免长时间获不到锁的等待,以及死锁的产生。这个方法只有一次尝试获得锁的机会。
void lockInterruptibly() throws InterruptedException;
获得可中断锁。可中断锁可以响应中断,可以让它中断自己或者在别的线程中中断它,中断后可以放弃等待,去处理其他事,而不可中断锁不会响应中断,将一直等待,synchronized就是不可中断。
举例子:
使用synchronized关键字
Buffer类:
public class Buffer {
private Object lock;
Reader类:
Writer类:
reader类没有被中断
使用ReentrantLock获得中断锁
BufferInterruptibly类:
Reader2类
Writere2类:
运行结果:
ReentrantLock的公平性
在ReentrantLock中的构造函数中,提供了一个参数,指定是否为公平锁。
如果指定了参数为true,默认为非公平锁。
公平锁:线程将按照它们发出的请求顺序来获得锁
非公锁:当一个线程请求非公平锁的时候,如果发出请求时,获得锁的线程刚好释放锁,则该线程将会获得锁而跳过在该锁上等待的线程。
一个公平锁例子:
运行结果:
什么时候选择使用synchronized,什么使用选择使用ReentrantLock
仅当synchronized不能满足时才使用ReentrantLockk,因为使用ReentrantLock要非常小心,不释放锁将影响其他需要该锁的代码块运行
不能使用synchronized不满足的情形:
1)公平性
2)可中断
4)分块结构的加锁,比如jdk1.7ConcurrentHashMap的分段锁(目前还不是提别理解这个,先记住这个例子,后头补充)
synchronized和ReentrantLock两者之间性能的比较
从jdk1.5以后,性能就差不多了,因为jvm对synchronized进行了很多优化
开始进入正题
两者的共同点:
1)协调多线程对共享对象、变量的访问
2)可重入,同一线程可以多次获得同一个锁
3)都保证了可见性和互斥性
两者的不同点:
1)ReentrantLock显示获得、释放锁,synchronized隐式获得释放锁
2)ReentrantLock可响应中断、可轮回,synchronized是不可以响应中断的,为处理锁的不可用性提供了更高的灵活性
3)ReentrantLock是API级别的,synchronized是JVM级别的
4)ReentrantLock可以实现公平锁
5)ReentrantLock通过Condition可以绑定多个条件
6)底层实现不一样, synchronized是同步阻塞,使用的是悲观并发策略,lock是同步非阻塞,采用的是乐观并发策略
虽然ReentrantLock可以提供比synchronized更高级的功能,但是仍不能替换synchronized
为什么呢?
《java并发编程实战》上说是因为如果使用reentrantlock时,你没有释放锁,很难追踪到最初发生错误的位置,因为没有记录应该释放锁的位置和时间。网上找了一下,没有找到其他比较合理的答案,先暂且记住吧
几个方法:
1) boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
time:等待锁定的最长时间
unit: 时间单位
这个方法起到了定时锁的作用,如果在指定时间内没有获取到锁,将会返回false
应用:具有时间限制的操作时使用
一个简单例子:
public class TestReentrantLock { public static void main(String[] args) { Lock r = new ReentrantLock(); //线程1 Thread thread1 = new Thread(new Runnable() { @Override public void run() { //获得锁 r.lock(); try { System.out.println("线程1获得了锁"); //睡眠5秒 Thread.currentThread().sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } finally { r.unlock(); } } }); thread1.start(); //线程2 Thread thread2 = new Thread(new Runnable() { @Override public void run() { try { if (r.tryLock(1000, TimeUnit.MILLISECONDS)) { System.out.println("线程2获得了锁"); } else { System.out.println("获取锁失败了"); } } catch (InterruptedException e) { e.printStackTrace(); } } }); thread2.start(); } }
运行结果:
线程1获得了锁 获取锁失败了
此时线程2在1秒之内没有获得到锁
boolean tryLock();
只有在获得锁的情况下才会返回true,可以通过使用这个方法先判断一下能否获得锁,避免长时间获不到锁的等待,以及死锁的产生。这个方法只有一次尝试获得锁的机会。
void lockInterruptibly() throws InterruptedException;
获得可中断锁。可中断锁可以响应中断,可以让它中断自己或者在别的线程中中断它,中断后可以放弃等待,去处理其他事,而不可中断锁不会响应中断,将一直等待,synchronized就是不可中断。
举例子:
使用synchronized关键字
Buffer类:
public class Buffer {
private Object lock;
public Buffer() { lock = this; //buffer自身 } public void write() { synchronized (lock) { long startTime = System.currentTimeMillis(); System.out.println("往这个buffer写入数据..."); //死循环模拟要处理很长时间 for(;;) { if (System.currentTimeMillis() - startTime > Integer.MAX_VALUE) { break; } } System.out.println("终于写完了"); } } public void read() { synchronized (lock) { System.out.println("从这个buff读数据"); } } public static void main(String[] args) { Buffer buffer = new Buffer(); final Writer writer = new Writer(buffer); final Reader reader = new Reader(buffer); //启动线程 writer.start(); reader.start(); //尝试启动一个线程去中断reader线程 new Thread(new Runnable() { @Override public void run() { long start = System.currentTimeMillis(); for(;;) { //等待五秒钟去中断 if (System.currentTimeMillis() - start > 5000) { System.out.println("不等了,尝试中断"); reader.interrupt(); break; } } } }).start(); } }
Reader类:
public class Reader extends Thread { private Buffer buffer; public Reader(Buffer buffer) { this.buffer = buffer; } public void run() { buffer.read(); //这里将会引起长时间的阻塞 System.out.println("读结束"); } }
Writer类:
public class Writer extends Thread { private Buffer buffer; public Writer(Buffer buffer) { this.buffer = buffer; } public void run() { buffer.write(); } }
reader类没有被中断
使用ReentrantLock获得中断锁
BufferInterruptibly类:
public class BufferInterruptibly { private Lock lock = new ReentrantLock(); public void write() { lock.lock(); try { long start = System.currentTimeMillis(); System.out.println("开始往这个buff写入数据..."); //模拟要处理很长时间 for (;;) { if (System.currentTimeMillis() - start > Integer.MAX_VALUE) { break; } } System.out.println("终于写完了"); } finally { lock.unlock(); } } public void read() throws InterruptedException { lock.lockInterruptibly(); //获得可中断锁 try { System.out.println("从这个buff读数据"); } finally { lock.unlock(); } } public static void main(String args[]) { BufferInterruptibly buff = new BufferInterruptibly(); final Writer2 writer = new Writer2(buff); final Reader2 reader = new Reader2(buff); writer.start(); reader.start(); new Thread(new Runnable() { @Override public void run() { long start = System.currentTimeMillis(); for (;;) { if (System.currentTimeMillis() - start > 5000) { System.out.println("不等了,尝试中断"); reader.interrupt(); //此处中断读操作 break; } } } }).start(); } }
Reader2类
public class Reader2 extends Thread { private BufferInterruptibly buff; public Reader2(BufferInterruptibly buff) { this.buff = buff; } @Override public void run() { try { buff.read();//可以收到中断的异常,从而有效退出 } catch (InterruptedException e) { System.out.println("我不读了"); } System.out.println("读结束"); } }
Writere2类:
public class Writer2 extends Thread { private BufferInterruptibly buff; public Writer2(BufferInterruptibly buff) { this.buff = buff; } @Override public void run() { buff.write(); } }
运行结果:
开始往这个buff写入数据... 不等了,尝试中断 我不读了 读结束
ReentrantLock的公平性
在ReentrantLock中的构造函数中,提供了一个参数,指定是否为公平锁。
public ReentrantLock(boolean fair) { sync = fair ? new FairSync() : new NonfairSync(); }
如果指定了参数为true,默认为非公平锁。
公平锁:线程将按照它们发出的请求顺序来获得锁
非公锁:当一个线程请求非公平锁的时候,如果发出请求时,获得锁的线程刚好释放锁,则该线程将会获得锁而跳过在该锁上等待的线程。
一个公平锁例子:
public class TestReentrantLock2 { private static Lock lock = new ReentrantLock(true); //lock为公平锁 public static void main(String[] args) { Thread t1 = new Thread(new Runnable() { @Override public void run() { lock.lock(); try { System.out.println("线程1启动..."); } finally { lock.unlock(); } } }); Thread t2 = new Thread(new Runnable() { @Override public void run() { lock.lock(); try { System.out.println("线程2启动..."); } finally { lock.unlock(); } } }); Thread t3 = new Thread(new Runnable() { @Override public void run() { lock.lock(); try { System.out.println("线程3启动..."); } finally { lock.unlock(); } } }); t1.start(); t3.start(); t2.start(); } }
运行结果:
线程1启动... 线程3启动... 线程2启动...
什么时候选择使用synchronized,什么使用选择使用ReentrantLock
仅当synchronized不能满足时才使用ReentrantLockk,因为使用ReentrantLock要非常小心,不释放锁将影响其他需要该锁的代码块运行
不能使用synchronized不满足的情形:
1)公平性
2)可中断
4)分块结构的加锁,比如jdk1.7ConcurrentHashMap的分段锁(目前还不是提别理解这个,先记住这个例子,后头补充)
synchronized和ReentrantLock两者之间性能的比较
从jdk1.5以后,性能就差不多了,因为jvm对synchronized进行了很多优化
相关文章推荐
- ReentrantLock与synchronized的区别 ( by quqi99 )
- java的两种同步方式, Synchronized与ReentrantLock的区别
- ReentrantLock和synchronized关键字有什么区别
- ReentrantLock--synchronized和ReentrantLock区别及使用
- 简述ReentrantLock和Synchronized区别
- synchronized和ReentrantLock的区别
- java的两种同步方式, Synchronized与ReentrantLock的区别
- Synchronized 与 ReentrantLock 的区别与联系
- java synchronized和(ReentrantLock)区别
- synchronized 与 ReentrantLock 的区别
- ReentrantLock和Synchronized的区别
- 多线程 - synchronized 和 ReentrantLock的区别
- Synchronized与ReentrantLock的区别
- Synchronized与ReentrantLock的区别
- volatile、atomic、reentrantLock、synchronized区别详解
- synchronized和ReentrantLock的区别
- synchronized 和 ReentrantLock的区别
- synchronized和lock(reentrantlock) 区别
- ReentrantReadWriteLock & ReentrantLock & Synchronized 区别
- java的两种同步方式, Synchronized与ReentrantLock的区别