Java,多线程,线程的同步机制

线程的安全问题与线程的同步机制

以火车站买票的问题来举例。假设火车站有100张票,分三个窗口售卖这一百张票。

分别用继承Thread类和实现Runnable接口的方式:

实现Runnable接口:

public class WindowTest
{public static void main(String[] args){//线程的安全问题SaleTicket ss = new SaleTicket();Thread t1 = new Thread(ss,"窗口一");Thread t2 = new Thread(ss,"窗口二");Thread t3 = new Thread(ss,"窗口三");t1.start();t2.start();t3.start();//这种情况下,会出现重票错票的问题}
}
class SaleTicket implements Runnable
{private int ticket = 100;public int getTicket(){return ticket;}@Overridepublic void run(){while(true){if(ticket > 0){try{Thread.sleep(10);} catch (InterruptedException e){e.getStackTrace();}System.out.println(Thread.currentThread().getName() + "卖出一张票,票号为" + ticket);ticket--;}else{break;}}}
}

继承Thread类:

public class WindowTest2
{public static void main(String[] args){//线程的安全问题Window w1 = new Window("窗口一");Window w2 = new Window("窗口二");Window w3 = new Window("窗口三");w1.start();w2.start();w3.start();//同样会出现重票错票的问题}
}class Window extends Thread
{public Window(){}public Window(String name){super(name);}private static int ticket = 100;@Overridepublic void run(){while(true){if(ticket > 0){try{Thread.sleep(10);} catch (InterruptedException e){e.getStackTrace();}System.out.println(Thread.currentThread().getName() + "卖出一张票,票号为" + ticket);ticket--;}else{break;}}}
}

分别运行可以发现:两中方式卖票时都会出现重票错票的问题。

出现这种现象的原因:

例如,线程一(窗口一)操作ticket的过程中,尚未结束的情况下,其他的线程也参与进来对ticket进行操作。

要解决这种问题,必须保证一个线程(假设是线程一)在操作ticket的过程中,其他线程必须等待,直到线程一操作ticket结束以后,其他线程才可以进来继续操作ticket。

要解决线程的安全问题,就要使用线程的同步机制。

方式一:同步代码块

格式:

synchronized(同步监视器){

//需要被同步的代码

//……

}

说明:

·需要被同步的代码,即为操作共享数据的代码。

·共享数据:即多个线程都需要操作的数据。

·需要被同步的代码,在被synchronized包裹以后,就使得一个线程在操作这些代码的过程中,其他线程必须等待。

·同步监视器,俗称锁,哪个线程获取了锁,哪个线程就能执行需要被同步的代码。

·同步监视器,可以使用任何一个类的对象充当。多个线程(这些线程要执行同一段需要被同步的代码)必须共用同一个同步监视器。

(同步监视器类似于交通信号灯,多个线程共用同一个同步监视器即多个线程看同一个交通信号灯,多个线程都是只有获取到这个锁之后才能执行相应需要被执行的代码)

·实现Runnable的方式创建线程的方式同步监视器使用this最方便,继承Thread类的方式创建线程的方式使用  当前类名.class  最方便。

使用同步代码块后的卖票的代码如下:

实现Runnable接口:

public class WindowTest
{public static void main(String[] args){//用同步代码块线程的安全问题SaleTicket ss = new SaleTicket();Thread t1 = new Thread(ss,"窗口一");Thread t2 = new Thread(ss,"窗口二");Thread t3 = new Thread(ss,"窗口三");t1.start();t2.start();t3.start();//这种情况下,会出现重票错票的问题}
}
class SaleTicket implements Runnable
{private int ticket = 100;//Object o = new Object();public int getTicket(){return ticket;}@Overridepublic void run(){while(true){try{Thread.sleep(10);} catch (InterruptedException e){e.getStackTrace();}//synchronized(o){//如果多个线程要使用同一段同步的代码,同步监视器中的对象要保证这些线程使用同一个锁。synchronized(this)//同步监视器中可以放任何一个唯一对象,只要保证唯一。一般情况下放this最方便。{if(ticket > 0){try{Thread.sleep(10);//调用sleep代码只是为了更好的显示同步代码块时轮流买票的情况。} catch (InterruptedException e){e.getStackTrace();}System.out.println(Thread.currentThread().getName() + "卖出一张票,票号为" + ticket);ticket--;}else{break;}}}}
}

继承Thread类:

public class WindowTest2
{public static void main(String[] args){//线程的安全问题Window w1 = new Window("窗口一");Window w2 = new Window("窗口二");Window w3 = new Window("窗口三");w1.start();w2.start();w3.start();//同样会出现重票错票的问题}
}class Window extends Thread
{public Window(){}public Window(String name){super(name);}private static int ticket = 100;//static Object oo = new Object();@Overridepublic void run(){while(true){//synchronized (this)//w1,w2,w3使用的并非同一个同步监视器,相当于三个线程看的“交通信号灯”不是同一个。//synchronized (oo)synchronized (Window.class)//继承Thread类的方式,同步监视器建议使用  当前类名.class{if(ticket > 0){try{Thread.sleep(100);} catch (InterruptedException e){e.getStackTrace();}System.out.println(Thread.currentThread().getName() + "卖出一张票,票号为" + ticket);ticket--;}else{break;}}}}
}

方式二:同步方法

如果操作共享数据的代码完整地声明在了一个方法中,那么我们就可以将此方法声明为同步方法。(在方法声明中加上synchronized)

·非静态的同步方法,默认同步监视器为this

·静态的同步方法,默认同步监视器是  当前类名.class  。

具体代码如下:

实现Runnable接口:

public class WindowTest
{public static void main(String[] args){//线程的安全问题//使用同步方法解决实现Runnable接口的方式的线程安全问题SaleTicket ss = new SaleTicket();Thread t1 = new Thread(ss,"窗口一");Thread t2 = new Thread(ss,"窗口二");Thread t3 = new Thread(ss,"窗口三");t1.start();t2.start();t3.start();}
}
class SaleTicket implements Runnable
{private int ticket = 100;public int getTicket(){return ticket;}@Overridepublic void run(){boolean b = true;//判断ticket是否大于0while(b){show(b);}}public synchronized void show(boolean b)//此时的同步监视器是this{if(ticket > 0){try{Thread.sleep(10);} catch (InterruptedException e){e.getStackTrace();}System.out.println(Thread.currentThread().getName() + "卖出一张票,票号为" + ticket);ticket--;}else{b = false;}}
}

继承Thread类:

public class WindowTest2
{public static void main(String[] args){//线程的安全问题Window w1 = new Window("窗口一");Window w2 = new Window("窗口二");Window w3 = new Window("窗口三");w1.start();w2.start();w3.start();}
}class Window extends Thread
{public Window(){}public Window(String name){super(name);}private static int ticket = 100;@Overridepublic void run(){boolean b = true;while(b){show(b);}}//public synchronized void show(boolean b)//同步监视器还是默认为this,而this在这里三个不同的线程过来时分别指三个对象,不能解决问题public static synchronized void show(boolean b)//此时同步监视器:当前类名.class//注意:不是使用静态的情况不要使用此方式,应该继续使用同步代码块的方式{if(ticket > 0){try{Thread.sleep(10);} catch (InterruptedException e){e.getStackTrace();}System.out.println(Thread.currentThread().getName() + "卖出一张票,票号为" + ticket);ticket--;}else{b = false;}}
}

synchronized关键字

好处:解决了线程安全问题。

弊端:在操作共享数据指多线程其实是串行执行的,意味着性能低。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Git系列之分支与标签的使用及应用场景模拟

🎉🎉欢迎来到我的CSDN主页!🎉🎉 🏅我是君易--鑨,一个在CSDN分享笔记的博主。📚📚 🌟推荐给大家我的博客专栏《Git实战开发》。🎯🎯 &a…

Java学习_对象

对象在计算机中的执行原理 类和对象的一些注意事项 this关键字 构造器 构造器是一种特殊的方法 : 特殊之处在于,名字必须与所在类的名字一样,而且不能写返回值类型 封装 封装的设计规范:合理隐藏、合理暴露 实体类 成员变量和局部变量的区别 …

mybatisPlus update更新部分字段

第一种方式&#xff1a; 其中&#xff0c; lambdaUpdateWrapper.set 表示要更新的字段值。 .eq 则表示 WHERE 条件。 public void updateEntity() {// LambdaUpdateWrapper<TestEntity> lambdaUpdateWrapper new LambdaUpdateWrapper<>();//有些版本可能不兼容上…

微信聊天,收到二维码图片就自动帮你提取出来的方法

10-3 如果你是二维码收集的重度用户&#xff0c;那我非常推荐你好好阅读本文&#xff0c;也许可以帮你解决你的问题&#xff0c;比如做网推的人&#xff0c;需要常年混迹在各种微信群&#xff0c;那如何在各个微信群中收集到群友分享出来的二维码&#xff0c;并且要立即保存出…

SOLID 原则,程序设计五大原则,设计模式

SOLID 是让软件设计更易于理解、更加灵活和更易于维护的五个原则的简称。 单一职责(Single Responsibility Principle)&#xff1a;修改一个类的原因只能有一个。开闭原则(Open/Closed Principle)&#xff1a;对于扩展&#xff0c;类应该是“开放”的&#xff1b;对于修改&…

组件的设计原则

目录 插槽的基本概念 基础用法 具名插槽 使用场景 布局控制 嵌套组件 组件的灵活性 高级用法 作用域插槽 总结 前言 Vue 的 slot 是一项强大的特性&#xff0c;用于组件化开发中。它允许父组件向子组件传递内容&#xff0c;使得组件更加灵活和可复用。通过 slot&…

Python之函数进阶-nonlocal和LEGB

Python之函数进阶-nonlocal和LEGB nonlocal语句 nonlocal:将变量标记为不在本地作用域定义&#xff0c;而是在上级的某一级局部作用域中定义&#xff0c;但不能是全局作用域中定义。 函数的销毁 定义一个函数就是生成一个函数对象&#xff0c;函数名指向的就是函数对象。可…

在 React Router 中使用 JWT

在这篇文章中&#xff0c;我们将探讨 JWT 身份校验与 React 和 React-router 的无缝集成。 我们还将学习如何处理公共路由、受校验保护路由&#xff0c;以及如何利用 axios 库通过身份验证令牌&#xff08;token&#xff09;发出 API 请求。 创建一个 React 项目 使用下方的指…

华为云Ascend310服务器使用

使用华为云服务器 cpu: 16vCPUs Kunpeng 920 内存&#xff1a;16GiB gpu&#xff1a;4* HUAWEI Ascend 310 cann: 20.1.rc1 操作系统&#xff1a;Ubuntu aarch64目的 使用该服务器进行docker镜像编译&#xff0c;测试模型。 已知生产环境&#xff1a;mindx版本为3.0.rc3&a…

keep-alive缓存,三级路由不生效

此文章讲诉在vue中使用keep-alive缓存&#xff0c;三级路由缓存失败处理方案。 一二级路由缓存无任何问题&#xff0c;三级以上就会失败&#xff0c;因此我们在路由守卫中对matched做出如下优化 Router.beforeEach((to, from, next)>{if(to.matched && to.matched.l…

【机器学习】Kmeans聚类算法

一、聚类简介 Clustering (聚类)是常见的unsupervised learning (无监督学习)方法&#xff0c;简单地说就是把相似的数据样本分到一组&#xff08;簇&#xff09;&#xff0c;聚类的过程&#xff0c;我们并不清楚某一类是什么&#xff08;通常无标签信息&#xff09;&#xff0…

【PTE-day07 文件上传2】

1、常见的绕过方式 (1)畸形后缀名绕过 .php、.pht、.php3、.php4、.php5、.php2、.phtml、.pHp、.html、.Htm......(2)双写过滤字符绕过 (3).htaccess文件绕过 <FilesMatch "jpg"> SetHandler application/x-httpd-php

通义千问, 文心一言, ChatGLM, GPT-4, Llama2, DevOps 能力评测

引言 “克隆 dev 环境到 test 环境&#xff0c;等所有服务运行正常之后&#xff0c;把访问地址告诉我”&#xff0c;“检查所有项目&#xff0c;告诉我有哪些服务不正常&#xff0c;给出异常原因和修复建议”&#xff0c;在过去的工程师生涯中&#xff0c;也曾幻想过能够通过这…

目标检测工程化最佳实践:Python 并行条件下YOLOv8的模型推理,线程安全的模型推理!

文章大纲 YOLOv8模型的线程安全推理 背景简介Python 线程的一些理解共享模型实例的问题与危害!非线程安全的代码样例: 单个模型实例非线程安全的代码样例: 多个个模型实例YOLOv8 中线程安全的推理方式Thread-Safe Example 1Thread-Safe Example 2YOLOv8 主要开发人员的回复结论…

【FAQ】Gradle开发问题汇总

1. buildSrc依赖Spring Denpendency时报错 来自预编译脚本的插件请求不能包含版本号。请从有问题的请求中删除该版本&#xff0c;并确保包含所请求插件io.spring.dependency-management的模块是一个实现依赖项 解决方案 https://www.5axxw.com/questions/content/uqw0grhttps:/…

Flink之Catalog

Catalog Catalog概述Catalog分类 GenericInMemoryCatalogJdbcCatalog下载JAR包及使用重启操作创建Catalog查看与使用Catalog自动初始化catalog HiveCatalog下载JAR包及使用重启操作hive metastore服务创建Catalog查看与使用CatalogFlink与Hive中操作自动初始化catalog 用户自定…

基于springboot实现桥牌计分管理系统项目【项目源码】计算机毕业设计

基于springboot实现桥牌计分管理系统演示 JAVA简介 JavaScript是一种网络脚本语言&#xff0c;广泛运用于web应用开发&#xff0c;可以用来添加网页的格式动态效果&#xff0c;该语言不用进行预编译就直接运行&#xff0c;可以直接嵌入HTML语言中&#xff0c;写成js语言&#…

MYSQL操作详解

一)计算机的基本结构 但是实际上&#xff0c;更多的是这种情况: 二)MYSQL中的数据类型: 一)数值类型: 数据类型内存大小(字节)说明bit(M)M指定位数,默认为1单个二进制位值&#xff0c;或者为0或者为1&#xff0c;主要用于开/关标志tinyint1字节1个字节的整数值&#xff0c;支持…

KITTI数据集(.bin数据)转换为点云数据(.pcd文件)

目录 cmake代码 代码 cmake代码 cmake_minimum_required(VERSION 3.17) project(TEST2)set(CMAKE_CXX_STANDARD 14)# Find PCL find_package(PCL 1.8 REQUIRED)# If PCL was found, add its include directories to the project if(PCL_FOUND)include_directories(${PCL_INC…

使用openvc进行人脸检测:Haar级联分类器

1 人脸检测介绍 1.1 什么是人脸检测 人脸检测的目标是确定图像或视频中是否存在人脸。如果存在多个面&#xff0c;则每个面都被一个边界框包围&#xff0c;因此我们知道这些面的位置 人脸检测算法的主要目标是准确有效地确定图像或视频中人脸的存在和位置。这些算法分析数据…