1、ReentrantLock 拥有Synchronized相同的并发性和内存语义,此外还多了 锁投票,定时锁等候和中断锁等候
线程A和B都要获取对象O的锁定,假设A获取了对象O锁,B将等待A释放对O的锁定,
如果使用 synchronized ,如果A不释放,B将一直等下去,不能被中断
如果 使用ReentrantLock,如果A不释放,可以使B在等待了足够长的时间以后,中断等待,而干别的事情

ReentrantLock获取锁定与三种方式:
a) lock(), 如果获取了锁立即返回,如果别的线程持有锁,当前线程则一直处于休眠状态,直到获取锁
b) tryLock(), 如果获取了锁立即返回true,如果别的线程正持有锁,立即返回false;
c)tryLock(long timeout,TimeUnit unit), 如果获取了锁定立即返回true,如果别的线程正持有锁,会等待参数给定的时间,在等待的过程中,如果获取了锁定,就返回true,如果等待超时,返回false;
d) lockInterruptibly:如果获取了锁定立即返回,如果没有获取锁定,当前线程处于休眠状态,直到或者锁定,或者当前线程被别的线程中断

2、synchronized是在JVM层面上实现的,不但可以通过一些监控工具监控synchronized的锁定,而且在代码执行时出现异常,JVM会自动释放锁定,但是使用Lock则不行,lock是通过代码实现的,要保证锁定一定会被释放,就必须将unLock()放到finally{}中

3、在资源竞争不是很激烈的情况下,Synchronized的性能要优于ReetrantLock,但是在资源竞争很激烈的情况下,Synchronized的性能会下降几十倍,但是ReetrantLock的性能能维持常态;

5.0的多线程任务包对于同步的性能方面有了很大的改进,在原有synchronized关键字的基础上,又增加了ReentrantLock,以及各种Atomic类。了解其性能的优劣程度,有助与我们在特定的情形下做出正确的选择。

总体的结论先摆出来:

synchronized:
在资源竞争不是很激烈的情况下,偶尔会有同步的情形下,synchronized是很合适的。原因在于,编译程序通常会尽可能的进行优化synchronize,另外可读性非常好,不管用没用过5.0多线程包的程序员都能理解。

ReentrantLock:
ReentrantLock提供了多样化的同步,比如有时间限制的同步,可以被Interrupt的同步(synchronized的同步是不能Interrupt的)等。在资源竞争不激烈的情形下,性能稍微比synchronized差点点。但是当同步非常激烈的时候,synchronized的性能一下子能下降好几十倍。而ReentrantLock确还能维持常态。

Atomic:
和上面的类似,不激烈情况下,性能比synchronized略逊,而激烈的时候,也能维持常态。激烈的时候,Atomic的性能会优于ReentrantLock一倍左右。但是其有一个缺点,就是只能同步一个值,一段代码中只能出现一个Atomic的变量,多于一个同步无效。因为他不能在多个Atomic之间同步。

所以,我们写同步的时候,优先考虑synchronized,如果有特殊需要,再进一步优化。ReentrantLock和Atomic如果用的不好,不仅不能提高性能,还可能带来灾难。

先贴测试结果:再贴代码(Atomic测试代码不准确,一个同步中只能有1个Actomic,这里用了2个,但是这里的测试只看速度)

round:100000 thread:5
Sync = 35301694
Lock = 56255753
Atom = 43467535

round:200000 thread:10
Sync = 110514604
Lock = 204235455
Atom = 170535361

round:300000 thread:15
Sync = 253123791
Lock = 448577123
Atom = 362797227

round:400000 thread:20
Sync = 16562148262
Lock = 846454786
Atom = 667947183

round:500000 thread:25
Sync = 26932301731
Lock = 1273354016
Atom = 982564544

package test.thread;

import static java.lang.System.out;

import java.util.Random;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.locks.ReentrantLock;

public class TestSyncMethods {

public static void test(int round,int threadNum,CyclicBarrier cyclicBarrier){new SyncTest("Sync",round,threadNum,cyclicBarrier).testTime();new LockTest("Lock",round,threadNum,cyclicBarrier).testTime();new AtomicTest("Atom",round,threadNum,cyclicBarrier).testTime();
}public static void main(String args[]){for(int i=0;i<5;i++){int round=100000*(i+1);int threadNum=5*(i+1);CyclicBarrier cb=new CyclicBarrier(threadNum*2+1);out.println("==========================");out.println("round:"+round+" thread:"+threadNum);test(round,threadNum,cb);}
}

}

class SyncTest extends TestTemplate{
public SyncTest(String _id,int _round,int _threadNum,CyclicBarrier _cb){
super( _id, _round, _threadNum, _cb);
}
@Override
/**
* synchronized关键字不在方法签名里面,所以不涉及重载问题
*/
synchronized long getValue() {
return super.countValue;
}
@Override
synchronized void sumValue() {
super.countValue+=preInit[index++%round];
}
}

class LockTest extends TestTemplate{
ReentrantLock lock=new ReentrantLock();
public LockTest(String _id,int _round,int _threadNum,CyclicBarrier _cb){
super( _id, _round, _threadNum, _cb);
}
/**
* synchronized关键字不在方法签名里面,所以不涉及重载问题
*/
@Override
long getValue() {
try{
lock.lock();
return super.countValue;
}finally{
lock.unlock();
}
}
@Override
void sumValue() {
try{
lock.lock();
super.countValue+=preInit[index++%round];
}finally{
lock.unlock();
}
}
}

class AtomicTest extends TestTemplate{
public AtomicTest(String _id,int _round,int _threadNum,CyclicBarrier _cb){
super( _id, _round, _threadNum, _cb);
}
@Override
/**
* synchronized关键字不在方法签名里面,所以不涉及重载问题
*/
long getValue() {
return super.countValueAtmoic.get();
}
@Override
void sumValue() {
super.countValueAtmoic.addAndGet(super.preInit[indexAtomic.get()%round]);
}
}
abstract class TestTemplate{
private String id;
protected int round;
private int threadNum;
protected long countValue;
protected AtomicLong countValueAtmoic=new AtomicLong(0);
protected int[] preInit;
protected int index;
protected AtomicInteger indexAtomic=new AtomicInteger(0);
Random r=new Random(47);
//任务栅栏,同批任务,先到达wait的任务挂起,一直等到全部任务到达制定的wait地点后,才能全部唤醒,继续执行
private CyclicBarrier cb;
public TestTemplate(String _id,int _round,int _threadNum,CyclicBarrier _cb){
this.id=_id;
this.round=_round;
this.threadNum=_threadNum;
cb=_cb;
preInit=new int[round];
for(int i=0;i<preInit.length;i++){
preInit[i]=r.nextInt(100);
}
}

abstract void sumValue();
/** 对long的操作是非原子的,原子操作只针对32位* long是64位,底层操作的时候分2个32位读写,因此不是线程安全*/
abstract long getValue();public void testTime(){ExecutorService se=Executors.newCachedThreadPool();long start=System.nanoTime();//同时开启2*ThreadNum个数的读写线程for(int i=0;i<threadNum;i++){se.execute(new Runnable(){public void run() {for(int i=0;i<round;i++){sumValue();}//每个线程执行完同步方法后就等待try {cb.await();} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();} catch (BrokenBarrierException e) {// TODO Auto-generated catch blocke.printStackTrace();}}});se.execute(new Runnable(){public void run() {getValue();try {//每个线程执行完同步方法后就等待cb.await();} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();} catch (BrokenBarrierException e) {// TODO Auto-generated catch blocke.printStackTrace();}}});}try {//当前统计线程也wait,所以CyclicBarrier的初始值是threadNum*2+1cb.await();} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();} catch (BrokenBarrierException e) {// TODO Auto-generated catch blocke.printStackTrace();}//所有线程执行完成之后,才会跑到这一步long duration=System.nanoTime()-start;out.println(id+" = "+duration);}

}

一、synchronize修饰不同代码都是锁住了什么?
大家都知道synchronize可以修饰属性、代码块,方法、类,但是修饰不同的代码锁住的内容是不同的。
1、修饰非静态属性和方法时,拿到的是调用这个方法或者属性的对象(this)的锁。
2、synchronize()修饰代码块时,拿到的是指定对象的锁。
3、修饰类、静态方法、静态代码块时,由于没有this指针,因此拿到的是类锁,也就是该类的class对象。
!!!注意:一个对象只有一个锁

二、关于synchronize
由于synchronize是由JVM实现的,因此当加锁代码出现异常时,对象锁可以由JVM释放,包含以下三种情况:
1、 占有锁的线程执行完了代码块,然后释放对锁的占有;
2、 占有锁的线程发生了异常,此时JVM会让线程自动释放锁;
3、 占有锁的线程调用了wait()方法,从而进入了WAITING状态需要释放锁。

三、关于Lock

由于Lock是由JDK实现的,所以不像synchronize锁的获取和释放都是由JVM控制的,Lock的获取和释放都需要手动进行,并且在发生异常时,不会自动释放锁。因此一般来说,使用Lock必须在try{}catch{}块中进行,并且将释放锁的操作放在finally块中进行,以保证锁一定被被释放,防止死锁的发生。

1、lock() 获取锁与释放锁 ,如果锁已被其他线程获取,则进行等待。

复制代码
1 Lock lock = …; lock.lock();
2 try{
3    //处理任务
4 }catch(Exception ex){
5 }finally{
6 lock.unlock(); //释放锁
7 }
复制代码

2、tryLock() 获取锁时有返回值可以得知获取锁操作是否成功

复制代码
1 Lock lock = …;
2 if(lock.tryLock()) {
3 try{
4 //处理任务
5    }catch(Exception ex){
6 }finally{
7 lock.unlock(); //释放锁
8   }
9 }else {
10 //如果不能获取锁,则直接做其他事情
11 }
复制代码
如果获取成功则返回True,如果锁被其他线程占用则返回FALSE,该方法会立即返回结果,不会让线程一直处于等待状态。

3、tryLock(long time,TimeUnit unit) 与tryLock() 类似,但是与tryLock立即返回结果不同,该方法在拿不到锁的情况下回等待time时间,如果在限定时间内还是拿不到锁就返回FALSE,如果在一开始或者等待时间内拿到锁则返回TRUE。

4、lockInterruptibly()

通过这个方法尝试获取锁时,如果线程正在等待获取锁,则该线程可以响应Thread.interrupt()中断。synchronize对于没有获得锁处于等待状态的线程无法响应中断。

1 public void method() throws InterruptedException {
2 lock.lockInterruptibly();
3 try { //… }
4 finally { lock.unlock(); }
5 }
lockInterruptibly方法必须放在try块中或者在调用lockInterruptibly的方法外声明抛出InterruptedException,推荐使用后者。

5、readWriteLock()

该锁提升了读操作的效率,不过要注意的是,如果有一个线程已经占用了读锁,则此时其他线程如果要申请写锁,则申请写锁的线程会一直等待释放读锁。如果有一个线程已经占用了写锁,则此时其他线程如果申请写锁或者读锁,则申请的线程也会一直等待释放写锁。

四、Lock和synchronized的选择:
1、 Lock是一个接口,属于JDK层面的实现;而synchronized属于Java语言的特性,其实现有JVM来控制(代码执行完毕,出现异常,wait时JVM会主动释放锁)。
2、 synchronized在发生异常时,会自动释放掉锁,故不会发生死锁现(此时的死锁一般是代码逻辑引起的);而Lock必须在finally中主动unlock锁,否则就会出现死锁。
3、 Lock能够响应中断,让等待状态的线程停止等待;而synchronized不行。
4、 通过Lock可以知道线程是否成功获得了锁,而synchronized不行。
5、 Lock提高了多线程下对读操作的效率。

五、扩展
1、可重入锁:synchronized和ReentrantLock都是可重入锁。当一个线程执行到某个synchronized方法时,比如说method1,而在method1中会调用另外一个synchronized方法method2,此时线程不必重新去申请锁,而是可以直接执行方法method2。

2、可中断锁:等待获得锁的等待过程是否可以中断。通过上面的例子,我们可以得知Lock是可中断锁,而synchronized不是。

3、公平锁:尽量以请求的顺序来获取锁,同是有多个线程在等待一个锁,当这个锁被释放时,等待时间最久的线程(最先请求的线程)会获得该锁。synchronized是非公平锁,而ReentrantLock和ReentReadWriteLock默认情况下是非公平锁,但是可以设置成公平锁。
ReentrantLock lock = new ReentrantLock(true);
ReentrantReadWriteLock lock = new ReentrantReadWriteLock(true);
设置为TRUE即为公平锁,为FALSE或者无参数为非公平锁。

4、读写锁:读写锁将对临界资源的访问分成了两个锁,一个读锁和一个写锁。增加读写灵活性。即ReadWriteLock接口及其实现ReentrantReadWriteLock。

Lock与synchronized 的区别相关推荐

  1. Java同步锁——lock与synchronized 的区别【转】

    在网上看来很多关于同步锁的博文,记录下来方便以后阅读 一.Lock和synchronized有以下几点不同: 1)Lock是一个接口,而synchronized是Java中的关键字,synchroni ...

  2. lock和synchronized的区别

    lock是一个接口,而synchronized是java的一个关键字,synchronized是内置的语言实现:(具体实现上的区别在<Java虚拟机>中有讲解底层的CAS不同,以前有读过现 ...

  3. Lock与synchronized测试区别

    原文:http://www.cnblogs.com/nsw2018/p/5821738.html 1.ReentrantLock 拥有Synchronized相同的并发性和内存语义,此外还多了 锁投票 ...

  4. java多线程:9、synchronized、Lock的底层实现原理以及和volatile、Lock、ReentrantLock的区别?

    文章目录 0.1.线程中安全性问题的体现: 0.2.线程安全问题的解决办法 1.synchronized的底层实现原理分析 2.Lock的底层实现原理分析? 3.synchronized和volati ...

  5. lock与synchronized锁有什么区别?它们的底层实现原理是什么?

    一.共同点 Lock和synchronized都是本地锁,它们都可以通过上锁解决多个线程访问共享资源的问题,并且synchronized和lock锁都支持可重入锁机制,即同一个线程在已经获得锁的情况下 ...

  6. Lock与Synchronized区别

    先说结论,后面详解 synchronized是关键字,Lock是接口; synchronized是隐式的加锁,lock是显式的加锁; synchronized可以作用于方法上,lock只能作用于方法块 ...

  7. Lock和synchronized比较详解

    原文:http://www.cnblogs.com/handsomeye/p/5999362.html 今天看了并发实践这本书的ReentantLock这章,感觉对ReentantLock还是不够熟悉 ...

  8. Java并发编程:Lock和Synchronized 转

    在上一篇文章中我们讲到了如何使用关键字synchronized来实现同步访问.本文我们继续来探讨这个问题,从Java 5之后,在java.util.concurrent.locks包下提供了另外一种方 ...

  9. Lock接口Condition,以及Lock与synchronized异同

    一.synchronized与Lock 基于synchronized关键字去实现同步的,(jvm内置锁,也叫隐式锁,由我们的jvm自动去加锁跟解锁的)juc下的基于Lock接口的这样的一种锁的实现方式 ...

最新文章

  1. 自研芯片架构 ,这家中国公司发布DPU芯片计划
  2. Oracle Database 11.2.0.1(静默安装)
  3. 5年後、10年後の自分のイメージ
  4. 中国量化投资将呈现三大发展趋势
  5. 各种Oracle常见操作2
  6. Linux中的Kdump
  7. GraphPad Prism 9.0.2 for MacOS 2021最新完美版科研绘图统计软件 附安装使用教程
  8. 计算机网络(二)-性能指标
  9. Hbase+Phoenix使用总结
  10. 贵州大学计算机网络试题,贵州大学计算机基础考试题库.doc
  11. 十分钟学懂Python入门基础3(中)
  12. 【渝粤教育】国家开放大学2018年秋季 2517T社区工作 参考试题
  13. 跨步电压和接触电压的区别及联系
  14. CentOS 7 虚拟机网卡失效问题:ens33:<NO-CARRIER,BROADCAST,MULTICAST,UP>mtu 1508 gdisc pf ifo_fast state DOWN
  15. android应用市场汇总
  16. Aria2使用详细教程
  17. [ATF] ARM Trusted firmware 构建选项
  18. Python入门学习(五)
  19. 关于vs在.cpp提取头文件运行的时候一直报错“xx函数主体重定义”
  20. 资产负债表与利润表钩稽关系

热门文章

  1. nginx 超时时间_我眼中的 Nginx(四):是什么让你的 Nginx 服务退出这么慢?
  2. 简单的全连接神经网络(tensorflow实现)
  3. Mac~git学习和应用需要注意的几个点
  4. Tableau实战系列Tableau基础概念全解析 (二)-万字长文解析数据类型及数据集
  5. MATLAB实战系列(二十七)-数据预处理-PCA主成分分析
  6. 完美解决mysql下utf-8的乱码问题
  7. c语言数字的拆解_C语言解决变态推理题
  8. C++学习路线(最全资源整合)
  9. numpy.argmax详解
  10. 由mysql分区想到的分表分库的方案