显式锁与隐式锁的区别

在面试的过程中有可能会问到:在Java并发编程中,锁有两种实现:使用隐式锁和使用显示锁,其中它们分别是什么?两者的区别是什么?所谓的显式锁和隐式锁的区别说的也是Synchronized和Lock的区别。

本文主要内容:将通过六个方面详细介绍sync和lock的区别。

1.两者的出身不同

synchronized是Java中的关键字,由JVM维护,是JVM层面的锁;

而lock是JDK5之后才出现的具体的类,使用Lock是调用对应的API,是API层面的锁。

synchronized的底层是通过monitorenter进行加锁的(底层是通过monitor对象来完成的,其中的wait/notify等方法也是依赖monito对象的,只有在同步块或者是同步方法中才可以调用wait/notify方法。因为只有在同步块或同步方法中,JVM才会调用monitor对象的。);通过monitorexit来退出锁的。

Lock则是通过调用相应的API方法来获取和释放锁
在这里插入图片描述

2.使用方式的不同

synchronized隐式锁;lock是显式锁

显氏锁和隐式锁的区别在于:使用显式锁的时候,使用者需要手动去获取和释放锁。

在使用synchronized关键字的时候,使用者不需要写其他的代码,程序就可以自动获取锁和释放锁。synchronized是由系统维护的,系统会自动的让程序释放占用的锁。

在使用lock的时候,需要使用者手动去获取锁和释放锁。如果没有释放锁,就可能出现死锁的现象。手动获取锁的方法lock.lock(); 释放锁的操作:unlock().
对于synchronized隐式锁使用
1、同步方法体,在方法声明中使用,如下:

public synchronized void method(){
        //方法体
}

2、同步代码块,修饰在代码块外层,指定加锁对象,如下:

public void method2(){
    synchronized (this) {
    //一次只能有一个线程进入   
    }
}

上述synchronized(this)指定了当前对象本身作为锁,和它持有相同对象锁的地方将产生互斥性。当一个线程访问method2的同步代码块时,它就获得了这个object的对象锁。其他的线程对该object所有同步代码部分的访问都被暂时的阻塞。

sychronized的不同写法对程序响应的快慢和对资源高并发的利用程度不一样,性能和执行效率从差到优排序如下:

同步方法体 < 同步代码块 < 小对象锁同步代码块

小对象锁同步代码块指锁的对象的所占内存小,因为锁是对象,加锁和解锁都需要释放资源,那肯定是锁对象越小越好,实际应用如下:

private byte[] lock = new byte[1];
public void method3(){
    synchronized (lock) {
        //一次只能有一个线程进入   
    }
}
package comx.demo;
import java.io.ObjectOutputStream;
public class demo2_1 {
    /**线程安全 1 同步代码块
     *
     * 线程同步:synchronized
     *
     * @param args
     */
    public static void main(String[] args) {
        //线程不安全
        //解决方案1. 同步代码块
        //格式;   synchronnized(锁对象){
        //              }
        
        //多态方式
        Runnable run = new Ticket();
        new Thread(run).start();
        new Thread(run).start();
        new Thread(run).start();
    }
    static class Ticket implements Runnable{
        //票数
        private int count = 10;
        private Object o = new Object();
        @Override
        public void run() {
            // 出现不安全问题,,会出现负数
            while(true){
                //多个人对应一把锁,排队进行
                //如果每个人对应一把自己的锁,则很可能会出现问题
                synchronized (o) {
                    if (count > 0) {
                        //卖票
                        System.out.println("正在准备卖票");
                        try {
                            //休眠1秒,增大不安全概率
                            Thread.sleep(1000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        count--;
                        System.out.println(Thread.currentThread().getName()+"出票成功,余票:" + count);
                    }else{
                        break;
                    }
                }
            }
        }
    }
}
package comx.demo;
public class demo2_2 {
    /**线程安全 2 同步方法
     *
     * 线程同步:synchronized
     *
     * @param args
     */
    public static void main(String[] args) {
        //线程不安全
        //解决方案2 . 同步方法
        //格式;   synchronnized(锁对象){
       //
        //        }
        //多态方式 创建一个任务
        Runnable run = new Ticket();
        new Thread(run).start();
        new Thread(run).start();
        new Thread(run).start();
    }
    static class Ticket implements Runnable{
        //票数
        private int count = 10;
       // private Object o = new Object();
        @Override
        public void run() {
            // 出现不安全问题,,会出现负数
            while(true){
              Boolean flag = sale();
              if (!flag){
                  break;
              }
            }
        }
        //增加一个修饰 synchronized 锁
        public synchronized boolean sale(){
            //this 关键字的使用
            /**
             * 使用
             * synchronized(this){
             *
             *          }
             *  使用这种,其他的都无法执行这段代码,需要排队进行
             */
            //如果为静态方法,则使用
            // 类名.class
            // Ticket.class
            if (true) {
                //卖票
                System.out.println("正在准备卖票");
                try {
                    //休眠1秒,增大不安全概率
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                count--;
                System.out.println(Thread.currentThread().getName()+"出票成功,余票:" + count);
                return true;
            }
            return false;
        }
    }
}

对于Lock类它的使用

package comx.demo;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class demo2_3 {
    /**
     * 同步代码块 和 同步方法   都属于隐式锁
     * 线程同步: Lock
     * @param args
     */
    public static void main(String[] args) {
        //线程不安全
        //解决方案3  显式锁  Lock  子类 ReentranLock
        Runnable run = new Ticket();
        new Thread(run).start();
        new Thread(run).start();
        new Thread(run).start();
    }
    static class Ticket implements Runnable{
        //票数
        private int count = 10;
        // 创建   显式锁   l
        private Lock l = new ReentrantLock();
        @Override
        public void run() {
            // 出现不安全问题,,会出现负数
            while(true) {
                //上锁(获取锁)
                l.lock();
                if (count > 0) {
                    //卖票
                    System.out.println("正在准备卖票");
                    try {
                        //休眠1秒,增大不安全概率
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    count--;
                    System.out.println("出票成功,余票:" + count);
                }else{
                    break;
                }
                //关闭锁(释放锁)
                l.unlock();
            }
        }
    }
}

3.等待是否中断

synchronized关键字是不可中断的,除非抛出异常或者正常运行结束

而Lock是可以中断的,中断方式:

调用设置超时方法tryLock(Long timeout,timeUnit unit)
调用lockInterruptibly()放到代码块中,然后调用interrupt()方法可以中断。

4.加锁的时候是否公平

synchronized是非公平锁;
而lock两者都可以,默认创建是非公平锁。
注意:什么是公平锁,非公平锁?在这里解释一下:
公平锁:先来先到 ,排队,
非公平锁:线程一起抢

5.锁绑定多个条件来condition

synchronized要么随机唤醒一个线程;要么是唤醒所有等待的线程。

lock:可以用实现分组唤醒需要唤醒的线程,可以精准的唤醒,而不是像synchronized那样,不能精准唤醒线程。

6.性能比较

synchronized是托管给JVM去执行的,而Lock是Java写的控制锁的代码。在Java1.5中,synchronized的性能是低效的,因为其是重量级锁的操作,需要从用户态切换到内核态消耗大量的时间。相比之下使用Java提供的Lock对象,性能更高一些。但是到了Java1.6之后,对synchronized关键字进行了优化,有适应性自旋,锁消除,轻量级锁,偏向锁等,导致在Java1.6中synchronized的性能并不比lock差。

在之前的synchronized版本中使用是悲观锁的机制,即线程独占锁,其它线程只能依靠阻塞来等待线程释放的锁,而线程阻塞时会引起线程的上下文切换,当有很多线程竞争锁的时候,会引起CPU频繁的上下文切换导致效率很低。

而Lock使用的乐观锁的方式。所谓乐观锁就是,每次不加锁而是假设没有冲突而去完成某项操作,如果因为冲突失败就重试,直到成功为止。乐观锁的实现方式就CAS机制(compareAndSetstate),调用的是CPU提供的底层指令。

转载于:
【Java锁体系】五、隐式锁和显式锁的区别(Synchronized和Lock的区别)

显示锁与隐式锁的区别

隐式锁 Synchronized 与显示锁 Lock的用法和简单对比

posted @   所遇所思  阅读(70)  评论(0编辑  收藏  举报  
点击右上角即可分享
微信分享提示
💬
评论
📌
收藏
💗
关注
👍
推荐
🚀
回顶
收起
🔑
  1. 1 404 not found REOL
  2. 2 偏爱 张芸京
  3. 3 Glimpse of Us Joji
偏爱 - 张芸京
00:00 / 00:00
An audio error has occurred, player will skip forward in 2 seconds.

作词 : 葛大为

作曲 : 陈伟

编曲 : 陈伟

把昨天都作废

现在你在我眼前

我想爱 请给我机会

如果我错了也承担

认定你就是答案

我不怕谁嘲笑我极端

相信自己的直觉

相信自己的直觉

顽固的人不喊累

爱上你 我不撤退

我说过 我不闪躲

我说过 我不闪躲

我非要这么做

讲不听 也偏要爱

更努力爱 让你明白

没有别条路能走

你决定要不要陪我

讲不听 偏爱

靠我感觉爱

等你的依赖

对你偏爱

痛也很愉快

把昨天都作废

把昨天都作废

现在你在我眼前

我想爱 请给我机会

如果我错了也承担

认定你就是答案

我不怕谁嘲笑我极端

相信自己的直觉

相信自己的直觉

顽固的人不喊累

爱上你 我不撤退

我说过 我不闪躲

我说过 我不闪躲

我非要这么做

讲不听 也偏要爱

更努力爱 让你明白

没有别条路能走

你决定要不要陪我

讲不听 偏爱

靠我感觉爱

等你的依赖

不后悔 有把握

不后悔 有把握

我不闪躲 我非要这么做

讲不听 也偏要爱

更努力爱 让你明白

没有别条路能走

你决定要不要陪我

讲不听 偏爱

靠我感觉爱

等你的依赖

对你偏爱 爱

痛也很愉快