高级并发编程系列七(锁入门)

1.引子

昨天是1024程序员节日,看到很多朋友发了朋友圈,在这里我想到了问一个问题:1024是2的多少此方?关于这个问题,如果你还需要默算而不是靠潜意识就能回答上来,那说明你不是一个合格的程序员,开个玩笑!

下面进入正题,今天开始在高级并发编程系列中,我们正式进入第二个小节系列: 。说起锁你一定不会陌生。日常生活中,我们经常谈及的很多都与锁有关,你比如说:出门的时候记得把门 好、 保险柜、装个防盗 等等。这些都是我们生活中的锁,锁好门、锁好保险柜都是为了安全 ;它其实与我们程序中锁目的是一致的,在程序中加锁的目的也是为了安全,这又是一个证明世间万物道理想通的铁证。

我们暂且记住,加锁的目的是为了:安全。这个系列中我们准备分享这么几个内容:

  • 锁定义
  • 常见锁分类
  • Lock接口和它的常见实现类分析
  • ReentrantLock使用案例
  • ReentrantReadWriteLock使用案例

今天这一篇,让我们先从入门开始,那么来吧

bash 复制代码
#考考你:
1.你能用自己的话,结合你的理解给锁下一个定义吗
2.你能说出常见的锁分类有哪些吗

2.案例

2.1.第一个加锁案例

我们先看一个大家都熟悉,而可能又陌生的案例操作。在看之前,你需要先思考一下:在大多数编程语言中,都提供了:++自曾的操作,关于这个操作它是线程安全的吗

2.1.1.案例版本一:不加锁

2.1.1.1.任务线程
csharp 复制代码
/**
 * 任务线程
 */
class AddITask implements Runnable{
​
    @Override
    public void run() {
        // for循环,让add_i变量自增操作:10000次
        for (int i = 0; i < 10000; i++){
            NeedDoLockDemo.addI();
        }
    }
​
}
2.1.1.2.主程序
csharp 复制代码
/**
 * 演示需要加锁的基本操作
 *
 * @author ThinkPad
 * @version 1.0
 * @date 2020/10/24 21:35
 */
public class NeedDoLockDemo {
​
    /**
     * 自曾操作变量add_i
     */
    public static int add_i = 0;
​
    /**
     * addI方法,实现add_i变量自曾操作
     * 注意:这里没有加锁
     */
    public  static void addI(){
        add_i ++;
    }
​
    public static void main(String[] args) {
        // 创建任务线程对象
        Runnable r1 = new AddITask();
​
        // 用20个线程,并行执行自增操作
        for(int i = 0; i < 20; i++){
            new Thread(r1).start();
        }
​
        // 循环等待子线程执行结束
        while (Thread.activeCount() > 2){
            ;
        }
​
        // 打印add_i变量值,预期结果:200000
        System.out.println("20个线程并行自增后,结果:" + add_i);
    }
​
}
2.1.1.3.执行结果

结果分析:

  • add_i变量初始值是:0

    arduino 复制代码
     public static int add_i = 0
  • 通过20个线程,并行执行自增操作

    scss 复制代码
    // 用20个线程,并行执行自增操作
    for(int i = 0; i < 20; i++){
        new Thread(r1).start();
    }
  • 每个线程,自增10000次

    ini 复制代码
    // for循环,让add_i变量自增操作:10000次
    for (int i = 0; i < 10000; i++){
         NeedDoLockDemo.addI();
    }
  • 预期结果:20 * 10000 = 200000

  • 实际结果:198359

  • 执行结果与预期结果不符,发生了并发不安全问题

2.1.2.案例版本二:加锁

在案例版本一中,由于没有加锁,发生了并发不安全的问题。这里有一个小知识点:对于jvm来说,++操作不是一个线程安全的操作,因为自增++操作,它不是一个原子性的操作

那如何实现自增++的放心操作,人畜无害呢?答案是:加锁。我们再来看加锁的版本。

2.1.2.1.方法addI加锁
arduino 复制代码
/**
* addI方法,实现add_i变量自曾操作
* 注意:这里的synchronized关键字
*/
public synchronized static void addI(){
      add_i ++;
}
2.1.2.2.执行结果

结果分析:

  • 加锁后,add_i变量的最终执行结果是:200000,符合预期

2.2.锁定义及常见锁分类

2.2.1.锁定义

我们知道了,在多线程并发操作下,需要考虑线程安全的问题。这是因为有共享资源的存在,所谓共享资源即是每个线程都会去操作的资源,你需要,我也需要,可以这么去理解 ,比如说上面案例中的add_i变量

那么问题来了,在并发操作的情况下,是不管先来后到的,即便后来,但是可能却先把活干了。这样下去,世界就乱了,没有规则没有秩序,重新陷入混沌状态

所以需要一种规则,建立一套秩序,让程序世界不能乱!锁就是编程世界中,解决混乱的规则和秩序,它的本质是在整体并行执行的应用程序中,实现局部串行有序执行

到了这里,我们可以用一句简洁的话来描述锁:锁是一种工具,用于控制对共享资源访问的工具

2.2.2.锁分类

关于锁的分类,如果要细分会非常多,非常难记,且没有必要。我们关注几类常见的吧:

  • 乐观锁与悲观锁

    • 乐观锁

      bash 复制代码
      #1.所谓乐观锁,它是典型的乐天派,认为在操作共享资源的时候,永远都只有自己一个人(一个线程)在操作,不会有其它线程与自己争
      #2.因此乐观锁本质上不加锁,而是引入了冲突检测的机制。关于冲突检测机制,在后面分享CAS的时候,我们在详细讨论
      #3.乐观锁适用于:读多写少,冲突小的业务场景
    • 悲观锁

    bash 复制代码
    #1.所谓悲观锁,它是典型的悲观派,时时刻刻都认为:在操作共享资源的时候,永远都有别人(别的线程),在跟自己抢!
    #2.因此不管三七二十一,先锁了再说,让你抢不着
    #悲观锁适用于:写多读少,容易发生冲突的业务场景
  • 可重入锁与非可重入锁

    • 可重入锁

      bash 复制代码
      #1.所谓可重入锁,一个线程拿到该锁以后,在释放以前,可以重复多次获取到该锁
      #2.在juc中提供的ReentrantLock即是可重入锁
    • 非可重入锁

      bash 复制代码
      #1.所谓非可重入锁,一个线程拿到该锁以后,在释放以前,不能重复多次获取到该锁
      #2.在jdk中提供的synchronized锁,即是非可重入锁
  • 共享锁与排它锁

    • 共享锁

      bash 复制代码
      #1.所谓共享锁,表示该锁比较大方,独乐乐不如众乐乐,一个锁可以被多个线程同时获取拥有
      #2.在juc中提供的ReentrantReadWriteLock锁中的读锁,即是共享锁
    • 排它锁

      bash 复制代码
      #1.所谓排它锁,表示有你没我,有我没你。即一个锁被一个线程获取以后,便不能再被其他线程获取
      #2.我们平常见的较多的锁,都是排它锁。比如ReentrantLock、synchronized、ReentrantReadWriteLock中的写锁等
相关推荐
ChineHe几秒前
Golang并发编程篇001_并发编程相关概念解释
开发语言·后端·golang
Uluoyu几秒前
word、pdf文档内容提取工具类
java·pdf·word
自由的疯13 分钟前
java 怎么判断事务有无提交成功
java·后端·架构
阿兰哥30 分钟前
【调试篇5】TransactionTooLargeException 原理解析
android·性能优化·源码
流星白龙30 分钟前
【Qt】3.认识 Qt Creator 界面
java·开发语言·qt
bcbnb34 分钟前
Socket 抓包工具与实战,从抓取到定位(Socket 的命令、分析)
后端
用户83562907805136 分钟前
用Python轻松转换Excel表格为HTML格式
后端·python
机灵猫39 分钟前
深入理解 Java 类加载与垃圾回收机制:从原理到实践
java·开发语言
Sunsets_Red41 分钟前
差分操作正确性证明
java·c语言·c++·python·算法·c#
QZ_orz_freedom42 分钟前
学习笔记--文件上传
java·笔记·学习