Java学习33-Java 多线程Thread 多线程安全问题

Thread的生命周期

  • JDK1.5之前
  • JDK1.5之后分为

NEW
RUNNABLE
BLOCKED
WAITING
TIMED_WAITING
TERMINATED

多线程安全问题

举例,要求三个窗口同时卖票,总共有100张票,打印出卖票过程,不允许重复售卖


package Thread;

public class TestWindow1 {
    public static void main(String[] args) {
        SaleTik x=new SaleTik();
        Thread t1 = new Thread(x);
        t1.start();
        Thread t2 = new Thread(x);
        t2.start();
        Thread t3 = new Thread(x);
        t3.start();
    }
}

class SaleTik implements Runnable{
int tik = 100;
    @Override
    public void run() {
        while(true)
            if(tik >=0){
                System.out.println(Thread.currentThread().getName()+"窗口在卖票,剩余"+tik);
                tik--;
            }

    }
}

运行结果


Thread-1窗口在卖票,剩余100
Thread-1窗口在卖票,剩余99
Thread-1窗口在卖票,剩余98
Thread-1窗口在卖票,剩余97
Thread-1窗口在卖票,剩余96
Thread-1窗口在卖票,剩余95
Thread-1窗口在卖票,剩余94
Thread-1窗口在卖票,剩余93
Thread-1窗口在卖票,剩余92
Thread-1窗口在卖票,剩余91
Thread-1窗口在卖票,剩余90
Thread-1窗口在卖票,剩余89
Thread-1窗口在卖票,剩余88
Thread-1窗口在卖票,剩余87
Thread-2窗口在卖票,剩余100
Thread-0窗口在卖票,剩余100
Thread-1窗口在卖票,剩余86
Thread-2窗口在卖票,剩余85
Thread-0窗口在卖票,剩余84
Thread-0窗口在卖票,剩余81
Thread-1窗口在卖票,剩余83
Thread-2窗口在卖票,剩余82
Thread-0窗口在卖票,剩余80
Thread-1窗口在卖票,剩余79
Thread-2窗口在卖票,剩余78
Thread-0窗口在卖票,剩余77
Thread-1窗口在卖票,剩余76
Thread-2窗口在卖票,剩余75
Thread-0窗口在卖票,剩余74
Thread-1窗口在卖票,剩余73
Thread-2窗口在卖票,剩余72
Thread-0窗口在卖票,剩余71
Thread-1窗口在卖票,剩余70
Thread-2窗口在卖票,剩余69
Thread-0窗口在卖票,剩余68
Thread-1窗口在卖票,剩余67
Thread-2窗口在卖票,剩余66
Thread-0窗口在卖票,剩余65
Thread-1窗口在卖票,剩余64
Thread-2窗口在卖票,剩余63
Thread-0窗口在卖票,剩余62
Thread-1窗口在卖票,剩余61
Thread-2窗口在卖票,剩余60
Thread-0窗口在卖票,剩余59
Thread-1窗口在卖票,剩余58
Thread-2窗口在卖票,剩余57
Thread-0窗口在卖票,剩余56
Thread-1窗口在卖票,剩余55
Thread-2窗口在卖票,剩余54
Thread-2窗口在卖票,剩余51
Thread-2窗口在卖票,剩余50
Thread-2窗口在卖票,剩余49
Thread-2窗口在卖票,剩余48
Thread-2窗口在卖票,剩余47
Thread-2窗口在卖票,剩余46
Thread-2窗口在卖票,剩余45
Thread-2窗口在卖票,剩余44
Thread-2窗口在卖票,剩余43
Thread-2窗口在卖票,剩余42
Thread-2窗口在卖票,剩余41
Thread-0窗口在卖票,剩余53
Thread-1窗口在卖票,剩余52
Thread-2窗口在卖票,剩余40
Thread-0窗口在卖票,剩余39
Thread-1窗口在卖票,剩余38
Thread-2窗口在卖票,剩余37
Thread-0窗口在卖票,剩余36
Thread-1窗口在卖票,剩余35
Thread-2窗口在卖票,剩余34
Thread-0窗口在卖票,剩余33
Thread-1窗口在卖票,剩余32
Thread-2窗口在卖票,剩余31
Thread-2窗口在卖票,剩余28
Thread-2窗口在卖票,剩余27
Thread-2窗口在卖票,剩余26
Thread-2窗口在卖票,剩余25
Thread-2窗口在卖票,剩余24
Thread-2窗口在卖票,剩余23
Thread-2窗口在卖票,剩余22
Thread-2窗口在卖票,剩余21
Thread-2窗口在卖票,剩余20
Thread-2窗口在卖票,剩余19
Thread-2窗口在卖票,剩余18
Thread-2窗口在卖票,剩余17
Thread-2窗口在卖票,剩余16
Thread-2窗口在卖票,剩余15
Thread-2窗口在卖票,剩余14
Thread-2窗口在卖票,剩余13
Thread-2窗口在卖票,剩余12
Thread-2窗口在卖票,剩余11
Thread-2窗口在卖票,剩余10
Thread-2窗口在卖票,剩余9
Thread-2窗口在卖票,剩余8
Thread-2窗口在卖票,剩余7
Thread-2窗口在卖票,剩余6
Thread-2窗口在卖票,剩余5
Thread-2窗口在卖票,剩余4
Thread-2窗口在卖票,剩余3
Thread-2窗口在卖票,剩余2
Thread-2窗口在卖票,剩余1
Thread-2窗口在卖票,剩余0
Thread-0窗口在卖票,剩余30
Thread-1窗口在卖票,剩余29

观察可知,Thread-N窗口在卖票,剩余100这条信息被打印了三次,显然并不是我们期望的。

这便引入了线程的安全问题,我们希望一条线程工作时候直到完毕,再进行下一条线程的工作。

下面的“同步机制”便是为了解决这个问题。

同步机制

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

方式1:同步代码块
synchronized(这里需要放独一无二的object!独一性才能保证线程安全限制成功){
//需要被同步的代码
}
说明:

需要被同步的代码,即为操作共享数据的代码
共享数据,即多个线程共同需要操作的数据,比如:ticket
需要被同步的代码,在被Synchronized包裹以后,就使得一个线程在操作这些代码的过程中,其他线程必须等待。
同步监视器,俗称锁。哪个线程获取了锁,哪个线程就能执行需要被同步的代码。
同步监视器可以使用任何一个类的对象充当。但是,多个线程必须共用同一个同步监视器。

package Thread;

public class TestWindow1 {
    public static void main(String[] args) {
        SaleTik x=new SaleTik();
        Thread t1 = new Thread(x);
        t1.start();
        Thread t2 = new Thread(x);
        t2.start();
        Thread t3 = new Thread(x);
        t3.start();
    }
}

class SaleTik implements Runnable{
int tik = 100;
Object obj = new Object();
//因为synchronized后面一定要带一个不限类型的变量
//用于不同的Thread一起share,所以这里随意建立了一个Object类型的obj

    @Override
    public void run() {


        while(true)
           {
               synchronized (obj) {

               if (tik > 0) {
                  // try {
                  //     Thread.sleep(100);
                  // } catch (InterruptedException e) {
                  //     throw new RuntimeException(e);
                  // }

                   System.out.println(Thread.currentThread().getName() + "售票,票号为" + tik);
                   tik--;
                   Thread.yield();//释放CPU,让其他thread有机会来一起抢票
               }

            else {
                       System.out.println("没有票了");
                       break;
                   }
               }
           }

    }
}



运行结果


Thread-0售票,票号为100
Thread-2售票,票号为99
Thread-2售票,票号为98
Thread-2售票,票号为97
Thread-2售票,票号为96
Thread-2售票,票号为95
Thread-2售票,票号为94
Thread-2售票,票号为93
Thread-2售票,票号为92
Thread-2售票,票号为91
Thread-2售票,票号为90
Thread-2售票,票号为89
Thread-2售票,票号为88
Thread-2售票,票号为87
Thread-2售票,票号为86
Thread-2售票,票号为85
Thread-2售票,票号为84
Thread-1售票,票号为83
Thread-1售票,票号为82
Thread-1售票,票号为81
Thread-1售票,票号为80
Thread-1售票,票号为79
Thread-1售票,票号为78
Thread-1售票,票号为77
Thread-1售票,票号为76
Thread-1售票,票号为75
Thread-1售票,票号为74
Thread-1售票,票号为73
Thread-1售票,票号为72
Thread-1售票,票号为71
Thread-1售票,票号为70
Thread-1售票,票号为69
Thread-1售票,票号为68
Thread-1售票,票号为67
Thread-1售票,票号为66
Thread-1售票,票号为65
Thread-1售票,票号为64
Thread-1售票,票号为63
Thread-2售票,票号为62
Thread-2售票,票号为61
Thread-2售票,票号为60
Thread-2售票,票号为59
Thread-2售票,票号为58
Thread-2售票,票号为57
Thread-2售票,票号为56
Thread-2售票,票号为55
Thread-2售票,票号为54
Thread-2售票,票号为53
Thread-2售票,票号为52
Thread-2售票,票号为51
Thread-2售票,票号为50
Thread-2售票,票号为49
Thread-2售票,票号为48
Thread-2售票,票号为47
Thread-1售票,票号为46
Thread-1售票,票号为45
Thread-1售票,票号为44
Thread-1售票,票号为43
Thread-1售票,票号为42
Thread-1售票,票号为41
Thread-1售票,票号为40
Thread-1售票,票号为39
Thread-1售票,票号为38
Thread-0售票,票号为37
Thread-0售票,票号为36
Thread-0售票,票号为35
Thread-0售票,票号为34
Thread-0售票,票号为33
Thread-0售票,票号为32
Thread-0售票,票号为31
Thread-0售票,票号为30
Thread-0售票,票号为29
Thread-0售票,票号为28
Thread-0售票,票号为27
Thread-0售票,票号为26
Thread-0售票,票号为25
Thread-0售票,票号为24
Thread-0售票,票号为23
Thread-0售票,票号为22
Thread-0售票,票号为21
Thread-0售票,票号为20
Thread-0售票,票号为19
Thread-0售票,票号为18
Thread-0售票,票号为17
Thread-0售票,票号为16
Thread-0售票,票号为15
Thread-1售票,票号为14
Thread-1售票,票号为13
Thread-2售票,票号为12
Thread-2售票,票号为11
Thread-2售票,票号为10
Thread-2售票,票号为9
Thread-2售票,票号为8
Thread-2售票,票号为7
Thread-2售票,票号为6
Thread-2售票,票号为5
Thread-2售票,票号为4
Thread-2售票,票号为3
Thread-2售票,票号为2
Thread-2售票,票号为1
没有票了
没有票了
没有票了

Process finished with exit code 0


方式2:同步方法
说明:用extends Thread的方式完成多线程情景,三个窗口轮流售100张票。

继续构造三个窗口轮流售票的代码:

package Thread;


public class TestWindow2 {
    public static void main(String[] args) {

        SaleTick x = new SaleTick();SaleTick y = new SaleTick();SaleTick z = new SaleTick();
        x.start();
        y.start();
        z.start();
    }
}

class SaleTick extends Thread{
//class SaleTick implements Runnable{

static int tickets =100;
    //static Object obj = new Object(); //需要确定obj是唯一的才能用于构造synchronized,可以用

    @Override
    public void run() {
        while(true){
        //synchronized (this) {//this:此时表示xyz,不能保证唯一性,不可以用
       // synchronized (obj) {//obj:使用static保证唯一性 可以用
            synchronized (SaleTick.class) {//(Class sth_balabla = SaleTick.class)
                // 定义时候用SaleTick.class看起来是一个类,并不是个object啊?
                // 其实,大写的Class的对象就是具体的某个类
                //(Class 常量值 = SaleTick.class)
                //类型 类型的变量名 = SaleTick.class
                // 大写的Class的对象(SaleTick.class)就是具体的某个类
                // 所以这里SaleTick.class并不认为是一个类,本质上是一个数值,保证唯一性,可以用来控制thread切换

                if(tickets>0){
                    //让其他线程也有机会购票
                   // try {
                   //     Thread.sleep(200);
                   // } catch (InterruptedException e) {
                   //     throw new RuntimeException(e);
                   // }

                    System.out.println(Thread.currentThread().getName()+"售票,票号为:"+tickets);

                    tickets--;
                    Thread.yield();//让其他线程机会也有机会购票
                }
                else break;
            }

        }


    }

}



运行结果看到,三个窗口在轮流售票:


Thread-0售票,票号为:100
Thread-0售票,票号为:99
Thread-0售票,票号为:98
Thread-0售票,票号为:97
Thread-0售票,票号为:96
Thread-0售票,票号为:95
Thread-0售票,票号为:94
Thread-0售票,票号为:93
Thread-0售票,票号为:92
Thread-0售票,票号为:91
Thread-0售票,票号为:90
Thread-0售票,票号为:89
Thread-2售票,票号为:88
Thread-2售票,票号为:87
Thread-2售票,票号为:86
Thread-2售票,票号为:85
Thread-2售票,票号为:84
Thread-2售票,票号为:83
Thread-2售票,票号为:82
Thread-2售票,票号为:81
Thread-2售票,票号为:80
Thread-2售票,票号为:79
Thread-2售票,票号为:78
Thread-2售票,票号为:77
Thread-2售票,票号为:76
Thread-2售票,票号为:75
Thread-2售票,票号为:74
Thread-2售票,票号为:73
Thread-2售票,票号为:72
Thread-2售票,票号为:71
Thread-2售票,票号为:70
Thread-2售票,票号为:69
Thread-2售票,票号为:68
Thread-2售票,票号为:67
Thread-2售票,票号为:66
Thread-2售票,票号为:65
Thread-2售票,票号为:64
Thread-2售票,票号为:63
Thread-2售票,票号为:62
Thread-2售票,票号为:61
Thread-2售票,票号为:60
Thread-2售票,票号为:59
Thread-2售票,票号为:58
Thread-2售票,票号为:57
Thread-2售票,票号为:56
Thread-2售票,票号为:55
Thread-2售票,票号为:54
Thread-2售票,票号为:53
Thread-2售票,票号为:52
Thread-2售票,票号为:51
Thread-2售票,票号为:50
Thread-2售票,票号为:49
Thread-2售票,票号为:48
Thread-1售票,票号为:47
Thread-1售票,票号为:46
Thread-1售票,票号为:45
Thread-1售票,票号为:44
Thread-1售票,票号为:43
Thread-1售票,票号为:42
Thread-1售票,票号为:41
Thread-1售票,票号为:40
Thread-1售票,票号为:39
Thread-1售票,票号为:38
Thread-1售票,票号为:37
Thread-1售票,票号为:36
Thread-1售票,票号为:35
Thread-1售票,票号为:34
Thread-1售票,票号为:33
Thread-1售票,票号为:32
Thread-1售票,票号为:31
Thread-1售票,票号为:30
Thread-1售票,票号为:29
Thread-1售票,票号为:28
Thread-1售票,票号为:27
Thread-1售票,票号为:26
Thread-1售票,票号为:25
Thread-1售票,票号为:24
Thread-1售票,票号为:23
Thread-1售票,票号为:22
Thread-1售票,票号为:21
Thread-1售票,票号为:20
Thread-1售票,票号为:19
Thread-1售票,票号为:18
Thread-1售票,票号为:17
Thread-1售票,票号为:16
Thread-1售票,票号为:15
Thread-1售票,票号为:14
Thread-1售票,票号为:13
Thread-1售票,票号为:12
Thread-1售票,票号为:11
Thread-1售票,票号为:10
Thread-1售票,票号为:9
Thread-1售票,票号为:8
Thread-1售票,票号为:7
Thread-1售票,票号为:6
Thread-1售票,票号为:5
Thread-1售票,票号为:4
Thread-1售票,票号为:3
Thread-1售票,票号为:2
Thread-1售票,票号为:1

Process finished with exit code 0


总结:
在实现implements Runnable接口的方式中,同步监视器可以考使用this比如写成代码synchronized (this){XXX}
在继承extends Thread类的方式中,同步监视器要慎用this,可以考虑使用当前类.class,比如写成代码块 synchronized (SaleTick.class){XXX}这个一般都是肯定唯一的。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/508058.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

HarmonyOS实战开发-目标管理、如何实现一个自定义弹窗。

介绍 本篇Codelab将介绍如何使用State、Prop、Link、Watch、Provide、Consume管理页面级变量的状态,实现对页面数据的增加、删除、修改。要求完成以下功能: 实现一个自定义弹窗,完成添加子目标的功能。实现一个可编辑列表,可点击…

商品服务 - 三级分类

1.递归查询树形结构 Overridepublic List<CategoryEntity> listWithTree() {//1.查出所有分类List<CategoryEntity> all this.list();//2.组装成父子的属性结构List<CategoryEntity> level1Menus all.stream().filter(c -> c.getParentCid().equals(0L)…

2004-2022年上市公司企业战略激进度数据(含原始数据+计算代码+计算结果)

2004-2022年上市公司企业战略激进度数据&#xff08;含原始数据计算代码计算结果&#xff09; 1、时间2004-2022年 2、来源&#xff1a;原始数据整理自csmar 3、指标&#xff1a; 证券代码、统计截止日期、员工人数、证券简称、报表类型、固定资产净额、无形资产净额、资产…

算法学习——LeetCode力扣动态规划篇6(121. 买卖股票的最佳时机、122. 买卖股票的最佳时机 II、123. 买卖股票的最佳时机 III)

算法学习——LeetCode力扣动态规划篇6 121. 买卖股票的最佳时机 121. 买卖股票的最佳时机 - 力扣&#xff08;LeetCode&#xff09; 描述 给定一个数组 prices &#xff0c;它的第 i 个元素 prices[i] 表示一支给定股票第 i 天的价格。 你只能选择 某一天 买入这只股票&…

Transformers -- 深入研究 - part 3

公众号:Halo咯咯,欢迎关注~ 前文回顾: Transformers -- 以通俗易懂的方式解释 - Part 1Transformers -- 未知英雄 - Part 2世界正在为人工智能和生成式人工智能而疯狂,特别是 2023 年的 ChatGPT 和大型语言模型。在我们讨论本系列后续部分的技术细节之前,让我们先从它的想…

用Servlet实现一个简单的表白墙

1. 准备工作 创建项目,引入依赖...... 将静态页面放到项目中(放在webapp目录下): 当前,这个表白墙页面,已经可以输入内容,点击提交之后也能显示内容,后续后端要做的工作即: ①存档 用户点提交的时候,把刚才输入的内容通过网络传输给服务器,由服务器保存这个数据. ②读档 …

NAT地址转换内外网通信

实验要求&#xff1a;内网地址通过nat转换成外网地址&#xff0c;联通外网服务器&#xff0c;达到内网外网互通 拓扑结构&#xff1a; 配置完成后&#xff0c;在ar1的G1口设置抓包&#xff0c;在pc1设备上ping ar2的地址&#xff0c;通过查看抓包信息&#xff0c;可以看到访问…

49 el-input 的 模型 视图 双向同步

前言 这里来看一下 el-input 这边的 数据 和 视图的双向绑定 最开始 我以为 这部分的处理应该是 vue 这边实现的, 但是跟踪调试了一下 发现这部分的处理是业务这边 自己实现的 这部分 还是有一些 值得记录的东西, 从这里 要去理解的而是 vue 这边从宏观的框架上面来说 帮我们…

python如何画奥运五环

绘制奥运五环主要涉及到Python中的turtle绘图库运用&#xff1a; 程序源代码为&#xff1a; import turtle turtle.width(10) turtle.color(black) turtle.circle(50) turtle.penup() turtle.goto(120,0) turtle.pendown() turtle.color(red) turtle.circle(50) turtle.penup()…

HWOD:整型数组排序

一、知识点 while(1){}表示永久循环 使用break结束循环 二、题目 1、描述 输入整型数组和排序标识&#xff0c;对其元素按照升序或降序进行排序 2、数据范围 1<n<1000 0<val<100000 3、输入 第一行输入数组元素个数 第二行输入待排序的数组&#x…

第十四届蓝桥杯(八题C++ 题目+代码+注解)

目录 题目一&#xff08;日期统计 纯暴力&#xff09;&#xff1a; 代码&#xff1a; 题目二&#xff08;01串的熵 模拟&#xff09;&#xff1a; 代码&#xff1a; 题目三&#xff08;治炼金属&#xff09;&#xff1a; 代码&#xff1a; 题目四&#xff08;飞机降落 深度…

【JAVASE】学习数组的定义与使用

✅作者简介&#xff1a;大家好&#xff0c;我是橘橙黄又青&#xff0c;一个想要与大家共同进步的男人&#x1f609;&#x1f609; &#x1f34e;个人主页&#xff1a; 再无B&#xff5e;U&#xff5e;G-CSDN博客 目标&#xff1a; 1. 理解数组基本概念 2. 掌握数组的基本用法…

星际门计划:微软与OpenAI联手打造未来AI超级计算机

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…

windows安装mysql

win r cmd 以管理员权限运行。数据库配置文件my.ini放到安装包里&#xff0c;配置文件内路径根据实际情况修改&#xff0c;配置文件字符集根据实际需要修改。 1、cd c:\mysql\bin切换目录 2、mysqld --initialize --console 初始化数据库&#xff0c;初始化完成…

[操作系统课设]GeeKOS操作系统的研究与实现

一.GeekOS操作系统概论 1.1教学操作系统 &#xff08;1&#xff09;针对RISC结构MIPS处理器 操作系统&#xff1a;Nachos、OS/161 &#xff08;2&#xff09;针对CISC结构Intel IA-32 (or x86)通用处理 操作系统&#xff1a;MINIX、GeekOS 我们用到的是&#xff1a;GeekOS 1&…

SpringMvc执行流程源码解析

一、简介 Spring web Mvc是基于ServletApi构建的原始Web模块&#xff0c;从一开始就包含在Spring框架中&#xff1b; 从Servlet到SpringMvc 最典型的MVc就是JSPServletjavaBean的模式&#xff1b; 弊端&#xff1a; 1、xml下配置Servlet的映射非常麻烦&#xff0c;效率低&…

OpenHarmony实战:命令行工具hdc安装应用指南

一、工具概述 hdc&#xff08;OpenHarmony Device Connector&#xff09;是为开发人员提供的用于设备连接调试的命令行工具&#xff0c;该工具需支持部署在 Windows/Linux/Mac 等系统上与 OpenHarmony 设备&#xff08;或模拟器&#xff09;进行连接调试通信。 简言之&#xf…

16进制的字符串转byte[]数组 以及将字节数组转换成十六进制的字符串

16进制的字符串转byte[]数组 public class ClientString16 {@Testpublic void get16Str(){String str="48 47 12 00 14 12 16 08 15 0d 30 0f 02 30 30 30 30 30 30 30 30 30 30 00 c2";byte[] bytes = hexStringToByteArray(str);getBytetoString(bytes);//String …

Redis实战篇-添加优惠卷

3.3 添加优惠卷 每个店铺都可以发布优惠券&#xff0c;分为平价券和特价券。平价券可以任意购买&#xff0c;而特价券需要秒杀抢购&#xff1a; tb_voucher&#xff1a;优惠券的基本信息&#xff0c;优惠金额、使用规则等 tb_seckill_voucher&#xff1a;优惠券的库存、开始抢…

(一)基于IDEA的JAVA基础10

相信最近许多朋友学习语言可能会有焦虑&#xff0c;“现在人工智能这么发达&#xff0c;丢个指令进去它就还给你一个结果&#xff0c;我们学习它还有意义吗&#xff1f;”。 对于这个问题&#xff0c;就像我们小学学习算数&#xff0c;我们明知道有计算器这么方便的东西&#…