大纲:java线程知识体系

这是不安全的问题代码

/*
Windows模拟的是售票窗口类
共享数据:多个线程共同操作的数据,即本案例中的tocketNum*/
public class Windows implements Runnable {private static int ticketNum = 10;@Overridepublic void run() {String name = Thread.currentThread().getName();while (true){if(ticketNum > 0) {try {//这一步是为了演示错票,原理是当前线程进入了if语句陷入沉睡的时候票被卖光,//然后当该线程苏醒时再来一次ticketNum--产生0号这个非法票Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(name +"卖出第" + ticketNum + "张票");ticketNum--;}}}
}
/*
在main中,windows可以理解为一个售票站点,总共十张票,这个站点开了三个售票窗口window1,window2,window3
到时间获后,用户涌入从这个三个窗口抢票。这是个不安全的售票程序,所以抢票过程中会出现重票,错票....*/
class ThreadTest{public static void main(String[] args) {Windows windows = new Windows();Thread window1 = new Thread(windows);window1.setName("窗口1");Thread window2 = new Thread(windows);;window2.setName("窗口2");Thread window3 = new Thread(windows);window3.setName("窗口3");window2.start();window1.start();window3.start();}
}

最终会出现重票,错票,漏票或者三种情况都有

其中最极端的状况是卖出两张错票(卖出0号票和-1号票)

在ticketNum值为1的状态时,三个线程同时进入了if语句,最后输出1,0,-1
0和-1是错票,就是如上图分析的结果

线程安全问题是由共享数据造成的,就好比上公厕,如果每个坑位都是私人vip专属的那么就不会出现线程安全问题。但现实中无论多少人(线程)排队,只要你锁好厕门(资源锁)就不会出现线程安全问题,无论排队的多么着急都要一个个来。反之,你在一个生意爆满的公厕上厕所不锁门,然后三四个人(线程)涌入并同时操作一个坑位(共享数据)才真的会出问题…
解决方案是每个坑位都要有一把门锁,无论谁抢到坑位,第一时间先上锁防止别人进来打扰你,然后你就可以在上锁期间为所欲为。

方法一、同步代码块

class Windows implements Runnable {private static int ticketNum = 10;//这个位置才能保证锁的唯一性Object obj = new Object();@Overridepublic void run() {String name = Thread.currentThread().getName();while (true){//这里也推荐用this(this代表的是main中的windows对象)或Windows.classsynchronized (obj){if(ticketNum > 0) {try {//这一步是为了演示错票,原理是当前线程进入了if语句陷入沉睡的时候票被卖光,//然后当该线程苏醒时再来一次ticketNum--产生0号这个非法票Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(name + "卖出第" + ticketNum + "张票");ticketNum--;}}}}
}

同步方法二 :同步方法

/*
思路与同步代码块一致,同步方法是将操作共享数据的代码提取出来定义为synchronized类型的方法
同步方法默认用的同步锁是this(main中的windows对象),不需要显示声明
静态同步方法默认监视器:this
非静态同步方法监视器:class对象*/
public class Windows implements Runnable {private static int ticketNum = 10;private Object obj = new Object();public synchronized void show(){String name = Thread.currentThread().getName();if(ticketNum > 0) {try {//这一步是为了演示错票,原理是当前线程进入了if语句陷入沉睡的时候票被卖光,//然后当该线程苏醒时再来一次ticketNum--产生0号这个非法票Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(name +"卖出第" + ticketNum + "张票");ticketNum--;}}@Overridepublic void run() {while (true){show();}}
}

同步方法三 ReetrantLock锁

/*
synchronized vs ReetrantLock
共同点:二者都可以解决线程安全问题
不同:前者同步范围是固定的,需要执行完相应的同步代码块时自动释放锁资源,
而后者无论是上锁还是解锁都是需要手动指定的,这就意味着后者用起来更加灵活*/
public class Windows implements Runnable {private static int ticketNum = 10;private ReentrantLock lock = new ReentrantLock();@Overridepublic void run() {while (true){try {//代码运行到此处上同步锁lock.lock();String name = Thread.currentThread().getName();if(ticketNum > 0) {try {//这一步是为了演示错票,原理是当前线程进入了if语句陷入沉睡的时候票被卖光,//然后当该线程苏醒时再来一次ticketNum--产生0号这个非法票Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(name +"卖出第" + ticketNum + "张票");ticketNum--;}} catch (Exception e) {e.printStackTrace();} finally {//解锁lock.unlock();}}}
}

死锁
两个或两个以上的进程在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程

例如:你(线程1)占据了坑位(s1)但你没纸(s2),外面的人(线程2)有纸(s2)但坑位(s1)被你占了,你打算让他给你纸你再给他坑位,但他要求你让出坑位再给你纸,大家都在等待对方先放弃,这样你俩都僵持住,无法完成"任务"。

/*
1 该案例基本每次运行都会死锁,死锁的运行结果就是控制台不进行任何输出
2 我们用sleep方法可以大大提高提高死锁概率,不加sleep也有可能发生死锁,只是概率很低
*/
public class DeadLockTest {public static void main(String[] args) {StringBuilder s1 = new StringBuilder();StringBuilder s2 = new StringBuilder();new Thread(new Runnable() {@Overridepublic void run() {synchronized (s1){s1.append("a");s2.append("1");//CPU快速的线程切换和数据处理能力使得上锁解锁都是一瞬间完成,基本不会产生死锁,除非使用sleep先卡住上面的程序//基于CPU的快速执行,我们在第一个线程t1中获取了锁s1时相应的t2获取了锁资源s2,但由于此时t1想要的s2在t2手中,//t2想要的s1在t1手中,他们不会主动释放锁也得不到锁;造成死锁try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}synchronized (s2){s1.append("b");s2.append("2");System.out.println(s1);System.out.println(s2);}}}}).start();new Thread(new Runnable() {@Overridepublic void run() {synchronized (s2){s1.append("c");s2.append("3");synchronized (s1){s1.append("d");s2.append("4");System.out.println(s1);System.out.println(s2);}}}}).start();}
}

总结:
锁的使用: 同步方法<同步代码块<lock

分不清静态同步方法和普通同步方法的看我的这篇博客:Synchronized同步静态方法和非静态方法、同步块。同样的案例,同样的风格,通俗易懂;

同步方法vs同步代码块①同步方法,它们所拥有的锁就是该方法所属的类的对象锁,换句话说,也就是this对象,如果是静态同步方法那么它的锁就是当前的运行时类对象,也就是当前类的class类型对象;而同步块可以更加精确的选择对象锁②同步方法使用关键字synchronized修饰方法而后者使用synchornize修饰代码块 ③同步块可以更精确的控制锁的作用域,锁的作用域就是从锁被获取到其被释放的时间。同步方法锁的作用域是整个方法,这可能导致其锁的作用域可能太大降低程序的运行效率

同步方法工作原理:因为同步方法的工作原理是基于java的每个对象都具有同步锁,调用同步方法的同时就会获取到到当前对象的同步锁,所以普通的同步方法默认使用this,所有的非静态同步方法用的都是同一把锁——实例对象本身。静态同步方法(static本身就不能和this扯上关系)默认使用当前类的class对象。如果获取不到相关的锁那么当前线程就会处于阻塞状态;普通同步方法锁住整个方法,静态同步方法锁住整个类(直接锁类对象相当于这把锁对全部的线程对象都有效)

同步代码块工作原理:被修饰的代码块相当于被加上了内置锁;实际开发中,我们们通常都会将共享资源的维护与操作单独定义成一个共享资源类(Clerk),该类中的方法必须是同步的才能有条不紊的被多线程访问,所以,这些共享方法中锁的都是当前资源类对象this =>不同的对象有各自的数据,为了统一管理我们就把clerk的获取设置为单例模式资源都是在run方法中调用共享资源类的对象的方法;
补充:synchronized只能修饰代码块和方法不能修饰构造器,因为构造器本身就是用来初始化对象的(分配空间 + 写入对象值),该过程由于JVM工作机制问题本身就类似于同步,所以不能被synchronized修饰

Lock vs 同步①同步方法全局只有一把锁和一把钥匙,最多能用两个线程(生产者消费者案例)协同工作,而lock更为强大,可以为每一个对象分配一把锁,当前线程执行完毕可以(根据钥匙condition)任意选择其中的线程将其唤醒并工作,也就是lock可以使n个线程顺序执行
②lock需要手动解锁unlock而同步方式是方法/代码块执行完自动解锁 =>lock方法更具有灵活性,lock是主流的同步方法,例如CurrentHashMap就是使用了lock技术

java多线程的安全问题与死锁(面向厕所编程)相关推荐

  1. java多线程通信基础(面向厕所编程)

    大纲:java线程知识体系 一.sleep与wait的区别,先举例,再理论! 公厕(公厕的坑位是共享资源)中无论多少人(人是线程)排队,只要你锁好厕门(synchronized)就不会出现线程安全问题 ...

  2. Java多线程——线程安全问题

    一.什么情况下会产生线程安全问题? 同时满足以下两个条件时: 1,多个线程在操作共享的数据. 2,操作共享数据的线程代码有多条. 当一个线程在执行操作共享数据的多条代码过程中,其他线程参与了运算,就会 ...

  3. Java多线程 - 线程安全问题

    文章目录 1. 什么是线程安全和线程不安全? 2. 自增运算为什么不是线程安全的? 3. 临界区资源和竞态条件 面试题: 什么是线程安全和线程不安全? 自增运算是不是线程安全的?如何保证多线程下 i+ ...

  4. java多线程(同步和死锁,生产者和消费者问题)

    首先我们来看看同步与死锁: 所谓死锁.这是A有banana,B有apple. A至B说:你把apple对我来说,,我会banana给你. B至A说:你把banana对我来说,,我会apple给你. 可 ...

  5. Java——多线程(线程安全问题)

    同步为安全,不同步为不安全:也就是有synchronized这个标识符,就为线程安全,反之,为线程不安全. ①Vector是线程安全的 ②StringBuffer是线程安全的 ③Hashtable是线 ...

  6. java多线程-基础

    什么是进程和线程 进程:启动一个QQ.exe就叫一个进程. 接着又启动一个360.exe,这叫两个进程.以此类推,每个独立执行的程序都称为进程.可以从Windows任务管理器中查看 进程栏里 Java ...

  7. Java多线程:多线程同步安全问题的 “三“ 种处理方式 ||多线程 ”死锁“ 的避免 || 单例模式”懒汉式“的线程同步安全问题

    Java多线程:多线程同步安全问题的 "三" 种处理方式 ||多线程 "死锁" 的避免 || 单例模式"懒汉式"的线程同步安全问题 每博一文 ...

  8. java多线程的安全_java-多线程的安全问题

    在实际应用中,我们通常会遇到多线程安全问题, 涉及到两个因素: 1,多个线程在操作共享数据. 2,有多条语句对共享数据进行运算. 原因:这多条语句,在某一个时刻被一个线程执行时,还没有执行完,就被其他 ...

  9. Java多线程基础-6:线程安全问题及解决措施,synchronized关键字与volatile关键字

    线程安全问题是多线程编程中最典型的一类问题之一.如果多线程环境下代码运行的结果是符合我们预期的,即该结果正是在单线程环境中应该出现的结果,则说这个程序是线程安全的. 通俗来说,线程不安全指的就是某一代 ...

最新文章

  1. Gartner:2019年十大数据与分析技术趋势
  2. CCF 2020年题目题解 - Python
  3. 知识图谱最新权威综述论文解读:时序知识图谱部分
  4. NYOJ题目171-聪明的kk(dp)
  5. C# 获取结构体长度 指针转结构体 指针转结构体数组
  6. 部署Azkaban多节点分布式模式
  7. Windows 配置libjpeg-turbo并在python中调用
  8. cubase手机版android,Cubase中文应用
  9. android客户端与服务器端的搭建,android客户端与服务器端的搭建.ppt
  10. SQL Server阻塞与锁
  11. 一家互联网创业公司的“估值”是如何来的
  12. 数理统计复习:统计量及其分布(3)充分统计量
  13. 组网方案设计,运用Mesh组网实现无缝漫游!
  14. Linux系统管理---权限管理
  15. 华科2020计算机专业录取线,华中科技大学2020录取分数线是多少
  16. 打造个人版微信小程序(1)——本地开发api接口调用
  17. Adobe Photoshop CS5.1 Extended 12.1中文特别版
  18. C语言课设会员计费系统(大作业)
  19. 计算机教师资格教案,小学信息技术教师资格证面试教案:操作系统新相识
  20. 与自己赛跑 迎5G而上 ,九州云做边缘计算实力玩家

热门文章

  1. .NET串口通讯解决方案
  2. javascript一些面试常用的问题总结
  3. 【ES6】模块功能的实现--export / import 命令
  4. Node.js「三」—— 创建静态 WEB 服务器
  5. android imageview图片旋转动画,Android 安卓动画 属性动画 - 旋转动画
  6. auto的作用c语言,auto指的是什么意思
  7. Redis Sentinel安装与部署,实现redis的高可用
  8. Selenium2+python自动化75-非input文件上传(SendKeys)
  9. 13章 购买服务器和域名绑定
  10. 跳台阶问题:动态规划,公式