java基础: synchronized与Lock的区别

主要区别

  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

原文地址:https://www.cnblogs.com/myseries/p/11824568.html

时间: 2024-07-29 08:40:32

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

Java中synchronized和Lock的区别

synchronized和Lock的区别synchronize锁对象可以是任意对象,由于监视器方法必须要拥有锁对象那么任意对象都可以调用的方法所以将其抽取到Object类中去定义监视器方法这样锁对象和监视器对象是同一个,只要创建了锁对象它既是锁对象同时也是监视器对象这样不能实现在一个锁对象上绑定多个监视器对象jdk1.5中Lock对象仅仅是一个锁对象监视器方法被封装到了Condition对象中这样实现了锁对象和监视器对象进行了分离更加的面向对象这样可以实现在一个锁对象上绑定多个监视器对象 在一个

java - synchronized与lock的区别

synchronized与lock的区别 原始构成 synchronized是关键字属于JVM层面 monitorenter(底层是通过monitor对象来完成,其实wait/notify等对象也依赖于monitor独享只有在同步块或方法中才能调wait/notify等方法) monitorexit Lock是具体类(java.utl.concurrent.locks.Lock)是api层面的锁 使用方法 synchronized不需要用户去手动释放锁,当synchronized代码执行完后系统

Synchronized和lock的区别和用法

一.synchronized和lock的用法区别 (1)synchronized(隐式锁):在需要同步的对象中加入此控制,synchronized可以加在方法上,也可以加在特定代码块中,括号中表示需要锁的对象. (2)lock(显示锁):需要显示指定起始位置和终止位置.一般使用ReentrantLock类做为锁,多个线程中必须要使用一个ReentrantLock类做为对 象才能保证锁的生效.且在加锁和解锁处需要通过lock()和unlock()显示指出.所以一般会在finally块中写unloc

synchronized和lock的区别和使用

---恢复内容开始--- 1.线程与进程 一个程序至少需要一个线程,一个进程至少需要一个线程  线程->进程->程序 线程是程序执行流的最小单位,进程是系统进行资源分配和调度的一个独立单位. 2.Thread的几个重要方法 ①start()方法:开始执行该线程 ②stop()方法:强制结束该线程 ③join()方法 :等待该线程结束 ④sleep()方法:该线程进入等待 ⑤run()方法    :直接执行该线程的run方法(线程调用start()也会执行run方法,区别是一个是由线程调度运行r

synchronized 与 lock 的区别

synchronized 和 lock 的用法区别 synchronized(隐式锁):在需要同步的对象中加入此控制,synchronized 可以加在方法上,也可以加在特定代码块中,括号中表示需要锁的对象. lock(显示锁):需要显示指定起始位置和终止位置.一般使用 ReentrantLock 类做为锁,多个线程中必须要使用一个 ReentrantLock 类做为对象才能保证锁的生效.且在加锁和解锁处需要通过 lock() 和 unlock() 显示指出.所以一般会在 finally 块中写

Java基础-synchronized关键字的用法(转载)

原文地址:http://blog.csdn.net/cq361106306/article/details/38736551 synchronized--同步 顾名思义是用于同步互斥的作用的. 这里精简的记一下它的使用方法以及意义: 当synchronized修饰?this或者非静态方法或者是一个实例的时候,所同步的锁是加在this或者实例对象引用上面的.比如a,b同为Main类的实例化对象,a调用被同步的方法,和b调用被同步的方法,没有形成互斥.但是不同线程的a对象调用被同步的方法就被互斥了.

深入Synchronized和java.util.concurrent.locks.Lock的区别详解

主要相同点:Lock能完成Synchronized所实现的所有功能.主要不同点:Lock有比Synchronized更精确的线程予以和更好的性能.Synchronized会自动释放锁,但是Lock一定要求程序员手工释放,并且必须在finally从句中释放.synchronized 修饰方法时 表示同一个对象在不同的线程中 表现为同步队列如果实例化不同的对象 那么synchronized就不会出现同步效果了.1.对象的锁 所有对象都自动含有单一的锁. JVM负责跟踪对象被加锁的次数.如果一个对象被

java中synchronized与lock的理解与应用

总结来说,Lock和synchronized有以下几点不同: 1)Lock是一个接口,而synchronized是Java中的关键字,synchronized是内置的语言实现: 2)synchronized在发生异常时,会自动释放线程占有的锁,因此不会导致死锁现象发生:而Lock在发生异常时,如果没有主动通过unLock()去释放锁,则很可能造成死锁现象,因此使用Lock时需要在finally块中释放锁: 3)Lock可以让等待锁的线程响应中断,而synchronized却不行,使用synchr

synchronized与Lock的区别

两者区别: 1.首先synchronized是java内置关键字,在jvm层面,Lock是个java类: 2.synchronized无法判断是否获取锁的状态,Lock可以判断是否获取到锁: 3.synchronized会自动释放锁(a 线程执行完同步代码会释放锁 :b 线程执行过程中发生异常会释放锁),Lock需在finally中手工释放锁(unlock()方法释放锁),否则容易造成线程死锁: 4.用synchronized关键字的两个线程1和线程2,如果当前线程1获得锁,线程2线程等待.如果