Java多线程学习二

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

引出:

多线程卖票,出现的问题:出现了重票和错票

 原因分析:

线程操作ticket的过程中,尚未结束的情况下,其他线程也参与进来,对ticket进行操作。

如何解决多线程不安全的问题

必须保证一个线程a在操作ticket的过程中,其它线程必须等待,直到线程a操作ticket结束以后,其它线程才可以进来继续操作ticket。

Java是如何解决线程的安全问题的?使用线程的同步机制。

方式1:同步代码块

synchronized(同步监视器){
    //需要被同步的代码
}

说明:


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

> 共享数据:即多个线程都需要操作的数据。比如:ticket

> 需要被同步的代码,在被synchronized包裹以后,就使得一个线程在操作这些代码的过程中,其

它线程必须等待。

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

> 同步监视器,可以使用任何一个类的对象充当。但是,多个线程必须共用同一个同步监视器。

注意:在实现Runnable接口的方式中,同步监视器可以考虑使用:this

     在继承Thread类的方式中,同步监视器要慎用this,可以考虑使用:当前类.class

方式2:同步方法

说明:


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

重要:
> 非静态的同步方法,默认同步监视器是this静态的同步方法,默认同步监视器是当前类本身。

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

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

方式三:同步锁

调用方法luck()和unlock()来对共享数据区域进行同步操作

死锁问题

原因:

不同的线程分别占用对方需要的同步资源不放弃,都在等待对方放弃自己需要的同步资源,就形成了线程的死锁

条件:

 互斥条件
占用且等待
不可抢夺(或不可抢占)
循环等待

满足所有一才会导致死锁

代码举例:

class A {public synchronized void foo(B b) {System.out.println("当前线程名: " + Thread.currentThread().getName()+ " 进入了A实例的foo方法");try {Thread.sleep(200);} catch (InterruptedException ex) {ex.printStackTrace();}System.out.println("当前线程名: " + Thread.currentThread().getName()+ " 企图调用B实例的last方法");b.last();}public synchronized void last() {System.out.println("进入了A类的last方法内部");}
}
class B {public synchronized void bar(A a) {System.out.println("当前线程名: " + Thread.currentThread().getName()+ " 进入了B实例的bar方法");try {Thread.sleep(200);} catch (InterruptedException ex) {ex.printStackTrace();}System.out.println("当前线程名: " + Thread.currentThread().getName()+ " 企图调用A实例的last方法");a.last();}public synchronized void last() {System.out.println("进入了B类的last方法内部");}
}public class DeadLock implements Runnable {A a = new A();B b = new B();public void init() {Thread.currentThread().setName("主线程");// 调用a对象的foo方法a.foo(b);System.out.println("进入了主线程之后");}public void run() {Thread.currentThread().setName("副线程");// 调用b对象的bar方法b.bar(a);System.out.println("进入了副线程之后");}public static void main(String[] args) {DeadLock dl = new DeadLock();new Thread(dl).start();dl.init();}
}

测试:

public class DeadLockTest {public static void main(String[] args) {StringBuilder s1 = new StringBuilder();StringBuilder s2 = new StringBuilder();new Thread(){@Overridepublic void run() {synchronized (s1){s1.append("a");s2.append("1");try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}synchronized (s2){s1.append("b");s2.append("2");System.out.println(s1);System.out.println(s2);}}}}.start();new Thread(){@Overridepublic void run() {synchronized (s2){s1.append("c");s2.append("3");try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}synchronized (s1){s1.append("d");s2.append("4");System.out.println(s1);System.out.println(s2);}}}}.start();}
}

线程通信

理解:

当我们`需要多个线程`来共同完成一件任务,并且我们希望他们有规律的执行,那么多线程之间需要一些通信机制,可以协调它们的工作,以此实现多线程共同操作一份数据

通信方法

wait():线程一旦执行此方法,就进入等待状态。同时,会释放对同步监视器的调用
notify():一旦执行此方法,就会唤醒被wait()的线程中优先级最高的那一个线程。(如果被wait()的多个线程的优先级相同,则随机唤醒一个)。被唤醒的线程从当初被wait的位置继续执行
notifyAll():一旦执行此方法,就会唤醒所有被wait的线程

代码举例:

class PrintNumber implements Runnable{private int number = 1;Object obj = new Object();@Overridepublic void run() {while(true){//            synchronized (this) {synchronized (obj) {obj.notify();if(number <= 100){try {Thread.sleep(20);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName() + ":" + number);number++;try {obj.wait(); //线程一旦执行此方法,就进入等待状态,同时,会释放对同步监视器的调用,释放} catch (InterruptedException e) {e.printStackTrace();}}else{break;}}}}
}public class PrintNumberTest {public static void main(String[] args) {PrintNumber p = new PrintNumber();Thread t1 = new Thread(p,"线程1");//创建对象Thread t2 = new Thread(p,"线程2");t1.start();t2.start();}
}

新增的多线程创建的方式

实现Callable

call()可以有返回值,更灵活
call()可以使用throws的方式处理异常,更灵活(可以直接抛出异常
Callable使用了泛型参数,可以指明具体的call()的返回值类型,更灵活

实现Callable的缺点:

如果在主线程中需要获取分线程call()的返回值,则此时的主线程是阻塞状态

使用线程池来创建多线程

优点:

提高了程序执行的效率。(因为线程已经提前创建好了)(优点像饿汉式)
提高了资源的复用率。(因为执行完的线程并未销毁,而是可以继续执行其他的任务)
可以设置相关的参数,对线程池中的线程的使用进行管理

代码:

class NumberThread implements Runnable{@Overridepublic void run() {for(int i = 0;i <= 100;i++){if(i % 2 == 0){System.out.println(Thread.currentThread().getName() + ": " + i);}}}
}class NumberThread1 implements Runnable{@Overridepublic void run() {for(int i = 0;i <= 100;i++){if(i % 2 != 0){System.out.println(Thread.currentThread().getName() + ": " + i);}}}
}public class ThreadPool {public static void main(String[] args) {//1. 提供指定线程数量的线程池ExecutorService service = Executors.newFixedThreadPool(10);ThreadPoolExecutor service1 = (ThreadPoolExecutor) service;
//        //设置线程池的属性
//        System.out.println(service.getClass());//ThreadPoolExecutorservice1.setMaximumPoolSize(50); //设置线程池中线程数的上限//2.执行指定的线程的操作。需要提供实现Runnable接口或Callable接口实现类的对象service.execute(new NumberThread());//适合适用于Runnableservice.execute(new NumberThread1());//适合适用于Runnable//        service.submit(Callable callable);//适合使用于Callable//3.关闭连接池service.shutdown();}}

这里的线程池没有做过多的解释

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

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

相关文章

Tülu 3:重新定义开源大模型的后训练范式

一、引言 在大型语言模型&#xff08;LLM&#xff09;的发展历程中&#xff0c;预训练阶段往往受到最多关注&#xff0c;动辄需要数百万美元算力投入和数万亿token的训练数据。然而&#xff0c;一个鲜为人知但同样关键的事实是&#xff1a;预训练完成的模型实际上并不能直接投…

【机器学习】机器学习的基本分类-监督学习-逻辑回归(Logistic Regression)

逻辑回归是一种分类算法&#xff0c;尽管名字中包含“回归”&#xff0c;但其主要用于解决二分类和多分类问题。它通过学习一个逻辑函数&#xff0c;预测输入属于某个类别的概率。 1. 逻辑回归的基本概念 目标 逻辑回归的目标是找到一个函数 h(x)&#xff0c;输出一个概率值 …

PyMOL操作手册

PyMOL 操作手册 The man will be silent, the woman will be tears. – itwangyang ​ 翻译整理&#xff1a;itwangyanng 2024 年 11月 29 日 目录 初识 PyMOL… 5 0.1 安装 PyMOL… 5 0.1.1 Windows 系统开源版 PyMOL 的安装… 5 0.1.2 教育版 PyMOL 的下载安装……

麒麟系统x86安装达梦数据库

一、安装准备前工作 操作系统&#xff1a;银河麒麟V10&#xff0c;CPU&#xff1a; x86_64 架构 下载地址&#xff0c;麒麟官网&#xff1a;https://www.kylinos.cn/ 数据库&#xff1a;dm8_20220915_x86_kylin10_64 下载地址&#xff0c;达梦数据库官网&#xff1a;https://…

Hot100 - 搜索二维矩阵II

Hot100 - 搜索二维矩阵II 最佳思路&#xff1a; 利用矩阵的特性&#xff0c;针对搜索操作可以从右上角或者左下角开始。通过判断当前位置的元素与目标值的关系&#xff0c;逐步缩小搜索范围&#xff0c;从而达到较高的效率。 从右上角开始&#xff1a;假设矩阵是升序排列的&a…

docker服务容器化

docker服务容器化 1 引言2 多个容器间网络联通2.1 单独创建关联2.2 创建时关联 3 服务搭建3.1 镜像清单3.2 容器创建 4 联合实战4.2 flink_sql之kafka到starrocks4.2 flink_sql之mysql到starrocks 5 文献借鉴 1 引言 ​ 利用docker可以很效率地搭建服务&#xff0c;本文在win1…

011变长子网掩码

变长子网掩码&#xff1a; 使用变长子网掩码&#xff08;VLSM&#xff09;优化地址分配 目标&#xff1a; 根据需求使用VLSM分配IP地址&#xff0c;减少浪费&#xff0c;并配置静态路由。 网络拓扑 创建一个包含三台路由器&#xff08;R1、R2、R3&#xff09;和五台PC&#x…

SpringBoot小知识(2):日志

日志是开发项目中非常重要的一个环节&#xff0c;它是程序员在检查程序运行的手段之一。 1.日志的基础操作 1.1 日志的作用 编程期调试代码运营期记录信息&#xff1a; * 记录日常运营重要信息(峰值流量、平均响应时长……) * 记录应用报错信息(错误堆栈) * 记录运维过程数据(…

大数据新视界 -- 大数据大厂之 Hive 数据安全:权限管理体系的深度解读(上)(15/ 30)

&#x1f496;&#x1f496;&#x1f496;亲爱的朋友们&#xff0c;热烈欢迎你们来到 青云交的博客&#xff01;能与你们在此邂逅&#xff0c;我满心欢喜&#xff0c;深感无比荣幸。在这个瞬息万变的时代&#xff0c;我们每个人都在苦苦追寻一处能让心灵安然栖息的港湾。而 我的…

智能探针技术:实现可视、可知、可诊的主动网络运维策略

网络维护的重要性 网络运维是确保网络系统稳定、高效、安全运行的关键活动。在当今这个高度依赖信息技术的时代&#xff0c;网络运维的重要性不仅体现在技术层面&#xff0c;更关乎到企业运营的方方面面。网络运维具有保障网络的稳定性、提升网络运维性能、降低企业运营成本等…

RT-DETR融合Inner-IoU及相关改进思路

RT-DETR使用教程&#xff1a; RT-DETR使用教程 RT-DETR改进汇总贴&#xff1a;RT-DETR更新汇总贴 《Inner-IoU: More Effective Intersection over Union Loss with Auxiliary Bounding Box》 一、 模块介绍 论文链接&#xff1a;https://arxiv.org/abs/2311.02877 代码链接&a…

在Springboot项目中实现将文件上传至阿里云 OSS

oss介绍 阿里云对象存储服务&#xff08;OSS&#xff09;是一种高效、安全和成本低廉的数据存储服务&#xff0c;可以用来存储和管理海量的数据文件。本文将教你如何使用 Java 将文件上传到阿里云 OSS&#xff0c;并实现访问文件。 1. 准备工作 1.1 开通 OSS 服务 登录阿里云…

Java项目中加缓存

Java项目中加缓存 1.更新频率低&#xff1b;但读写频率高的数据很适合加缓存&#xff1b; 2.可以加缓存的地方很多&#xff1a;浏览器的缓存&#xff1b;CDN的缓存&#xff1b;服务器的缓存&#xff1b; 本地内存&#xff1b;分布式远端缓存&#xff1b; 加缓存的时候不要…

Vuex —— Day1

vuex概述 vuex是vue的状态管理工具&#xff0c;可以帮我们管理vue通用的数据&#xff08;多组件共享的数据&#xff09; vuex的应用场景&#xff1a; 某个状态在很多个组件中都会使用&#xff08;eg.个人信息&#xff09;多个组件共同维护一份数据&#xff08;eg.购物车&…

【前端】Next.js 服务器端渲染(SSR)与客户端渲染(CSR)的最佳实践

关于Next.js 服务器端渲染&#xff08;SSR&#xff09;与客户端渲染&#xff08;CSR&#xff09;的实践内容方面&#xff0c;我们按下面几点进行阐述。 1. 原理 服务器端渲染 (SSR): 在服务器上生成完整的HTML页面&#xff0c;然后发送给客户端。这使得用户在首次访问时能够…

基于FPGA的FM调制(载波频率、频偏、峰值、DAC输出)-带仿真文件-上板验证正确

基于FPGA的FM调制-带仿真文件-上板验证正确 前言一、FM调制储备知识载波频率频偏峰值个人理解 二、代码分析1.模块分析2.波形分析 总结 前言 FM、AM等调制是学习FPGA信号处理一个比较好的小项目&#xff0c;通过学习FM调制过程熟悉信号处理的一个简单流程&#xff0c;进而熟悉…

Scala学习记录,统计成绩

统计成绩练习 1.计算每个同学的总分和平均分 2.统计每个科目的平均分 3.列出总分前三名和单科前三名&#xff0c;并保存结果到文件中 解题思路如下&#xff1a; 1.读入txt文件&#xff0c;按行读入 2.处理数据 &#xff08;1&#xff09;计算每个同学的总分平均分 import s…

路由策略与路由控制实验

AR1、AR2、AR3在互联接口、Loopback0接口上激活OSPF。AR3、AR4属于IS-IS Area 49.0001&#xff0c;这两者都是Level-1路由器&#xff0c;AR3、AR4的系统ID采用0000.0000.000x格式&#xff0c;其中x为设备编号 AR1上存在三个业务网段A、B、C&#xff08;分别用Loopback1、2、3接…

第J7周:对于RenseNeXt-50算法的思考

&#x1f368; 本文为&#x1f517;365天深度学习训练营 中的学习记录博客&#x1f356; 原作者&#xff1a;K同学啊 文章目录 一、前言1、导入包2、分组卷积模块3、残差单元4、堆叠残差单元5、搭建ResNeXt-50网络 二、问题思考 电脑环境&#xff1a; 语言环境&#xff1a;Pyth…

某充电桩业务服务内存监控和程序行为分析

原作者&#xff1a;展贝 原文地址&#xff1a;https://mp.weixin.qq.com/s/nnYCcVtwowvmj7Zn9XLIUg 在当今数据驱动的环境中&#xff0c;理解内存指标和程序行为对于确保应用程序的性能和可靠性至关重要。在依赖实时数据处理和高可用性的行业中尤其如此。通过利用可观测工具&am…