zoukankan      html  css  js  c++  java
  • synchronized锁和lock锁

    Java多线程加锁机制,有两种:

    • Synchronized
    • 显式Lock

    接下来就讲一下这两种加锁方式。

    一、synchronized

    1.1synchronized锁是什么?

    synchronized是Java的一个关键字,它能够将代码块(方法)锁起来

    • 它使用起来是非常简单的,只要在代码块(方法)添加关键字synchronized,即可以实现同步的功能

        public synchronized void test() {
            // doSomething
        }

    synchronized是一种互斥锁

    • 一次只能允许一个线程进入被锁住的代码块

    synchronized是一种内置锁/监视器锁

    • Java中每个对象都有一个内置锁(监视器,也可以理解成锁标记),而synchronized就是使用对象的内置锁(监视器)来将代码块(方法)锁定的! (锁的是对象,但我们同步的是方法/代码块)

    1.2synchronized用处是什么?

    • synchronized保证了线程的原子性(被保护的代码块是一次被执行的,没有任何线程会同时访问)
    • synchronized还保证了可见性(当执行完synchronized之后,修改后的变量对其他的线程是可见的)

    Java中的synchronized,通过使用内置锁,来实现对变量的同步操作,进而实现了对变量操作的原子性和其他线程对变量的可见性,从而确保了并发情况下的线程安全。

    1.3synchronized的原理

    同步代码块:

    • monitorenter和monitorexit指令实现的

    同步方法(在这看不出来需要看JVM底层实现)

    • 方法修饰符上的ACC_SYNCHRONIZED实现。

    synchronized底层是是通过monitor对象,对象有自己的对象头,存储了很多信息,其中一个信息标示是被哪个线程持有。

    1.4synchronized如何使用

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

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

    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();
      }
    }

    当线程A进入到LoggingWidget的doSomething()方法时,此时拿到了LoggingWidget实例对象的锁。

    随后在方法上又调用了父类Widget的doSomething()方法,它又是被synchronized修饰。

    那现在我们LoggingWidget实例对象的锁还没有释放,进入父类Widget的doSomething()方法还需要一把锁吗?

    不需要的!

    因为锁的持有者是“线程”,而不是“调用”。线程A已经是有了LoggingWidget实例对象的锁了,当再需要的时候可以继续“开锁”进去的!

    这就是内置锁的可重入性。记住,持有锁的是线程。

    1.6释放锁的时机

    当方法(代码块)执行完毕后会自动释放锁,不需要做任何的操作。

    当一个线程执行的代码出现异常时,其所持有的锁会自动释放。

    • 不会由于异常导致出现死锁现象

    二、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都是默认使用非公平锁的。如果不是必要的情况下,不要使用公平锁

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

    三、Java锁简单总结

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

    • synchronized好用,简单,性能不差
    • 没有使用到Lock显式锁的特性就不要使用Lock锁了。
  • 相关阅读:
    LeetCode 230. Kth Smallest Element in a BST
    LeetCode 114. Flatten Binary Tree to Linked List
    LeetCode 222. Count Complete Tree Nodes
    LeetCode 129. Sum Root to Leaf Numbers
    LeetCode 113. Path Sum II
    LeetCode 257. Binary Tree Paths
    Java Convert String & Int
    Java Annotations
    LeetCode 236. Lowest Common Ancestor of a Binary Tree
    LeetCode 235. Lowest Common Ancestor of a Binary Search Tree
  • 原文地址:https://www.cnblogs.com/harpoonJava/p/13213203.html
Copyright © 2011-2022 走看看