synchronized的使用

大佬之所以叫大佬,就是因为他们即使一次看不懂,看二十遍也要看懂,再对萌新说:这个方法不是挺简单的嘛


1、同步方法

要注意的是,synchronized锁的不是方法,而是对象的资源。

33行,成员方法锁的是this,也就是p,方法中使用的资源必须都是对象p的资源,才能正确的锁住。

package cn.hanquan.test;public class Test {public static void main(String[] args) throws InterruptedException {Person p = new Person();new Thread(p, "程序猿").start();new Thread(p, "傻子").start();new Thread(p, "世纪帅男").start();}
}class Person implements Runnable {private int ticket = 100;@Overridepublic void run() {while (ticket > 0) {try {Thread.sleep(100);grab();} catch (InterruptedException e) {e.printStackTrace();}}}// 同步方法:抢票private synchronized void grab() throws InterruptedException {if (ticket <= 0) {return;}ticket--;System.out.println(Thread.currentThread().getName() + "\t" + ticket);}
}

在以上代码中,

 // 同步方法:抢票private synchronized void grab() throws InterruptedException {if (ticket <= 0) {return;}ticket--;System.out.println(Thread.currentThread().getName() + "\t" + ticket);}

等同于:

 // 同步方法:抢票private void grab() throws InterruptedException {synchronized (this) {if (ticket <= 0) {return;}ticket--;System.out.println(Thread.currentThread().getName() + "\t" + ticket);}}

说明:synchronized方法的同步,其实锁的就是this对象。

而下面这种方式不可以,因为ticket的对象是变动的:

 // 同步方法:抢票private void grab() throws InterruptedException {synchronized ((Integer) ticket) {// 这里要的是引用类型,ticket是int类型,需要强转一下。if (ticket <= 0) {return;}ticket--;System.out.println(Thread.currentThread().getName() + "\t" + ticket);}}

锁this的时候,虽然this的属性在变,但是this这个大的对象不变。但是 ticket 这个数是一直在变的,把它转化成对象(Integer) ticket之后,对象一直在变。这样不可以。synchronized用于锁不变的对象才可以,变化的对象锁不住。

  • 这里的变指的不是内容,而是地址

性能的再优化:double checking

看似代码变多了,实际上减少了锁定的范围(在锁定之前过滤一下,如果票数小于0,就不锁了,直接返回)


2、同步块

(1)线程不安全示例

package cn.hanquan.test;/** 线程安全----使用同步块*/public class Account {public int money = 100;// 账户余额public static void main(String[] args) throws InterruptedException {// 账户Account account = new Account();ATM you = new ATM(account, 80, "你");ATM wife = new ATM(account, 90, "她");you.start();wife.start();}
}// 模拟取款机
class ATM extends Thread {Account account;int drawMoney;int packetTotal;// constructorpublic ATM(Account account, int drawMoney, String name) {super(name);this.account = account;this.drawMoney = drawMoney;}@Overridepublic void run() {try {test();} catch (InterruptedException e) {e.printStackTrace();}}// 取钱操作public synchronized void test() throws InterruptedException {// 这里的account是作为参数传进来的if (account.money - drawMoney < 0) {return;}Thread.sleep(1000);account.money -= drawMoney;packetTotal += drawMoney;System.out.println("【" + this.getName() + "】 " + "账户余额:" + account.money + "  口袋的钱:" + packetTotal);}
}

运行结果:

【她】 账户余额:-70 口袋的钱:90
【你】 账户余额:-70 口袋的钱:80

看运行结果,发现:账户余额成了负数。

也就是说,当余额不足以取出时,仍然进行了取款操作。

这样的结果说明,synchronized没有锁住正确的对象。我们发现,实际上,public synchronized void test()锁住的,是this所指向的ATM本身的资源;而取款操作,改变的是外部的account对象的资源。

也就是说,应该锁住的是银行账户,而不是ATM取款机本身。

因此,做出以下改进:

(2)线程安全示例

下面是上例中代码的改进,实现了线程安全。注意44行使用的同步块。

package cn.hanquan.test;/** 线程安全----使用同步块*/public class Account {public int money = 100;// 账户余额public static void main(String[] args) throws InterruptedException {// 账户Account account = new Account();ATM you = new ATM(account, 80, "你");ATM wife = new ATM(account, 90, "她");you.start();wife.start();}
}// 模拟取款机
class ATM extends Thread {Account account;int drawMoney;int packetTotal;// constructorpublic ATM(Account account, int drawMoney, String name) {super(name);this.account = account;this.drawMoney = drawMoney;}@Overridepublic void run() {try {test();} catch (InterruptedException e) {e.printStackTrace();}}// 取钱操作public void test() throws InterruptedException {synchronized (account) {// account本身是独立的类,作为参数才能传进来的if (account.money - drawMoney < 0) {return;}Thread.sleep(1000);account.money -= drawMoney;packetTotal += drawMoney;System.out.println("【" + this.getName() + "】 " + "账户余额:" + account.money + "  口袋的钱:" + packetTotal);}}
}

运行结果:

【你】 账户余额:20 口袋的钱:80


3、synchronized在容器中的使用

22行前面要加一个sleep(100),因为要避免还没数完数就打印了的情况。

【Java线程安全】 synchronized同步方法、同步块:模拟抢票、模拟取款相关推荐

  1. 多线程抢票_java多线程下模拟抢票

    我们设置三个对象分别同时抢20张票,利用多线程实现. public class Web123506 implements Runnable{ private int ticteksNums=20;// ...

  2. python模拟app抢票_python并发编程多进程 模拟抢票实现过程

    抢票是并发执行 多个进程可以访问同一个文件 多个进程共享同一文件,我们可以把文件当数据库,用多个进程模拟多个人执行抢票任务 db.txt {"count": 1} 并发运行,效率高 ...

  3. Java线程学习实例——采用同步锁,互斥锁与同步锁的区别,synchronized的使用方法

    栗子来源:https://blog.csdn.net/wenzhi20102321/article/details/52524545 首先对java中同步锁与互斥锁进行区分,主要来源于知乎中的大佬总结 ...

  4. 四、java多线程核心技术——synchronized同步方法与synchronized同步快

    一.synchronized同步方法 论:"线程安全"与"非线程安全"是多线程的经典问题.synchronized()方法就是解决非线程安全的. 1.方法内的变 ...

  5. java同步方法同步块_java 同步代码块与同步方法

    同步代码块 synchronized(obj) {//代码块 } obj 为同步监视器,以上代码的含义为:线程开始执行同步代码块(中的代码)之前,必须先获得对同步监视器的锁定. 代码块中的代码是执行代 ...

  6. java同步方法同步块_java使用同步方法和同步块的区别

    一.概述有谁能举例说明同步方法优于同步块的优势吗? 二.详解 在块上使用同步方法没有明显的优势. 也许唯一的一个(但我不会称其为优势)是您不需要包括对象引用this. 方法: public synch ...

  7. java 同步解决不安全类_「JAVA」Java 线程不安全分析,同步锁和Lock机制,哪个解决方案更好...

    线程不安全 线程不安全的问题分析:在小朋友抢气球的案例中模拟网络延迟来将问题暴露出来:示例代码如下: public class ImplementsDemo { public static void ...

  8. java 线程之对象的同步和异步

    一.多线程环境下的同步与异步 同步:A线程要请求某个资源,但是此资源正在被B线程使用中,因为同步机制存在,A线程请求不到,怎么办,A线程只能等待下去. package com.jalja.org.th ...

  9. Java多线程基础学习,Thread解读、java线程的状态、同步和异步、两阶段终止模式

    理论概述 单线程和多线程 为什么要使用多线程呢?多线程有什么好处呢? 如果在程序中,需要读写一个文件,该文件很大,那我们执行到该io操作时,cpu就会等待该io操作执行完才会继续运行下面的代码,进程调 ...

最新文章

  1. Jquery--遮罩弹窗特效
  2. zabbix mysql监控告警_Zabbix监控mysql配置及故障告警配置
  3. WebStorm调试Electron
  4. 软件开发过程(CMMI/RUP/XP/MSF)是与非?
  5. python xlutils教程_Python基于xlutils修改表格内容过程解析
  6. [jQuery基础] jQuery动效案例(一) -- 弹窗广告、对联广告
  7. UVA10049 Self-describing Sequence【数列】
  8. photoshop菜鸟实用入门(2)----常用的一些快捷操作
  9. Linux入门基础命令学习记录
  10. groovy 打印json_groovyJSON - Groovy教程
  11. 计算机网络选择公用还是家庭,如何设置打印机共享?
  12. Python 批量发送邮件脚本
  13. 在 Kali Linux安装环境中,配置网络出现DHCP错误,解决方案。(安装kali Linux的网络配置失败问题)
  14. 计算机专业今日份例句
  15. 微信公众账号开发模式3
  16. 轻松搞定应用启动黑白屏
  17. 网站分析实战——如何以数据驱动决策,提升网站价值(大数据时代的分析利器)
  18. 笔记本给移动设备共享wifi
  19. 《数字图像处理》手动实现最佳陷波滤波
  20. 5G通信技术解读:波束成形如何为5G添翼?

热门文章

  1. linux hibernate suspend 区别,实现Linux休眠(sleep/hibernate)和挂起(suspend)
  2. uva1624knots
  3. python基础语法-三大内建数据结构之列表(list)
  4. EOJ_1057_排名汇总
  5. 最短路径之Spfa算法
  6. Shell变量:Shell变量的定义、删除变量、只读变量、变量类型
  7. c++ template(4)基本技巧
  8. C++内存管理全景指南
  9. 图文结合,白话 Go 的垃圾回收原理
  10. LiveVideoStackCon 2019北京开幕 成为多媒体技术生态风向标