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;
    @Override
    public 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;
    @Override
    public void run() {
        //
        while (true) {//死循环
            //两个线程都进入到了循环了
            //此时两个线程所持有的ticket 都是50
            //但是两个线程都要往下执行
            //有可能线程1 先执行了sout(50)  线程2在等待哦!!!
            //线程1执行了--操作并出了循环 线程1ticket = 49
            //线程1又抢到循环了 sout(49) tiket--
            //再进入倒这个循环,有可能线程2抢到这个执行权
            //线程2要往下执行输出语句  ticket=50 打印50
            if (ticket > 0) {
                //线程具有抢占式的运行
                //咱们有没有可能,线程3进入到if语句
                //此时线程1也进入到if语句了
                //线程3去打印 卖出了50张票
                //在线程1里面  ticket=50
                //线程3又抢到ticket-- 又进入到循环了  ticket = 49
                //线程3又抢到了ticket-- 又进入倒循环 ticket=48
                //线程1又抢到资源要执行,执行输出语句  tiekct=50
                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();


    }
}

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方法外面等待
    @Override
    public  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;
    @Override
    public 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();等等,大家得多花点时间,静下心看代码,写代码,多理解。

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

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

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

相关文章

ElasticSearch 8.x 使用 snapshot(快照)进行数据迁移

ElasticSearch 1、ElasticSearch学习随笔之基础介绍 2、ElasticSearch学习随笔之简单操作 3、ElasticSearch学习随笔之java api 操作 4、ElasticSearch学习随笔之SpringBoot Starter 操作 5、ElasticSearch学习随笔之嵌套操作 6、ElasticSearch学习随笔之分词算法 7、ElasticS…

儿童护眼台灯怎么选择?一文教你如何选择儿童护眼台灯

护眼台灯是家长最常为孩子购买的用品之一,但是大部分人对它的了解并不多,很多人购买之后反而会觉得眼睛更容易疲劳,有不适的情况!最主要的原因是因为挑选的台灯不够专业,次要原因则是使用方法不正确。所以今天跟大家讲…

BI是什么?

企业SaaS服务2B或者其他2C,数据都会越集越多,丰富的数据可视化展示成为销售及老板们的需求;当然通过如下BI了解 你需要思考是不是只是能做、想做、值得做一个,如果写简单的数据统计,至于用不用的上BI都另说&#xff0…

本地部署GeoServe服务并结合内网穿透实现任意浏览器远程访问

文章目录 前言1.安装GeoServer2. windows 安装 cpolar3. 创建公网访问地址4. 公网访问Geo Servcer服务5. 固定公网HTTP地址 前言 GeoServer是OGC Web服务器规范的J2EE实现,利用GeoServer可以方便地发布地图数据,允许用户对要素数据进行更新、删除、插入…

C#学习笔记_类(Class)

类的定义 类的定义是以关键字 class 开始,后跟类的名称。类的主体,包含在一对花括号内。 语法格式如下: 访问标识符 class 类名 {//变量定义访问标识符 数据类型 变量名;访问标识符 数据类型 变量名;访问标识符 数据类型 变量名;......//方…

【Linux】-多线程的知识都收尾(线程池,封装的线程,单例模式,自旋锁)

💖作者:小树苗渴望变成参天大树🎈 🎉作者宣言:认真写好每一篇博客💤 🎊作者gitee:gitee✨ 💞作者专栏:C语言,数据结构初阶,Linux,C 动态规划算法🎄 如 果 你 …

深度剖析Sentinel热点规则

欢迎来到我的博客,代码的世界里,每一行都是一个故事 深度剖析Sentinel热点规则 前言核心概念解析:数字守护者的起源核心概念解析:简单示例演示: 参数索引:规则的基石参数索引的作用:不同场景下选…

2024美国大学生数学建模美赛选题建议+初步分析

总的来说&#xff0c;去年算是美赛环境题元年&#xff0c;去年的开放度是较高的&#xff0c;今年每种赛题类型相对而言平均了起来 提示&#xff1a;DS C君认为的难度&#xff1a;E<BCF<AD&#xff0c;开放度&#xff1a;DBCE<A<F。 以下为A-F题选题建议及初步分析…

小型内衣裤洗衣机哪个牌子好?家用小型洗衣机推荐

相信对于很多用户而言&#xff0c;宁愿强撑着疲惫的身子手洗内衣裤&#xff0c;也不愿把内衣裤与外穿衣物一起放进洗衣机洗。内衣裤与外穿衣物的脏污情况不同&#xff0c;内衣裤是贴身衣物&#xff0c;上面留有人体的汗液和分泌物&#xff0c;有可能带有大量真菌。而外衣上则是…

动环系统断电告警的防误报

机房一般接入的市电为三相380伏特&#xff0c;也有用单向220伏特的。UPS本身提供断电告警的功能&#xff0c;这个告警在各种种类的UPS中都是提供的&#xff0c;不同电压的市电输入都支持&#xff1b;三相电另外有缺相告警事件。但这些告警事件存在抖动或者误判。 瞬间的低压或…

LiveGBS流媒体平台GB/T28181功能-支持配置开启 HTTPS 服务什么时候需要开启HTTPS服务

LiveGBS功能支持配置开启 HTTPS 服务什么时候需要开启HTTPS服务 1、配置开启HTTPS1.1、准备https证书1.1.1、选择Nginx类型证书下载 1.2、配置 LiveCMS 开启 HTTPS1.2.1 web页面配置1.2.2 配置文件配置 2、验证HTTPS服务3、为什么要开启HTTPS3.1、安全性要求3.2、功能需求 4、搭…

Linux基础知识合集

整理了一下学习的一些关于Linux的一些基础知识&#xff0c;同学们也可以通过公众号菜单栏查看&#xff01; 一、基础知识 Linux基础知识 Linux命令行基础学习 Linux用户与组概念初识 Linux文件与目录权限基础 Linux中文件内容的查看 Linux系统之计划任务管理 二、服务器管理 Vm…

Vue中使用 Element-ui form和 el-dialog 进行自定义表单校验清除表单状态

文章目录 问题分析 问题 在使用 Element-ui el-form 和 el-dialog 进行自定义表单校验时&#xff0c;出现点击编辑按钮之后再带年纪新增按钮&#xff0c;出现如下情况&#xff0c;新增弹出表单进行了一次表单验证&#xff0c;而这时不应该要表单验证的 分析 在寻找多种解决…

深信服技术认证“SCCA-C”划重点:深信服云计算关键技术

为帮助大家更加系统化地学习云计算知识&#xff0c;高效通过云计算工程师认证&#xff0c;深信服特推出“SCCA-C认证备考秘笈”&#xff0c;共十期内容。“考试重点”内容框架&#xff0c;帮助大家快速get重点知识。 划重点来啦 *点击图片放大展示 深信服云计算认证&#xff08…

【无刷电机学习】电流采样电路硬件方案

【仅作自学记录&#xff0c;不出于任何商业目的】 目录 AD8210 INA282 INA240 INA199 AD8210 【AD8210数据手册】 在典型应用中&#xff0c;AD8210放大由负载电流通过分流电阻产生的小差分输入电压。AD8210抑制高共模电压(高达65V)&#xff0c;并提供接地参考缓冲输出&…

Trie树数据结构——(字符串统计,最大异或对)

Trie树&#xff1a;是一种能够高效存储和查找字符串集合的数据结构 Trie字符串统计 思路&#xff1a; &#xff08;笔记来自AcWing 835. Trie字符串统计 - AcWing&#xff09; 代码如下&#xff1a; #include<iostream> #include<cstdio> #include<string>…

华为FreeClip耳机可以调节音量大小吗?附教程!

不会只有我一个人吧&#xff1f;都用华为FreeClip耳机一段时间了&#xff0c;才发现它竟然不支持在耳机上直接调节音量&#xff0c;也是没谁了&#xff01;但是后来自己摸索了一下&#xff0c;发现了华为FreeClip耳机原来是几个简单有效的调节音量大小的方法滴~不得不说&#x…

计算机视觉:高级图像处理,满足您的所有需求。

一、说明 特征提取是机器学习管道中的关键步骤&#xff0c;可增强模型在不同数据集上的泛化和良好表现能力。特征提取方法的选择取决于数据的特征和机器学习任务的具体要求。本文揭示图像处理的数学原理&#xff0c;实现增强的计算机视觉 二、关于计算机视觉的普遍问题 在计算机…

Nginx 多项目部署,vue刷新404 解决方案

网上找的资料大多都解决不了&#xff0c;废话不多说直接告诉你解决方法。 环境是 TP6 VUE前端官网 VUE 后台管理 部署 两个项目 刷新 404 解决方案 Nginx 配置 直接贴图 如果解决了&#xff0c;给我顶起来&#xff0c;让更多人 快速的解决。

借力华为云CodeArts,使用软件开发生产线快速搭建项目

前言 项目的实际开发&#xff0c;研发接到需求并不是立马进入开发的&#xff0c;实际的开发生成流程是一个完整的迭代流程。 流程的节点和每个节点的内容如下&#xff1a; 开发生产的流程很标准很规范&#xff0c;看似研发只需要按照流程执行每一步的操作即可。但实际开发中&…