同步、异步、互斥的区别

在很多文章中,直接把同步缩小为互斥,并称之为同步。下面也是这样的。

一、线程同步 = 队列 + 锁

同步(这里说的其实是互斥)就是多个线程同时访问一个资源。

那么如何实现? 队列+锁。

想要访问同一资源的线程排成一个队列,按照排队的顺序访问。访问的时候加上一个锁(参考卫生间排队+锁门),访问完释放锁。

二、 不安全案例

2.1 不安全的抢票系统

之前我们实现过这个例子。

package Unsafe;public class RailwayTicketSystem{public static void main(String[] args) {BuyTicket buyer = new BuyTicket();new Thread(buyer,"黑黑").start();new Thread(buyer,"白白").start();new Thread(buyer,"黄牛党").start();}
}class BuyTicket implements Runnable{private int ticketNums = 10;  //系统里有10张票//抢票行为@Overridepublic void run() {while(ticketNums>0){try {Thread.sleep(100);  //模拟延时,放大问题的发生性} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName()+"--->抢到了第"+ticketNums+"张票");ticketNums--;}}
}

2.2 不安全的银行取钱

场景: 黑土有一张存款为100万的卡,黑土去银行柜台取钱50万,同一时刻,黑土的老婆白云也要通过网上银行从卡里取走100万。

因为取钱是用户各自取各自账户里的钱,不存在多个线程操作同一个对象(所有用户都去抢系统里的票),所以可以用extends Thread。

package Unsafe;public class UnsafeBank {public static void main(String[] args) {//黑黑的卡里一共有100万Account  黑土的卡 = new Account("黑土的卡",100);//黑黑要从卡里取走50万DrawMoney 黑土 = new DrawMoney(黑土的卡,50);黑土.start();//同时,白白也来到了银行,白白要从卡里取走100万DrawMoney 白云 = new DrawMoney(黑土的卡, 100);白云.start();}
}//银行卡
class Account{private String name; //持卡人private int money ; //余额public Account(String name, int money) {this.name = name;this.money = money;}public String getName() {return name;}public void setName(String name) {this.name = name;}public int getMoney() {return money;}public void setMoney(int money) {this.money = money;}
}//银行:模拟取款
class DrawMoney extends Thread{Account account; //账户int drawMoney; //要取多少钱public DrawMoney(Account account,int drawMoney){this.account = account;this.drawMoney = drawMoney;}//取钱@Overridepublic void run() {if(account.getMoney()-drawMoney<0){System.out.println("余额已不足,【"+Thread.currentThread().getName()+"】无法取钱");return;}//延时,放大问题的发生try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}//余额变动account.setMoney(account.getMoney() - drawMoney);System.out.println(Thread.currentThread().getName()+"取走了"+drawMoney);//输出余额System.out.println(account.getMoney());}
}

2.3 不安全的集合

这里以ArraryList为例,我们知道ArraryList的底层是用数组存储的。当多个线程同时执行add方法时,会出现多个线程向数组的同一个位置存放数据的情况。

三、同步机制

由于我们可以用private关键字来保证变量只能被方法访问,所以我们只需要针对类似于getXx()方法提出一套机制,这套机制就是synchronized关键字,synchronized就能实现队列+锁机制。它包括两种用法

所以说,synchronized 锁的既是对象(的资源/成员变量)(临界资源),也是一段代码(临界区)

1. synchronized 方法

在方法前面加synchronized 关键字。

同步方法所属类所创建的每个对象,都有一把锁。

2. synchronized 块

如何使用?中括号括起来临界区,小括号内填上临界资源。

​​​​​​​        

一个方法中同时存在读取和增删改的代码,但是读取不属于同时操作资源。假如一个方法有1000行,里面只有5行代码是增删改,需要同步,剩下的995行不需要同步,那么使用synchronized声明整个方法会造成线程不必要的等待,浪费时间。所以出现了synchronized块。

顾名思义就是把一个代码段声明为synchronized。

        可以指定要锁定的对象,如果不指定的话默认锁的是this。

3.1 解决不安全的抢票系统

我们给将run方法声明为synchronized,发现虽然结果不会出现负数的情况。

但是票都被同一个人抢去了。

我们来看一下这是为什么。给run方法上锁,意味着所有进入run方法的对象都要把run方法执行完才能释放这个锁给下一个排队的对象用。在我们的代码中,一旦某个对象进入了run方法就要一直抢票,直到 ticketNums<0,也就是意味着一张票也没有了,才会退出run方法。所以,除了第一个被执行的线程能抢到票且抢走了所有票,其他的线程一张票都抢不到。

可是这不是我们的目的呀!

错就错在,我们想要锁的操作是“抢一张票”,而我们上面锁的是“抢完所有票”。

所以应该把抢一张票的逻辑单独写成一个方法,然后加上synchronized关键字。

package Unsafe;public class RailwayTicketSystem{public static void main(String[] args) {BuyTicket system= new BuyTicket(); //镜像new Thread(system,"黑黑").start();  //容器1new Thread(system,"白白").start();  //容器2new Thread(system,"黄牛党").start(); //容器3}
}class BuyTicket implements Runnable{private int ticketNums = 10;  //系统里有10张票private boolean flag = true;  //系统初始化是开放的//抢票行为@Overridepublic void run() {while(flag==true){try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}buy();}}public synchronized void buy(){if(ticketNums>0){System.out.println(Thread.currentThread().getName()+"--->抢到了第"+ticketNums+"张票");ticketNums--;if(ticketNums == 0) flag = false;}}
}

注意,睡眠代码的位置也值得思考:

3.2 解决不安全的银行取钱系统

在需要同步的代码中,发生变动(增删改)的是account,而不是run方法所在的DrawMoney类。所以要指定锁的对象account,如果不指定的话默认锁的是所在类。此时我们不能给方法加synchronized了,因为方法无法指定被锁的对象。我们使用同步块:

3.3 解决不安全的集合

java 多线程(四)—— 线程同步/互斥=队列+锁相关推荐

  1. Java多线程之线程同步机制(锁,线程池等等)

    Java多线程之线程同步机制 一.概念 1.并发 2.起因 3.缺点 二.三大不安全案例 1.样例一(模拟买票场景) 2.样例二(模拟取钱场景) 3.样例三(模拟集合) 三.同步方法及同步块 1.同步 ...

  2. java多线程之线程同步问题

    1.线程不安全的问题分析 当多线程并发访问同一个资源对象的时候,可能出现线程不安全的问题.但是,我们分析打印的结果,发现没有问题: 为了让问题更明显:     Thread.sleep(10);//当 ...

  3. java多线程采集+线程同步-【多线程数据采集之四】

    前些日子讲解了java数据抓取, 今天就讲解最核心的. java多线程数据抓取. java多线程采集+数据同步+线程同步[多线程数据采集之四] 主要讲解多线程抓取,多线程同步,多线程启动,控制等操作. ...

  4. java多线程:线程同步synchronized(不同步的问题、队列与锁),死锁的产生和解决

    0.不同步的问题 并发的线程不安全问题: 多个线程同时操作同一个对象,如果控制不好,就会产生问题,叫做线程不安全. 我们来看三个比较经典的案例来说明线程不安全的问题. 0.1 订票问题 例如前面说过的 ...

  5. Java多线程(四)线程锁

    6.锁 由于多个线程是共同占有所属进程的资源和地址空间的,那么就会存在一个问题: 如果多个线程要同时访问某个资源,怎么处理? 在Java并发编程中,经常遇到多个线程访问同一个 共享资源 ,这时候作为开 ...

  6. Java多线程(五) —— 线程并发库之锁机制

    参考文献: http://www.blogjava.net/xylz/archive/2010/07/08/325587.html 一.Lock与ReentrantLock 前面的章节主要谈谈原子操作 ...

  7. Java 多线程和线程同步总结

    转载:JAVA多线程实现和线程同步总结 1.JAVA多线程实现方式 JAVA多线程实现方式主要有三种:继承Thread类.实现Runnable接口.使用ExecutorService.Callable ...

  8. Java多线程编程——线程同步与线程安全问题及synchronized关键字

    在多线程环境下,我们常常需要让多个线程同时去操作同一资源.在某些情况下,这种情形会导致程序的运行结果出现差错.专业上的,当多个线程在执行同一段代码的时候,每次的执行结果和单线程执行的结果都是一样的,不 ...

  9. Java多线程之线程同步

    线程同步 线程同步:当有一个线程在对内存进行操作时,其他线程都不可以对这个内存地址进行操作,直到该线程完成操作, 其他线程才能对该内存地址进行操作,而其他线程又处于等待状态,实现线程同步的方法有很多. ...

最新文章

  1. firefox html5 canvas,html5 Canvas
  2. Android--面试题整理(二)
  3. 跨语言平台的RSA加密、解密、签名、验证算法的实现
  4. poj 1060 Modular multiplication of polynomials
  5. Laravel 的安装使用
  6. 技术或运营的妥协/退让场景
  7. 现实世界的 Windows Azure:Davide Bedin讲述aKite零售管理解决方案
  8. wangEditor富文本编辑器的简单使用
  9. Matlab2016b中文乱码怎么办
  10. python生成随机姓名
  11. Golang面试题整理
  12. 合并拆分wim文件命令imagex
  13. 持续集成与持续部署(五)01-TravisCI——使用简介-Travis CI 只支持 Github,提供的是持续集成服务 配置项目的.travis.yml文件
  14. pr模板.mogrt格式安装方法
  15. python类型转换方法_详解python中的类型转换方法
  16. 自家的摇头扇线掉了 (电风扇的五根线怎么接)
  17. win7下ie卸载后文件夹打开总新页面打开
  18. 实时系统与非实时系统的区别
  19. 吉林大学软件学院——UML作业1
  20. cocos2dx学习笔记之常用动作类Action详解

热门文章

  1. PHP字符串运算结果,php字符运算
  2. Mac中Caps Lock(大写锁定键)为Control键(Emacs解放小拇指.)
  3. linux替换屏幕保护进程,有没有一个体面的方式来阻止linux中的屏幕保护程序?...
  4. 2017年ACM第八届山东省赛I题: Parity check(判断 第n项斐波那契数列奇偶性)
  5. log(二)——MDC实现之ThreadLocal
  6. Cadence之ORCAD:导出Bom清单
  7. HTML5的数据存储和数据处理的功能有,浅析 HTML5 数据存储的方法及应用
  8. 微软xcloud服务器,微软公布云游戏服务Project xCloud详情 2019年公测
  9. javascript小案例-----tab栏切换
  10. 常见的服务器类型有哪些?