zoukankan      html  css  js  c++  java
  • 类锁与对象锁,重入锁

    看的该文章的总结:

    https://juejin.im/post/5adf14dcf265da0b7b358d58

    synchronized一般我们用来修饰三种东西:

    • 修饰普通方法
    • 修饰代码块
    • 修饰静态方法

    1.4.1修饰普通方法:

    用的锁是Java3y对象(内置锁)

    public class Java3y {
    
    
        // 修饰普通方法,此时用的锁是Java3y对象(内置锁)
        public synchronized void test() {
            // 关注公众号Java3y
            // doSomething
        }
    }

    1.4.2修饰代码块:

    用的锁是Java3y对象(内置锁)--->this

    public class Java3y {
        
        public  void test() {
            
            // 修饰代码块,此时用的锁是Java3y对象(内置锁)--->this
            synchronized (this){
                // 关注公众号Java3y
                // doSomething
            }
        }
    }

    当然了,我们使用synchronized修饰代码块时未必使用this,还可以使用其他的对象(随便一个对象都有一个内置锁)

    所以,我们可以这样干:

    public class Java3y {
    
        // 使用object作为锁(任何对象都有对应的锁标记,object也不例外)
        private Object object = new Object();
    
        public void test() {
    
            // 修饰代码块,此时用的锁是自己创建的锁Object
            synchronized (object){
                // 关注公众号Java3y
                // doSomething
            }
        }
    
    }

    上面那种方式(随便使用一个对象作为锁)在书上称之为-->客户端锁,这是不建议使用的

    书上想要实现的功能是:给ArrayList添加一个putIfAbsent(),这需要是线程安全的。

    假定直接添加synchronized是不可行的

    使用客户端锁,会将当前的实现与原本的list耦合了:

    书上给出的办法是使用组合的方式(也就是装饰器模式)

    1.4.3修饰静态方法

    获取到的是类锁(类的字节码文件对象):Java3y.class

    public class Java3y {
    
        // 修饰静态方法代码块,静态方法属于类方法,它属于这个类,获取到的锁是属于类的锁(类的字节码文件对象)-->Java3y.class
        public static synchronized void test() {
    
            // 关注公众号Java3y
            // doSomething
        }
    }

    1.4.4类锁与对象锁

    synchronized修饰静态方法获取的是类锁(类的字节码文件对象),synchronized修饰普通方法或代码块获取的是对象锁。

    • 它俩是不冲突的,也就是说:获取了类锁的线程和获取了对象锁的线程是不冲突的!
    public class SynchoronizedDemo {
    
        //synchronized修饰非静态方法
        public synchronized void function() throws InterruptedException {
            for (int i = 0; i <3; i++) {
                Thread.sleep(1000);
                System.out.println("function running...");
            }
        }
        //synchronized修饰静态方法
        public static synchronized void staticFunction()
                throws InterruptedException {
            for (int i = 0; i < 3; i++) {
                Thread.sleep(1000);
                System.out.println("Static function running...");
            }
        }
    
        public static void main(String[] args) {
            final SynchoronizedDemo demo = new SynchoronizedDemo();
    
            // 创建线程执行静态方法
            Thread t1 = new Thread(() -> {
                try {
                    staticFunction();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            });
    
            // 创建线程执行实例方法
            Thread t2 = new Thread(() -> {
                try {
                    demo.function();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            });
            // 启动
            t1.start();
            t2.start();
        }
    }

    结果证明:类锁和对象锁是不会冲突的!

    
    
     

    1.5重入锁

    我们来看下面的代码:

    public class Widget {
    
        // 锁住了
        public synchronized void doSomething() {
            ...
        }
    }
    
    public class LoggingWidget extends Widget {
    
        // 锁住了
        public synchronized void doSomething() {
            System.out.println(toString() + ": calling doSomething");
            super.doSomething();
        }
    }
    1. 当线程A进入到LoggingWidget的doSomething()方法时,此时拿到了LoggingWidget实例对象的锁
    2. 随后在方法上又调用了父类Widget的doSomething()方法,它又是被synchronized修饰
    3. 那现在我们LoggingWidget实例对象的锁还没有释放,进入父类Widget的doSomething()方法还需要一把锁吗?

    不需要的!

    因为锁的持有者是“线程”,而不是“调用”

    线程A已经是有了LoggingWidget实例对象的锁了,当再需要的时候可以继续**“开锁”**进去的

    这就是内置锁的可重入性。

    1.6释放锁的时机

    1. 当方法(代码块)执行完毕后会自动释放锁,不需要做任何的操作。
    2. 当一个线程执行的代码出现异常时,其所持有的锁会自动释放。
    • 不会由于异常导致出现死锁现象~。

    二、Lock显式锁

    2.1Lock显式锁简单介绍

    Lock显式锁是JDK1.5之后才有的,之前我们都是使用Synchronized锁来使线程安全的~

    Lock显式锁是一个接口,我们来看看:

    随便翻译一下他的顶部注释,看看是干嘛用的:

    可以简单概括一下:

    • Lock方式来获取锁支持中断、超时不获取、是非阻塞的
    • 提高了语义化,哪里加锁,哪里解锁都得写出来
    • Lock显式锁可以给我们带来很好的灵活性,但同时我们必须手动释放锁
    • 支持Condition条件对象
    • 允许多个读线程同时访问共享资源

    2.2synchronized锁和Lock锁使用哪个

    前面说了,Lock显式锁给我们的程序带来了很多的灵活性,很多特性都是Synchronized锁没有的。那Synchronized锁有没有存在的必要??

    必须是有的!!Lock锁在刚出来的时候很多性能方面都比Synchronized锁要好,但是从JDK1.6开始Synchronized锁就做了各种的优化(毕竟亲儿子,牛逼)

    所以,到现在Lock锁和Synchronized锁的性能其实差别不是很大!而Synchronized锁用起来又特别简单。Lock锁还得顾忌到它的特性,要手动释放锁才行(如果忘了释放,这就是一个隐患)

    所以说,我们绝大部分时候还是会使用Synchronized锁,用到了Lock锁提及的特性,带来的灵活性才会考虑使用Lock显式锁~

    2.3公平锁

    公平锁理解起来非常简单:

    • 线程将按照它们发出请求的顺序来获取锁

    非公平锁就是:

    • 线程发出请求的时可以**“插队”**获取锁

    Lock和synchronize都是默认使用非公平锁的。如果不是必要的情况下,不要使用公平锁

    • 公平锁会来带一些性能的消耗的

    四、最后

    本文讲了synchronized内置锁和简单描述了一下Lock显式锁,总得来说:

    • synchronized好用,简单,性能不差
    • 没有使用到Lock显式锁的特性就不要使用Lock锁了。

    Lock锁这里只是介绍了一些些,明天会详解它的相关子类和需要注意的地方,敬请期待~

    之前在学习操作系统的时候根据《计算机操作系统-汤小丹》这本书也做了一点点笔记,都是比较浅显的知识点。或许对大家有帮助

     
  • 相关阅读:
    tpescript中declare
    vue-router history模式 为什么需要服务端配置以及如何配置
    组件库搭建总结
    Babel
    高德地图实现轨迹围栏
    通过canvas实现描点连线功能
    vue项目使用echarts实现区域地图绘制,且可点击单独区域
    javascript正则表达式学习笔记
    el-loading修改默认样式
    Cannot read property 'resetFields' of undefined 问题及引申
  • 原文地址:https://www.cnblogs.com/alsf/p/9054097.html
Copyright © 2011-2022 走看看