IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> 线程安全问题生活举例以及解决方案(ReentrantLock) -> 正文阅读

[Java知识库]线程安全问题生活举例以及解决方案(ReentrantLock)

1. 前言

2. 线程安全问题举例以及解决方案

  • 等待

    2.1 存在线程安全的代码

    • 为了方便,我这里把“T”给去掉了,直接用数字,那也不是我们演示的重点,直接看代码吧:
package com.liu.susu.thread.lock.example3;

/**
 * @FileName OrderNumberRun
 * @Description 开启三个线程生成取单号
 * @Author susu
 * @date 2022-03-03
 **/
public class OrderNumberRun implements Runnable {

    private int number = 1;//排队取餐的去取单号从1号开始

    @Override
    public void run() {
        //假定今天针对前100位下单的有优惠
        while (true) {
            if (number <= 100) {
                System.out.println(Thread.currentThread().getName() + "-田老师家,取单号为-->" + number);
                number++;
            }else {
                break;
            }
        }
    }
}
class clientTest3 {
    public static void main(String[] args) {
        OrderNumberRun orderNumberRun = new OrderNumberRun();
        Thread thread1 = new Thread(orderNumberRun);
        Thread thread2 = new Thread(orderNumberRun);
        Thread thread3 = new Thread(orderNumberRun);
        thread1.setName("线程1");
        thread2.setName("线程2");
        thread3.setName("线程3");
        thread1.start();
        thread2.start();
        thread3.start();
    }
}
  • 效果展示:
    在这里插入图片描述
    明显不安全,上篇也有介绍,不多说了,重点往下看处理方案……

    2.2 用 ReentrantLock 解决线程安全

    • 修改代码部分如图:
      在这里插入图片描述
    • 完整代码如下:
package com.liu.susu.thread.lock.example4;

import java.util.concurrent.locks.ReentrantLock;

/**
 * @FileName OrderNumberRun
 * @Description 开启三个线程生成取单号
 * @Author susu
 * @date 2022-03-03
 **/
public class OrderNumberRun implements Runnable {

    private int number = 1;//排队取餐的去取单号从1号开始
    /**
     * 1.实例化ReentrantLock 定义锁
     *   如果参数fair设置true-->公平锁(即:3个线程公平拿锁,避免一个线程把单号取完),默认fair是 false
     */
    private final ReentrantLock lock = new ReentrantLock(true);
    @Override
    public void run() {
        //假定今天针对前100位下单的有优惠
        while (true) {
            try {
                //2.调用lock()方法 加锁
                lock.lock();
                if (number <= 100) {
                    System.out.println(Thread.currentThread().getName() + "-田老师家,取单号为-->" + number);
                    number++;
                }else {
                    break;
                }
            } finally {
                //3.调用unlock()方法 解锁
                lock.unlock();
            }
        }
    }
}

class clientTest4 {
    public static void main(String[] args) {
        OrderNumberRun orderNumberRun = new OrderNumberRun();
        Thread thread1 = new Thread(orderNumberRun);
        Thread thread2 = new Thread(orderNumberRun);
        Thread thread3 = new Thread(orderNumberRun);
        thread1.setName("线程1");
        thread2.setName("线程2");
        thread3.setName("线程3");
        thread1.start();
        thread2.start();
        thread3.start();
    }
}
  • 运行效果如下:
    在这里插入图片描述

3. synchronized 与 ReentrantLock 区别

3.1 解释说明

3.1.1 相同点

  • synchronized 与 ReentrantLock 都可以解决线程安全问题是肯定的
  • ReentrantLock 表示可重入锁,与synchronized一样,都是属于可重入锁;
    可重入锁:同一个线程 如果首次获取到该锁资源,则它就有权力再次获取到该锁,这就是锁的重入!
  • 都是加锁方式同步,而且都是阻塞式的同步,也就是说当如果一个线程获得了对象锁,进入了同步块,其他访问该同步块的线程都必须阻塞在同步块外面等待,而进行线程阻塞和唤醒的代价是比较高的(操作系统需要在用户态与内核态之间来回切换,代价很高,不过可以通过对锁优化进行改善)。

3.1.1 不同点

(1)实现上区别

  • 使用 lock 初始化可选择公平锁、非公平锁
  • lock 锁只有代码块锁,synchronized 有代码块儿锁和方法锁
  • synchronized 是隐式锁,自动释放同步监视器(自动释放锁)
    lock是显示锁,需要手动启动同步(lock()方法加锁),结束同步时需要手动解锁(调用unclock()方法)
  • 对于Synchronized来说,它是java语言的关键字,是原生语法层面的互斥,需要 JVM 实现。而 ReentrantLock 是JDK5.0 之后提供的API层面的互斥锁,需要lock()和unlock()方法配合 try/finally 语句块来完成.
  • Synchronized的使用更方便,并且由编译器去保证锁的加锁和释放,而 ReenTrantLock 需要手工声明来加锁和释放锁,为了避免忘记手工释放锁造成死锁,所以最好在 finally 中声明释放锁。

(2)性能上区别

  • 使用 lock 锁,JVM 将花费较少的时间来调度线程,性能更好,还具有很好的扩展性(提供更多的子类)

3.2 表格对比

对比synchronizedReentrantLock
出现一直有JDK5.0新增
可重入性可重入可重入
锁类型公平锁 / 非公平锁非公平锁
释放形式必须显示调用unclock()释放锁自动释放监视器
灵活性不够灵活
锁实现机制依赖AQS监视器模式
  • 待深究部分,锁的机制实现原理?
  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2022-03-06 12:48:23  更:2022-03-06 12:48:48 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 11:41:33-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码