当前位置: 首页 > news >正文

ThreadSafe解决线程安全的问题

创建一个Runnable接口的实现类,实现一个卖票的案例

package demo06.ThreadSafe;/*
 *@author wanghongyuan
 *@Create 2021/1/5 8:02
 */
/*
   实现买票案例
 */
public class RunnableImpl implements Runnable{
    // 定义一个多个线程共享的票源
   private int ticket = 100;


    // 设置线程任务买票
    @Override
    public void run() {
       // 使用死循环,让卖票操作重复执行
        while (true){
            // 先判断票是否存在
            if (ticket> 0){
				// 提高安全问题出现的概率,让程序睡眠
				try{
				Thread.sleep(mills:10);
                }catch (InterruptedException e){
					e.printStackTrace();
}// 票存在,卖票,ticket--
                System.out.println(Thread.currentThread().getName()+"正在卖票-->"+ticket+"张票");
                ticket--;
            }
        }

    }
}

创建一个main执行卖票案例
通过这个案例我们发现,如果三个线程同时共享一个票源,不经过处理的话,三个线程同时抢占CPU资源,就会出现线程安全问题,会有多个线程同时卖一张相同票的情况。

package ThreadDemo;/*
 @Author wanghongyuan
  @Date 2021/1/5 
 
*/
/*
    买票出现了线程安全的问题
    卖出了不存在和重复的票

    解决线程安全问题的第一种方案:使用同步代码块
     格式:
        synchronized(锁对象){
            可能会出现线程安全问题的代码(访问了共享数据的代码)
        }

     注意:
        1.通过代码块中的锁对象,可以使用任意的对象。
        2.但是必须保证多个线程使用的锁对象是同一个
        3.锁对象的作用:
            把同步代码块锁住,只让一个线程在同步代码块中执行

 */
public class Demo01ThreadSafe {
    public static void main(String[] args) {
        RunnableImpl run = new RunnableImpl();
        Thread T0= new Thread(run);
        Thread T1= new Thread(run);
        Thread T2= new Thread(run);
        T0.start();
        T1.start();
        T2.start();
    }
}


使用同步代码块解决线程安全问题

package ThreadDemo;/*
 @Author wanghongyuan
  @Date 2021/1/5 
 
*/
/*
    买票出现了线程安全的问题
    卖出了不存在和重复的票

    解决线程安全问题的第一种方案:使用同步代码块
     格式:
        synchronized(锁对象){
            可能会出现线程安全问题的代码(访问了共享数据的代码)
        }

     注意:
        1.通过代码块中的锁对象,可以使用任意的对象。
        2.但是必须保证多个线程使用的锁对象是同一个
        3.锁对象的作用:
            把同步代码块锁住,只让一个线程在同步代码块中执行

 */
public class RunnableImpl implements Runnable{

    private int ticket = 100;
    // 创建一个锁对象
    Object obj = new Object();

    @Override
    public void run() {
        while (true){
            // 创建一个同步代码块
            synchronized (obj){
                if (ticket>0){
                    try {
                        Thread.sleep(10);

                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName()+"正在售票"+ticket+"张");
                    ticket--;
                }
            }
        }
    }
}

使用静态方法

package newww;/*
 @Author wanghongyuan
  @Date 2021/1/5 
 
*/
/*
    买票出现了线程安全的问题
    卖出了不存在和重复的票

    解决线程安全问题的第三种方案:使用静态方法
     格式:
        synchronized(锁对象){
            可能会出现线程安全问题的代码(访问了共享数据的代码)
        }

     注意:
        1.通过代码块中的锁对象,可以使用任意的对象。
        2.但是必须保证多个线程使用的锁对象是同一个
        3.锁对象的作用:
            把同步代码块锁住,只让一个线程在同步代码块中执行

 */
public class RunnableImpl implements Runnable{

    private static int ticket = 100;
    // 创建一个锁对象
    Object obj = new Object();

    @Override
    public void run() {
        while (true){
            payTicketStatic();
        }
    }
/*
    静态的同步方法
    锁对象是谁?
    不能是this
    this是创建对象之后产生的,静态方法优先于对象
    静态方法的锁对象是本类的class属性-->class文件对象(反射)
 */
    public static /*synchronized*/ void payTicketStatic() {
        while (true){
            // 创建一个同步代码块 RunnableImpl.class这个也是一个对象。
            synchronized (RunnableImpl.class){
                if (ticket>0){
                    try {
                        Thread.sleep(10);

                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName()+"正在售23票"+ticket+"张");
                    ticket--;
                }
            }
        }
    }
}

使用Lock解决线程安全问题

package Demo09.Lock;/*
 @Author wanghongyuan
  @Date 2021/1/5 
 
*/

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/*
    卖票案例出现了线程安全的问题
    卖出了不存在和重复的票

    解决线程安全的问题第四种方案:使用Lock锁

    java.util.concurrent.Locks.Lock接口
    Lock中的方法:
    void lock()获取锁
    void unlock()释放锁
    java.util.concurrent.Locks.ReentrantLock implement lock接口

    使用步骤:
        1.在成员位置创建一个ReentrantLock对象
        2.在可能会出现安全问题的代码前调用lock接口中的方法lock获取锁
        3.在可能会出现安全问题的代码后调用lock接口中的方法unlock释放锁
 */
public class RunnableImpl implements Runnable{
    private int ticket = 100;
    //1.在成员位置创建一个ReentrantLock对象
    Lock l= new ReentrantLock();
    @Override
    public void run() {
        while (true){
            // 2.在可能会出现安全问题的代码前调用lock接口中的方法lock获取锁
            l.lock();
            if (ticket>0){
                try {
                    Thread.sleep(10);
                    System.out.println(Thread.currentThread().getName()+"正在1卖第"+ticket+"张票");
                    ticket--;
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }finally {
                    //  3.在可能会出现安全问题的代码后调用lock接口中的方法unlock释放锁
                    l.unlock();// 无论程序是否异常,都释放锁
                }
            }
        }


       /* while (true){
            // 2.在可能会出现安全问题的代码前调用lock接口中的方法lock获取锁
            l.lock();
            if (ticket>0){
                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName()+"正在卖第"+ticket+"张票");
                ticket--;
            }
            //  3.在可能会出现安全问题的代码后调用lock接口中的方法unlock释放锁
            l.unlock();

        }*/
    }
}

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 资源唤醒案例
  • 线程池的使用
  • Lambda标准格式
  • File类的构造方法
  • 递归计算
  • 使用递归遍历目录
  • 对文本内容进行排序
  • 测试框架
  • 写个web自动化
  • 与全球外国人即时聊天的网站
  • 将GBK的文件转换成UTF-8的文件格式
  • 反序列化流de使用
  • 使用序列化和反序列化操作集合中的值
  • 接口的使用-常用的函数式接口举例
  • 学习个人项目课
  • [Vue CLI 3] 配置解析之 css.extract
  • 「前端早读君006」移动开发必备:那些玩转H5的小技巧
  • 【React系列】如何构建React应用程序
  • AHK 中 = 和 == 等比较运算符的用法
  • bearychat的java client
  • HTTP中GET与POST的区别 99%的错误认识
  • If…else
  • java2019面试题北京
  • JavaScript 是如何工作的:WebRTC 和对等网络的机制!
  • Javascript编码规范
  • js
  • 利用jquery编写加法运算验证码
  • 普通函数和构造函数的区别
  • 使用parted解决大于2T的磁盘分区
  • 学习HTTP相关知识笔记
  • 一些css基础学习笔记
  • 3月27日云栖精选夜读 | 从 “城市大脑”实践,瞭望未来城市源起 ...
  • ![CDATA[ ]] 是什么东东
  • #pragam once 和 #ifndef 预编译头
  • #使用清华镜像源 安装/更新 指定版本tensorflow
  • %3cli%3e连接html页面,html+canvas实现屏幕截取
  • (14)Hive调优——合并小文件
  • (BAT向)Java岗常问高频面试汇总:MyBatis 微服务 Spring 分布式 MySQL等(1)
  • (层次遍历)104. 二叉树的最大深度
  • (二)c52学习之旅-简单了解单片机
  • (学习日记)2024.01.19
  • (一)appium-desktop定位元素原理
  • (一)十分简易快速 自己训练样本 opencv级联haar分类器 车牌识别
  • (原創) 如何解决make kernel时『clock skew detected』的warning? (OS) (Linux)
  • .naturalWidth 和naturalHeight属性,
  • .NET Core/Framework 创建委托以大幅度提高反射调用的性能
  • .NET 的静态构造函数是否线程安全?答案是肯定的!
  • .NET/C# 解压 Zip 文件时出现异常:System.IO.InvalidDataException: 找不到中央目录结尾记录。
  • .net解析传过来的xml_DOM4J解析XML文件
  • .Net下C#针对Excel开发控件汇总(ClosedXML,EPPlus,NPOI)
  • .NET与 java通用的3DES加密解密方法
  • .net与java建立WebService再互相调用
  • :如何用SQL脚本保存存储过程返回的结果集
  • @Transaction注解失效的几种场景(附有示例代码)
  • [ C++ ] STL---string类的模拟实现