Java学习day24:线程的同步和锁(例题+知识点详解)

声明:该专栏本人重新过一遍java知识点时候的笔记汇总,主要是每天的知识点+题解,算是让自己巩固复习,也希望能给初学的朋友们一点帮助,大佬们不喜勿喷(抱拳了老铁!)


往期回顾

Java学习day23:线程构造方法、常用方法(例题+知识点详解)-CSDN博客

Java学习day22:进程和线程、并发并行、线程创建方式(知识点详解)-CSDN博客

Java学习day21:System类、Runtime类、Date类、Calendar类(知识点详解)-CSDN博客

 Java学习day24:线程的同步和锁

一、同步和锁

1.为什么要进行线程的同步?

Java是允许多线程(多个线程),当多个线程操作同一个资源(咋操作)的时候,会导致得到或者打印的数据不准确。从而发生冲突。咋解决?加同步锁。

2.多线程操作导致数据不准确

比如,当三个线程美团、淘票票、猫眼同时卖票, 美团、淘票票这个两个线程,都去卖同一场次的票,结果美团卖出去一张1排1列的票
结果淘票票也卖出去了1排1列的票  你感觉合适吗?不合适的,分享同一个资源的时候,要保证分享资源的数据,合法性!!!否则就会出现数据的混乱,就是下面的这种结果!!! 

示例:


class MySync implements Runnable {int ticket = 50;@Overridepublic void run() {//while (true) {//死循环if (ticket > 0) {System.out.println(Thread.currentThread().getName() + "卖出了第" + ticket + "票");ticket--;} else {System.out.println("买完了");break;//终止循环!!!}}}
}
public class Demo1 {public static void main(String[] args) {MySync mySync = new MySync();//这三个线程Thread thread1 = new Thread(mySync, "线程1");thread1.start();Thread thread2 = new Thread(mySync, "线程2");thread2.start();Thread thread3 = new Thread(mySync, "线程3");thread3.start();}
}

运行结果:

这里就是,三个线程同时分享同一个资源,也就是卖同一组票,此时理想的情况应该是:先线程1进入到ticket=50,循环循环结束以后此时tiket=49了,循环第二次的时候线程2抢到资源了 此时ticket=49循环 打印49 tiket-- ticket=48了,循环第三次的时候 线程2 抢到资源了, 此时ticket=48......就这样循环下去。

而实际上会出现什么问题呢?

两个线程都进入到了循环了,此时两个线程的ticket都是50,但是两个线程都会往下执行,可能的状态就是,线程3抢占到cpu资源,循环一遍,ticket为49,然后线程3再次抢占到cpu资源,继续执行一遍循环,一直到线程3执行了4次循环,线程1才终于抢占到cpu资源,执行循环,但是由于线程1是在ticket为50的时候进入循环的,只是一直在等待,此时线程1输出的就是ticket=50

好好理解这个原理,线程的抢占式执行带来的结果,但是抢占式能够将cpu最大化的利用,此时怎么在继续抢占式的同时避免数据的混乱,这就引入了锁的概念。

示例:

//最理想的状态!!!
//先线程1进入到ticket=50,循环 循环结束以后 此时
//tiket=49了
//循环第二次的时候 线程2抢到资源了 此时ticket=49
//循环 打印49 tiket-- ticket=48了
//循环第三次的时候 线程2 抢到资源了, 此时ticket=48
//打印卖第48张票。ticket--  tiket=47
//线程1又抢到循环
//现在的情况是:有可能两个线程同时进入到while循环
//
class MySync implements Runnable {int ticket = 50;@Overridepublic void run() {//while (true) {//死循环//两个线程都进入到了循环了//此时两个线程所持有的ticket 都是50//但是两个线程都要往下执行//有可能线程1 先执行了sout(50)  线程2在等待哦!!!//线程1执行了--操作并出了循环 线程1ticket = 49//线程1又抢到循环了 sout(49) tiket--//再进入倒这个循环,有可能线程2抢到这个执行权//线程2要往下执行输出语句  ticket=50 打印50if (ticket > 0) {//线程具有抢占式的运行//咱们有没有可能,线程3进入到if语句//此时线程1也进入到if语句了//线程3去打印 卖出了50张票//在线程1里面  ticket=50//线程3又抢到ticket-- 又进入到循环了  ticket = 49//线程3又抢到了ticket-- 又进入倒循环 ticket=48//线程1又抢到资源要执行,执行输出语句  tiekct=50System.out.println(Thread.currentThread().getName() + "卖出了第" + ticket + "票");ticket--;} else {System.out.println("买完了");break;//终止循环!!!}}}
}
public class Demo1 {public static void main(String[] args) {MySync mySync = new MySync();//这三个线程Thread thread1 = new Thread(mySync, "线程1");thread1.start();Thread thread2 = new Thread(mySync, "线程2");thread2.start();Thread thread3 = new Thread(mySync, "线程3");thread3.start();}
}

3.解决方法 

3.1.同步方法:使用一个关键字synchronized修饰方法。

因为Java对象都有一个内置的锁对象。当使用这个关键字修饰方法的时候,这个方法就会被锁保护起来,被锁锁住,当一个线程进来以后,会立马锁住当前的方法。意味着只有一个线程进来,其他线程都在外面等着。

语法格式:

public synchronized  void run () {

}

我们把run方法加上锁,但是你会发现,最后所有的票都是一个线程买完的,原因很简单,一个线程进去了其他的进不去,进去的那个会一直占有资源直到结束,才会释放其占有的资源,也就意味着会把票卖完,这就不符合生活场景,比如三个平台卖票的,结果有一个平台抢到后就不松手,自己一个人卖完了所有。

3.2同步代码块

就是用了synchronized  关键字修饰一个语句块。被修饰的语句块会被加锁。从而实现同步。 

语法格式:

synchronized  (this) {
 被加锁的代码块
}

这个时候我们再想,锁哪一部分的代码块,肯定不能锁while循环,不然放进去一个线程,这个线程要把while循环执行完才出来。

示例:


class MySync2 implements Runnable {int ticket = 500;//对这个run方法加了锁 就意味着只有一个线程进入到run方法中//其他线程都在run方法外面等待@Overridepublic  void run() {//能不能对循环加锁?不能 因为循环加锁以后,还是一个线程循环完,没有任何意义while (true) {//死循环//if语句加了锁以后//就意味着只有一个线程进入到if语句//假如线程1进入if语句了,线程2和线程3就会等待//线程1打印50 并--  ticket变量为49//线程2抢到了49 sout(49) tiket--  48//其他线程再抢!!!//核心业务 加了锁,只让一个线程进入,操作完以后。锁释放掉//然后这三个线程再抢。还只能进一个,再操作核心业务synchronized (this) {//只能让一个线程进入操作,其他线程在外面等待排队if (ticket > 0) {System.out.println(Thread.currentThread().getName() + "卖出了第" + ticket + "票");ticket--;} else {System.out.println("买完了");break;//终止循环!!!}}}}
}
public class Demo3 {public static void main(String[] args) {MySync2 mySync = new MySync2();//这三个线程Thread thread1 = new Thread(mySync, "线程1");thread1.start();Thread thread2 = new Thread(mySync, "线程2");thread2.start();Thread thread3 = new Thread(mySync, "线程3");thread3.start();}
}

所以在这个案例里,真正应该锁的,是while循环里面的if语句,三个线程都能进while循环,但是只有一个线程能进if语句执行卖票操作,执行完就要释放资源,然后三个线程又抢,看哪个能进去。

所以很重要的一点,需要准确知道把锁加在哪里。


总而言之,加锁的目的为了保证数据的准确性。
案例:

卖电影票:
 三个线程:
  淘票票
  美团
  猫眼
  100张票 

参考:

class SaleTicket implements Runnable {//声明一个变量票//静态的变量和对象没有关系了private static int ticket = 100;@Overridepublic void run() {while (true) {//美团  猫眼  淘票票synchronized (this) {if (ticket > 0) {System.out.println(Thread.currentThread().getName() + "卖出了第" + ticket + "票");    ticket--;} else {System.out.println("卖完了");break;}}}}
}
public class Demo4 {public static void main(String[] args) {SaleTicket saleTicket = new SaleTicket();new Thread(saleTicket, "淘票票").start();new Thread(saleTicket, "美团").start();new Thread(saleTicket, "猫眼").start();}
}

线程就是这样,抢占式运行导致不可控制,但是可以加锁。让他可控制。


以上,就是今天的所有知识点了。锁的问题,是Java中非常重要的核心,大家一定要自己去百度一些资料,增长自己的见识 ,比如lock();  unlock();等等,大家得多花点时间,静下心看代码,写代码,多理解。

加油吧,预祝大家变得更强!

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

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

相关文章

Matlab图像模拟加噪——高斯噪声、椒盐噪声、泊松噪声、乘性噪声、均匀噪声、指数噪声

1.高斯噪声 (1)通过均值和方差来产生 Jimnoise(I, gaussian, 0, 0.01);%高斯噪声,均值为0,方差为0.01(2)通过位置信息来产生 Iim2double(I); Vzeros(size(I)); %建立矩阵V for i1:size(V, 1)V(i,:)0.02*i/size(V,1); end Jimnoise(I, localvar, V); …

Linux安装aria2出现No package aria2 available.的解决方案

大家好,我是爱编程的喵喵。双985硕士毕业,现担任全栈工程师一职,热衷于将数据思维应用到工作与生活中。从事机器学习以及相关的前后端开发工作。曾在阿里云、科大讯飞、CCF等比赛获得多次Top名次。现为CSDN博客专家、人工智能领域优质创作者。喜欢通过博客创作的方式对所学的…

9.回文数

回文数 将整型转换为字符型反转前一半是否等于后一半将数字本身反转输入一个整数 x,如果 x是一个回文整数,返回 true;否则,返回 false 。 回文数是指正序(从左向右)和倒序(从右向左)读都是一样的整数。 例如,121 是回文,而 123 不是。 将整型转换为字符型 反转…

牛客,OR36 链表的回文结构,快慢指针和反转链表的实践

链表的回文结构_牛客题霸_牛客网 (nowcoder.com) 还是比较简单的,主要分为三个步骤,两种需掌握的函数实现 目录 主要思路过程,1,找到中间结点,2,反转中间结点往后的结点,3,遍历比…

双非本科准备秋招(13.1)—— 力扣 栈、队列与堆

1、103. 二叉树的锯齿形层序遍历 昨天做的二叉树的层序遍历,把代码直接拿过来。 这个题要求的是一个Z型遍历,如下图。 用一个变量f记录正反顺序,然后使用LinkedList记录答案,下图可以看到LinkedList继承了Deque,所以…

Python算法题集_除自身以外数组的乘积

Python算法题集_除自身以外数组的乘积 题239:除自身以外数组的乘积1. 示例说明2. 题目解析- 题意分解- 优化思路- 测量工具 3. 代码展开1) 标准求解【暴力求解】2) 改进版一【字典改进乘积计算】3) 改进版二【字典改进乘积计算预计算数字乘积】4) 改进版三【前缀乘积…

第二集《修道宗范》

请大家打开讲义第5页,我们讲到丙一、先修出离心。 身为一个有情众生,我们内心当中,有一个没办法改变的本性,我们就是希望生命能够离苦得乐,这是没办法改变的。换句话说,我们每一个人都喜欢安乐&#xff0c…

代码随想录算法训练营Day20|最大二叉树、合并二叉树、二叉搜索树中的搜索、验证二叉搜索树

最大二叉树 题目: 最大二叉树定义: 二叉树的根是数组中的最大元素 左子树是通过数组中最大值左边部分构造出的最大二叉树。 右子树是通过数组中最大值右边部分构造出的最大二叉树。 通过给定的数组构建最大二叉树,并且输出这个树的节点…

【JavaScript】Generator

MDN-Generator Generator对象由生成器函数返回,并且它符合可迭代协议和迭代器协议。 Generator-核心语法 核心语法: 定义生成器函数获取generator对象yield表达式的使用通过for of获取每一个yield的值 // 1. 通过function* 创建生成器函数 function* foo() {//…

Unity DOTween插件常用方法(一)

文章目录 1.1 控制Api1.2 动画Api 1.1 控制Api DOKill DOKill表示停止该物体上所有的Tween动画。DOTween可以同时运行多个Tween,如果需要停止所有正在运行的Tween,可以使用这个方法; 还有一种使用场景,即反复打开某一视图,而该视…

Java技术栈 —— Hadoop入门(二)实战

Java技术栈 —— Hadoop入门(二) 一、用MapReduce对统计单词个数1.1 项目流程1.2 可能遇到的问题1.3 代码勘误1.4 总结 一、用MapReduce对统计单词个数 1.1 项目流程 (1) 上传jar包。 (2) 上传words.txt文件。 (3) 用hadoop执行jar包的代码,…

【C++】 C++入门 — auto关键字

C入门 auto 关键字1 介绍2 使用细则3 注意事项 Thanks♪(・ω・)ノ谢谢阅读下一篇文章见!!! auto 关键字 1 介绍 编程时常常需要把表达式的值赋给变量,这就要求在声明变量时清楚地知道表达式的类…

56-主,回调函数,回调函数的参数传参,函数和变量的公私有,特权方法,立即执行函数,闭包(解除引用)

1.回调函数 定义了函数,没有调用函数,但最终执行了。 <script>//回调函数// 定时器setInterval(function(){console.log("a")},1000)// 延迟器setTimeout(function(){console.log("a")},3000)</script> 2.将实参变为函数,将person方法作为…

什么是多态?它和重载有什么区别?

前言 大家好&#xff0c;我是chowley&#xff0c;相信学过编程语言的你&#xff0c;肯定听说过多态和重载两个概念&#xff0c;可多数人对他们之间的区别还是不太清晰&#xff0c;导致同时听到两个词一起出现时会大脑空白&#xff0c;今天我就来详细的介绍一下二者的区别&…

指针的深入理解(四)

这节主要讨论sizeof和strlen的区别&#xff0c;以及一些理解题。 sizeof 求的是对象的大小&#xff0c;深入理解一点就是&#xff1a;这个对象&#xff0c;他一定有一块对应的内存空间。求的就是这一块内存空间。 strlen 只能用来求字符串&#xff0c; 求取的是字符串的长度。…

面试了字节大模型算法岗(实习),快被问哭了。。。。

最近技术群组织了一次算法面试讨论会&#xff0c;今天分享的是一位小伙子的痛苦面试经历&#xff0c;如果你想加入我们的讨论群&#xff0c;见文末。 本次分享的内容如下&#xff1a; 应聘岗位&#xff1a;字节大模型算法实习生 面试轮数&#xff1a;第一轮 整体面试感觉&…

python封装的.exe文件是如何在cmd中获取.xml路径的?

这段日子搞项目算法封装&#xff0c;愁死我。来回改了三遍&#xff0c;总算把相对路径、绝对路径&#xff0c;还有cmd给.exe传参的方式搞懂了。 主要是这个语句 workspace sys.argv[1] sys.argv[]的作用就是,在运行python文件的时候从外部输入参数往文件里面传递参数。 外部就…

CTF盲水印工具:Blind-WaterMark安装

工具下载地址&#xff1a;GitCode - 开发者的代码家园 下载完毕后&#xff0c;只留这些东西就行 接下来需要安装两个依赖&#xff1a; opencv、matplotlib 直接pip install安装的话&#xff0c;工具使用会报错 所以需要到网站里挑选适合的版本进行安装 下载地址&#xff1…

项目:博客

1. 运行环境&#xff1a; 主机 主机名 系统 服务 192.168.223.129 Server_Web Linux Web 192.168.48.131 Server-NFS-DNS Linux NFS/DNS 2. 基础配置 配置主机名&#xff0c;静态IP地址 开启防火墙并配置 部分开启SElinux并配置 服务器之间使用同ntp.aliyun.com进行…

代码随想录算法训练营第二十二天|235. 二叉搜索树的最近公共祖先 ● 701.二叉搜索树中的插入操作 ● 450.删除二叉搜索树中的节点

235. 二叉搜索树的最近公共祖先 发现规律&#xff1a; 当我们从上向下去递归遍历&#xff0c;第一次遇到 cur节点是数值在[p, q]区间中&#xff0c;那么cur就是p和q的最近公共祖先。 class Solution { public:TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, Tr…