主要区别

  1. 锁机制不一样:synchronized是java内置关键字,是在JVM层面实现的,系统会监控锁的释放与否,lock是JDK代码实现的,需要手动释放,在finally块中释放。可以采用非阻塞的方式获取锁;

  2. 性能不一样:资源竞争激励的情况下,lock性能会比synchronize好,竞争不激励的情况下,synchronize比lock性能好,synchronize会根据锁的竞争情况,从偏向锁-->轻量级锁-->重量级锁升级,而且编程更简单

  3. synchronized无法判断是否获取锁的状态,Lock可以判断是否获取到锁;

  4. synchronized会自动释放锁(a 线程执行完同步代码会释放锁 ;b 线程执行过程中发生异常会释放锁),Lock需在finally中手工释放锁(unlock()方法释放锁),否则容易造成线程死锁;

  5. 用synchronized关键字的两个线程1和线程2,如果当前线程1获得锁,线程2线程等待。如果线程1阻塞,线程2则会一直等待下去,而Lock锁就不一定会等待下去,如果尝试获取不到锁,线程可以不用一直等待就结束了;

  6. synchronized的锁可重入、不可中断、非公平,而Lock锁可重入、可判断、可公平(两者皆可)

  7. 用法不一样:synchronize可以用在代码块上,方法上。lock只能写在代码里,不能直接修改方法。

  8. Lock可以绑定多个condition。例如,ReentrantLock用来实现分组唤醒需要唤醒的线程们,可以精确唤醒,而不是像synchronized要么随机唤醒一个,要么唤醒全部线程。

分别讨论synchronized、Lock

一:synchronized

加同步格式: 

  synchronized(需要一个任意的对象(锁)){

  代码块中放操作共享数据的代码;

  }

synchromized缺陷

  synchronized是java中的一个关键字,也就是说是java语言的内置的特性。

如果一个代码块被synchronized修饰,当一个线程获取了对应的锁,并执行代码块时,其他线程只能一直等待,等待获取锁的线程释放锁,而这里获取锁的线程释放锁只会有两种情况:

(1)获取锁的线程执行完了改代码块,然后线程释放对锁的占有

(2)线程执行发生异常,此时JVM会让线程自动释放锁。

例子1:

  如果这个获取锁的线程由于要等待IO或者其他原因(比如调用sleep方法)被阻塞了,但是又没有释放锁,其他线程便只能干巴巴地等待,试想一下,这多么影响程序执行效率。

  因此就需要有一种机制可以不让等待的线程一直无期限地等待下去(比如只等待一定的时间或者能够响应中断),通过Lock就可以办到。

例子2:

  当有多个线程读写文件时,读操作和写操作会发生冲突现象,写操作和写操作会发生冲突现象,但是读操作和读操作不会发生冲突现象。

  但是采用synchronized关键字来实现同步的话,就会导致一个问题:如果多个线程都只是进行读操作,当一个线程在进行读操作时,其他线程只能等待无法进行读操作。

  因此就需要一种机制来使得多个线程都只是进行读操作时,线程之间不会发生冲突,通过Lock就可以办到。

  另外,通过Lock可以知道线程有没有成功获取到锁。这个是synchronized无法办到的。

  总的来说,也就是说Lock提供了比synchronized更多的功能。

二:LOCK

首先要说明的就是,通过查看LOCK 的源码可知道,lock是一个接口

lock接口中每个方法的使用:lock()、tryLock()、tryLock(long time, TimeUnit unit)、lockInterruptibly()是用来获取锁的。 unLock()方法是用来释放锁的。 四个获取锁方法的区别:

(1)lock()方法时平常使用的最多的一个方法,就是用来获取锁的,如果锁已经被其他线程获取,则进行等待。

由于在前面讲到如果采用lock,必须主动去释放锁,并且在发生异常时,不会自动释放锁。因此一般来说,使用lock必须在try{}catch{}块中进行,并且将释放锁的操作放在finally块中进行,以保证锁一定被释放掉,防止死锁的发生。

(2)tryLock()方法是有返回值的,他表示用来尝试获取锁,如果获取成功,则返回true,如果获取失败(即锁已经被其他线程获取),则返回false,也就说这个方法无论如何都会立即返回。在拿不到锁时不会一直在哪里等待

(3)tryLock(long time, TimeUnit unit)方法和tryLock()方法时类似的,只不过区别在于这个方法在拿不到锁时会等待一定时间,在时间限制之内如果还是拿不到锁,就返回false。如果一开始就拿到锁或者在等待期间内拿到了锁,则就返回true。

(4)lockinterruptibly()方法比较特殊,当通过这个方法区获取锁时,如果线程正在等待获取锁,则这个线程能够相应中断,即中断线程的等待状态。也就是说,当连个线程同时通过lock.lockinterruputibly()向获取某个锁时,假如此时线程A获取到了锁,而线程B只有等待,那么对线程调用threadB.interrupt()方法能够中断线程B的等待过程。

注意:当一个线程获取了锁之后,是不会被interrupt()方法中断的

因此当通过lockinterruptibly()方法获取某个锁时,如果不能获取到,只有进行等待的情况下,是可以相应中断的

而用synchronized修饰的话,当一个线程处于等待某个锁的状态,是无法被中断的,只有一直等待下去。

ReentrantLock

  直接使用lock接口的话,我们需要实现很多方法,不太方便,ReentrantLock是唯一实现了Lock接口的类,并且ReentrantLock提供了更多的方法,ReentrantLock,意思是“可重入锁”。

  ReadWriteLock也是一个接口,在它里面只定义了两个方法:

    一个用来获取读锁,一个用来获取写锁。也就是说将文件的读写操作分开,分成2个锁来分配给线程,从而使得多个线程可以同时进行读操作。ReentrantReadWriteLock实现了ReadWriteLock接口。

ReentrantReadWriteLock

  ReentrantReadWriteLock里面提供了很多丰富的方法,不过最主要的两个方法:readlock()和writelock用来获取读锁和写锁

注意:

  不过要注意的是,如果有一个线程已经占用了读锁,则此时其他线程如果要申请写锁,则申请写锁的线程会一直等待释放读锁。

  如果有一个线程已经占用了写锁,则此时其他线程如果申请写锁或者读锁,则申请的线程会一直等待释放写锁。

LOCK和SYNCHRONIZED的选择

  (1)lock是一个接口,而synchronized是java的关键字,synchronized是内置的语言实现;

  (2)synchronized在发生异常时,会自动释放线程占有的锁,因此不会导致死锁现象发生;而lock在发生异常时,如果没有主动通过unlock()去释放锁,则很可能造成死锁现象,因此使用lock()时需要在finally块中释放锁;

  (3)lock可以让等待锁的线程响应中断,而synchronized却不行,使用synchronized时,等待的线程会一直等待下去,不讷讷狗狗响应中断

  (4)通过lock可以知道有没有成功获取锁,而synchronized却无法办到

  (5)lock可以提高多个线程进行读操作的效率

  

  在性能上来说,如果竞争资源不激烈,两者的性能是差不多的,而竞争资源非常激烈是(既有大量线程同时竞争),此时lock的性能要远远优于synchronized。所以说,在具体使用时适当情况选择。

一些小例子:

package com.cn.test.thread.lock;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock; public class LockTest {
private Lock lock = new ReentrantLock();
/*
* 使用完毕释放后其他线程才能获取锁
*/
public void lockTest(Thread thread) {
lock.lock();//获取锁
try {
System.out.println("线程"+thread.getName() + "获取当前锁"); //打印当前锁的名称
Thread.sleep(2000);//为看出执行效果,是线程此处休眠2秒
} catch (Exception e) {
System.out.println("线程"+thread.getName() + "发生了异常释放锁");
}finally {
System.out.println("线程"+thread.getName() + "执行完毕释放锁");
lock.unlock(); //释放锁
}
} public static void main(String[] args) {
LockTest lockTest = new LockTest();
//声明一个线程 “线程一”
Thread thread1 = new Thread(new Runnable() {
@Override
public void run() {
lockTest.lockTest(Thread.currentThread());
}
}, "thread1");
//声明一个线程 “线程二”
Thread thread2 = new Thread(new Runnable() { @Override
public void run() {
lockTest.lockTest(Thread.currentThread());
}
}, "thread2");
// 启动2个线程
thread2.start();
thread1.start(); }
}

执行结果:

  

package com.cn.test.thread.lock;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock; public class LockTest {
private Lock lock = new ReentrantLock(); /*
* 尝试获取锁 tryLock() 它表示用来尝试获取锁,如果获取成功,则返回true,如果获取失败(即锁已被其他线程获取),则返回false
*/
public void tryLockTest(Thread thread) {
if(lock.tryLock()) { //尝试获取锁
try {
System.out.println("线程"+thread.getName() + "获取当前锁"); //打印当前锁的名称
Thread.sleep(2000);//为看出执行效果,是线程此处休眠2秒
} catch (Exception e) {
System.out.println("线程"+thread.getName() + "发生了异常释放锁");
}finally {
System.out.println("线程"+thread.getName() + "执行完毕释放锁");
lock.unlock(); //释放锁
}
}else{
System.out.println("我是线程"+Thread.currentThread().getName()+"当前锁被别人占用,我无法获取");
}
}
public static void main(String[] args) {
LockTest lockTest = new LockTest(); Thread thread1 = new Thread(new Runnable() {
@Override
public void run() {
lockTest.tryLockTest(Thread.currentThread());
}
}, "thread1");
//声明一个线程 “线程二”
Thread thread2 = new Thread(new Runnable() { @Override
public void run() {
lockTest.tryLockTest(Thread.currentThread());
}
}, "thread2");
// 启动2个线程
thread2.start();
thread1.start(); }
}

执行结果:

package com.cn.test.thread.lock;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock; public class LockTest {
private Lock lock = new ReentrantLock();
public void tryLockParamTest(Thread thread) throws InterruptedException {
if(lock.tryLock(3000, TimeUnit.MILLISECONDS)) { //尝试获取锁 获取不到锁,就等3秒,如果3秒后还是获取不到就返回false
try {
System.out.println("线程"+thread.getName() + "获取当前锁"); //打印当前锁的名称
Thread.sleep(4000);//为看出执行效果,是线程此处休眠2秒
} catch (Exception e) {
System.out.println("线程"+thread.getName() + "发生了异常释放锁");
}finally {
System.out.println("线程"+thread.getName() + "执行完毕释放锁");
lock.unlock(); //释放锁
}
}else{
System.out.println("我是线程"+Thread.currentThread().getName()+"当前锁被别人占用,等待3s后仍无法获取,放弃");
}
}
public static void main(String[] args) {
LockTest lockTest = new LockTest();
Thread thread1 = new Thread(new Runnable() {
@Override
public void run() {
try {
lockTest.tryLockParamTest(Thread.currentThread());
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}, "thread1");
//声明一个线程 “线程二”
Thread thread2 = new Thread(new Runnable() {
@Override
public void run() {
try {
lockTest.tryLockParamTest(Thread.currentThread());
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}, "thread2");
// 启动2个线程
thread2.start();
thread1.start();
}
}

执行结果:

因为此时线程1休眠了4秒,线程2等待了3秒还没有获取到就放弃获取锁了,执行结束

将方法中的 Thread.sleep(4000)改为Thread.sleep(2000)执行结果如下:

因为此时线程1休眠了2秒,线程2等待了3秒的期间线程1释放了锁,此时线程2获取到锁,线程2就可以执行了

题目:多线程之间按找顺序调用,实现A->B->C三个线程启动,要求如下

  AA打印5次,BB打印10次,CC打印15次,重复上述过程10次.

class ShareResource{
private int number = 1; // A:1, B:2, C:3
private Lock lock = new ReentrantLock();
private Condition conditionA = lock.newCondition();
private Condition conditionB = lock.newCondition();
private Condition conditionC = lock.newCondition();
public void print5(){
try {
lock.lock();
while (number != 1){
conditionA.await();
} for (int i = 1; i <= 5; i++){
System.out.print("A");
}
System.out.println();
number++;
conditionB.signal();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void print10(){
try {
lock.lock();
while (number != 2){
conditionB.await();
} for (int i = 1; i <= 10; i++){
System.out.print("B");
}
System.out.println();
number++;
conditionC.signal();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
} public void print15(){
try {
lock.lock();
while (number != 3){
conditionC.await();
} for (int i = 1; i <= 15; i++){
System.out.print("C");
}
System.out.println();
number = 1;
conditionA.signal();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
public class SynchronizedLockDifference { public static void main(String[] args) {
ShareResource shareResource = new ShareResource();
new Thread(()->{
for (int i = 1; i <= 10; i++){
shareResource.print5();
}
}, "A").start(); new Thread(()->{
for (int i = 1; i <= 10; i++){
shareResource.print10();
}
}, "B").start(); new Thread(()->{
for (int i = 1; i <= 10; i++){
shareResource.print15();
}
}, "C").start(); }
}

输出结果:

AAAAA
BBBBBBBBBB
CCCCCCCCCCCCCCC
AAAAA
BBBBBBBBBB
CCCCCCCCCCCCCCC
AAAAA
BBBBBBBBBB
CCCCCCCCCCCCCCC
AAAAA
BBBBBBBBBB
CCCCCCCCCCCCCCC
AAAAA
BBBBBBBBBB
CCCCCCCCCCCCCCC
AAAAA
BBBBBBBBBB
CCCCCCCCCCCCCCC
AAAAA
BBBBBBBBBB
CCCCCCCCCCCCCCC
AAAAA
BBBBBBBBBB
CCCCCCCCCCCCCCC
AAAAA
BBBBBBBBBB
CCCCCCCCCCCCCCC
AAAAA
BBBBBBBBBB
CCCCCCCCCCCCCCC Process finished with exit code 0

java基础: synchronized与Lock的区别的更多相关文章

  1. Java中synchronized和Lock的区别

    synchronized和Lock的区别synchronize锁对象可以是任意对象,由于监视器方法必须要拥有锁对象那么任意对象都可以调用的方法所以将其抽取到Object类中去定义监视器方法这样锁对象和 ...

  2. 【Java】synchronized与lock的区别

    从Java 5之后,在java.util.concurrent.locks包下提供了另外一种方式来实现同步访问,那就是Lock. 也许有朋友会问,既然都可以通过synchronized来实现同步访问了 ...

  3. Java 多线程 - synchronized与Lock的区别

    https://blog.csdn.net/qq_39521554/article/details/81130442 http://www.cnblogs.com/huangbw/p/8516024. ...

  4. Java synchronized和 Lock 的区别与用法

    在分布式开发中,锁是线程控制的重要途径.Java为此也提供了2种锁机制,synchronized和lock.做为Java爱好者,自然少不了对比一下这2种机制,也能从中学到些分布式开发需要注意的地方.  ...

  5. java - synchronized与lock的区别

    synchronized与lock的区别 原始构成 synchronized是关键字属于JVM层面 monitorenter(底层是通过monitor对象来完成,其实wait/notify等对象也依赖 ...

  6. 详解synchronized与Lock的区别与使用

    知识点 1.线程与进程 在开始之前先把进程与线程进行区分一下,一个程序最少需要一个进程,而一个进程最少需要一个线程.关系是线程–>进程–>程序的大致组成结构.所以线程是程序执行流的最小单位 ...

  7. (转)synchronized和lock的区别

    背景:最近在准备java基础知识,对于可重入锁一直没有个清晰的认识,有必要对这块知识进行总结. 1 . 什么是可重入锁 锁的概念就不用多解释了,当某个线程A已经持有了一个锁,当线程B尝试进入被这个锁保 ...

  8. Synchronized与Lock的区别与应用场景

    转载. https://blog.csdn.net/fly910905/article/details/79765381 同步代码块,同步方法,或者是用java提供的锁机制,我们可以实现对共享资源变量 ...

  9. 同步锁Synchronized与Lock的区别?

    synchronized与Lock两者区别: 1:Lock是一个接口,而Synchronized是关键字. 2:Synchronized会自动释放锁,而Lock必须手动释放锁. 3:Lock可以让等待 ...

  10. Synchronized和lock的区别和用法

    一.synchronized和lock的用法区别 (1)synchronized(隐式锁):在需要同步的对象中加入此控制,synchronized可以加在方法上,也可以加在特定代码块中,括号中表示需要 ...

随机推荐

  1. hearthbuddy中的Class276

    构造函数 需要注意的是this.intptr_0 = this.method_18("mono.dll"); 所以,这个类里面的操作,最后是和mono.dll相关的 interna ...

  2. GitHub-Microsoft:DotNet3

    ylbtech-GitHub-Microsoft:DotNet3 1.返回顶部 · mbmlbook Sample code for the Model-Based Machine Learning ...

  3. C++学习 之 初识变量和基本类型 之 内置类型

    声明:            本人自学C++, 没有计算机基础,在学习的过程难免会出现理解错误,出现风马牛不相及的现象,甚至有可能会贻笑大方. 如果有幸C++大牛能够扫到本人的博客,诚心希望大牛能给予 ...

  4. 过滤器demo-编码统一处理

    package com.loaderman.demo.a_loginFilter; import java.io.IOException; import java.lang.reflect.Invoc ...

  5. 转: Android 设备的远程调试入门

    从 Windows.Mac 或 Linux 计算机远程调试 Android 设备上的实时内容. 本教程将向您展示如何: 设置您的 Android 设备进行远程调试,并从开发计算机上发现设备. 从您的开 ...

  6. 7NiuYun云存储UploadPicture

    1.七牛云云存储图片,上传.下载.源代码地址:ssh (git@gitee.com:516877626/QiNiuYunUploadpicture.git) .https(https://gitee. ...

  7. 【经验分享】Mongodb操作类实现CRUD

    一.背景 公司项目中在做数据存储时使用到Mongodb,所以想着将Mongodb的操作封装后可供项目中其他成员方便使用. 附上Mongodb的下载地址: 下载 1.Mongodb类 此类主要是用来构造 ...

  8. HTML中Data的数据类型

    "data"类型的Url格式,是在RFC2397中提出的,目的对于一些“小”的数据,可以在网页中直接嵌入,而不是从外部文件载入. 例如对于img这个Tag,哪怕这个图片非常非常的小 ...

  9. Django-ORM外键属性总结

    ForeignKey ForeignKey(ForeignObject) # ForeignObject(RelatedField) to, # 要进行关联的表名 to_field=None, # 要 ...

  10. Leetcode之广度优先搜索(BFS)专题-1162. 地图分析(As Far from Land as Possible)

    Leetcode之广度优先搜索(BFS)专题-1162. 地图分析(As Far from Land as Possible) BFS入门详解:Leetcode之广度优先搜索(BFS)专题-429. ...