zoukankan      html  css  js  c++  java
  • Synchronized和Lock的区别

    引言

      在多线程中,为了使线程安全,我们经常会使用synchronized和Lock进行代码同步和加锁,但是具体两者有什么区别,什么场景下适合用什么可能还不大清楚,主要的区别大致如下:

    区别

        1、synchronized是java关键字,而Lock是java中的一个接口

        2、synchronized会自动释放锁,而Lock必须手动释放锁

        3、synchronized是不可中断的,Lock可以中断也可以不中断

       4、通过Lock可以知道线程有没有拿到锁,而synchronized不能

        5、synchronized能锁住方法和代码块,而Lock只能锁住代码块

        6、Lock可以使用读锁提高多线程读效率

        7、synchronized是非公平锁,ReentranLock可以控制是否公平锁

        起初 java 中只有 synchronized 这一种对程序加锁的方式,在 java SE5 之后加入了 Lock 接口(以及相关实现类)用来实现锁的功能,它提供了与 synchronized 关键字类似的同步功能,只是在使用时需要显式的获取和释放锁,两者的逻辑结构如下。
     1 Object objlock = new Object();
     2 synchronized(objlock) { //加锁
     3     //... 逻辑 ...
     4 } //解锁
     5 
     6 
     7 Lock lock = new ReentrantLock();
     8 lock.lock(); //加锁
     9 try {
    10     //... 逻辑 ...
    11 } finally {
    12     lock.unlock(); //解锁
    13 }

    从Lock接口中我们可以看到主要有5个方法,这些方法的功能从注释中可以看出:

    1 lock():获取锁,如果锁被暂用则一直等待 
    2 unlock():释放锁 
    3 tryLock(): 注意返回类型是boolean,如果获取锁的时候锁被占用就返回false,否则返回true
    4 tryLock(long time, TimeUnit unit):比起tryLock()就是给了一个时间期限,保证等待参数时间 
    5 lockInterruptibly():用该锁的获得方式,如果线程在获取锁的阶段进入了等待,那么可以中断此线程,先去做别的事   通过 以上的解释,大致可以解释在上个部分中“锁类型(lockInterruptibly())”,“锁状态(tryLock())”等问题,还有就是前面子所获取的过程我所写的“大致就是可以尝试获得锁,线程可以不会一直等待”用了“可以”的原因。

    lock():

     1 public class LockTest {
     2     private Lock lock = new ReentrantLock();
     3 
     4     private void method(Thread thread) {
     5         lock.lock();
     6         try {
     7             System.out.println(thread.getName() + " has gotten the lock!");
     8         } catch (Exception e) {
     9             e.printStackTrace();
    10         } finally {
    11             System.out.println(thread.getName() + " has unlocked the lock!");
    12             lock.unlock();
    13         }
    14     }
    15 
    16     public static void main(String[] args) {
    17         final LockTest test = new LockTest();
    18 
    19         Thread t1 = new Thread(new Runnable() {
    20             @Override
    21             public void run() {
    22                 test.method(Thread.currentThread());
    23             }
    24         }, "t1");
    25         Thread t2 = new Thread(new Runnable() {
    26             @Override
    27             public void run() {
    28                 test.method(Thread.currentThread());
    29             }
    30         }, "t2");
    31         t1.start();
    32         t2.start();
    33     }
    34 
    35 }

    运行结果:

    t1 has gotten the lock!
    t1 has unlocked the lock!
    t2 has gotten the lock!
    t2 has unlocked the lock!

    tryLock():

     1 public class LockTest {
     2     private Lock lock = new ReentrantLock();
     3 
     4     private void method(Thread thread) {
     5     
     6         if (lock.tryLock()) {
     7             lock.lock();
     8             try {
     9                 System.out.println(thread.getName() + " has gotten the lock!");
    10             } catch (Exception e) {
    11                 e.printStackTrace();
    12             } finally {
    13                 System.out.println(thread.getName() + " has unlocked the lock!");
    14                 lock.unlock();
    15             }
    16         } else {
    17             System.out.println("I'm "+thread.getName()+". Someone has gotten the lock!");
    18         }
    19     }
    20 
    21     public static void main(String[] args) {
    22         LockTest test = new LockTest();
    23 
    24         Thread t1 = new Thread(() -> test.method(Thread.currentThread()), "t1");
    25         Thread t2 = new Thread(new Runnable() {
    26             @Override
    27             public void run() {
    28                 test.method(Thread.currentThread());
    29             }
    30         }, "t2");
    31         t1.start();
    32         t2.start();
    33     }
    34 }

    运行结果:

    t1 has gotten the lock!
    t1 has unlocked the lock!
    I'm t2. Someone has gotten the lock!

    看到这里相信大家也都会使用如何使用Lock了吧,关于tryLock(long time, TimeUnit unit)和lockInterruptibly()不再赘述。前者主要存在一个等待时间,在测试代码中写入一个等待时间,后者主要是等待中断,会抛出一个中断异常,常用度不高,喜欢探究可以自己深入研究。

    • 公平锁:是指多个线程按照申请锁的顺序来获取锁。
    • 非公平锁:是指多个线程获取锁的顺序并不是按照申请锁的顺序,有可能后申请的线程比先申请的线程优先获取锁。有可能,会造成优先级反转或者饥饿现象。
    • 可重入锁:是指可重复可递归调用的锁,在外层使用锁之后,在内层仍然可以使用,并且不发生死锁(前提是同一个对象或者class),这样的锁就叫做可重入锁。Lock和synchronized都是可重入锁
    • 独享锁 :该锁每一次只能被一个线程所持有。
    • 共享锁 :该锁可被多个线程共有,典型的就是ReentrantReadWriteLock里的读锁,它的读锁是可以被共享的,但是它的写锁确每次只能被独占。
    • 互斥锁 :在访问共享资源之前对其进行加锁操作,在访问完成之后进行解锁操作。 加锁后,任何其他试图再次加锁的线程会被阻塞,直到当前进程解锁。
    • 读写锁 :读写锁既是互斥锁,又是共享锁,read模式是共享,write是互斥(排它锁)的
    • 乐观锁:总是假设最坏的情况,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会阻塞直到它拿到锁
    • 悲观锁:总是假设最好的情况,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号机制和CAS算法实现。
    • 偏向锁:是指一段同步代码一直被一个线程所访问,那么该线程会自动获取锁。降低获取锁的代价。
    • 轻量级锁:是指当锁是偏向锁的时候,被另一个线程所访问,偏向锁就会升级为轻量级锁,其他线程会通过自旋的形式尝试获取锁,不会阻塞,提高性能。
    • 重量级锁:是指当锁为轻量级锁的时候,另一个线程虽然是自旋,但自旋不会一直持续下去,当自旋一定次数的时候,还没有获取到锁,就会进入阻塞,该锁膨胀为重量级锁。重量级锁会让其他申请的线程进入阻塞,性能降低。
    • 自旋锁:是指当一个线程在获取锁的时候,如果锁已经被其它线程获取,那么该线程将循环等待,然后不断的判断锁是否能够被成功获取,直到获取到锁才会退出循环 。
  • 相关阅读:
    .net中AjaxPro的简单使用
    关于 Activity 中 startActivityForResult 和 onActivityResult
    产品经理值得读的12本书
    存储过程中使用事务与try catch
    扁平化数据解构转化成树形结构
    JavaScript编程精解
    泛型
    OATS:Orthogonal Array Testing Strategy
    等价类划分
    C# homework
  • 原文地址:https://www.cnblogs.com/zhaosq/p/14202009.html
Copyright © 2011-2022 走看看