当前位置: 首页 > news >正文

Java并发编程 第四章 共享模型之管程 上

1.  共享问题

先看一段代码

@Slf4j(topic = "c.yuanzixing")
public class yuanzixing {static int counter = 0;public static void main(String[] args) throws InterruptedException {Thread t1 = new Thread(() -> {for (int i = 0; i < 5000; i++) {counter++;}}, "t1");Thread t2 = new Thread(() -> {for (int i = 0; i < 5000; i++) {counter--;}}, "t2");t1.start();t2.start();t1.join();t2.join();log.debug("{}", counter);}
}

结果有可能是0

有可能是负数

还有可能正数 

这是因为i++和i--并不是原子操作

i++实际会产生如下的 JVM 字节码指令:

单线程下这八条指令是顺序执行(不会交错)没有问题。

但多线程下这 8 行代码可能交错运行

出现负数的情况

出现正数的情况 

        一个程序运行多个线程本身是没有问题的,问题出在多个线程访问共享资源,多个线程读共享资源其实也没有问题,但在多个线程对共享资源读写操作时发生指令交错,就会出现问题。
        一段代码块内如果存在对共享资源的多线程读写操作,称这段代码块为临界区。


2. synchronized 解决方案

语法

synchronized(对象) // 线程1, 线程2(blocked)
{
临界区
}

 例如

@Slf4j(topic = "c.Test19")
public class Test17 {static int counter = 0;static final Object room = new Object();public static void main(String[] args) throws InterruptedException {Thread t1 = new Thread(() -> {for (int i = 0; i < 5000; i++) {synchronized (room) {counter++;}}}, "t1");Thread t2 = new Thread(() -> {for (int i = 0; i < 5000; i++) {synchronized (room) {counter--;}}}, "t2");t1.start();t2.start();t1.join();t2.join();log.debug("{}", counter);}}

想象 synchronized(对象) 中的对象是一个房间(room),这个房间有唯一的入口(门),门只能一次进入一人。

  • 线程 t1 和 t2 就像两个想进入房间的人。
  1. 线程 t1 执行到 synchronized(room) 时,相当于 t1 进入了房间,并锁上了门,拿走了钥匙,在房间里执行 count++ 代码。

  2. 线程 t2 如果也运行到了 synchronized(room),它会发现门被锁住了,只能在门外等待。此时,线程 t2 会被阻塞,发生上下文切换,等待 t1 完成工作。

  3. 即使 t1 的 CPU 时间片用完 被踢出了门外(注意,锁住对象并不能让 t1 一直执行),门仍然锁住,t1 依然拿着钥匙。此时,t2 线程仍在阻塞状态,无法进入房间。

  4. 当 t1 执行完 synchronized{} 块内的代码后,t1 才会离开房间并解开门上的锁,把钥匙还给 t2 线程。此时 t2 线程才可以进入房间,锁住门,拿上钥匙,执行它的 count-- 代码。

 如图

 如果把 synchronized(obj) 放在 for 循环的外面,如何理解?-- 原子性

此时也不会出错,就是锁住了整个for循环近20000次操作。
如果 t1 synchronized(obj1) 而 t2 synchronized(obj2) 会怎样运作?-- 锁对象

锁不是同一把对象跟没上一样。会出错
如果 t1 synchronized(obj) 而 t2 没有加会怎么样?如何理解?-- 锁对象

t2没加锁那么则发生上下文切换后轮到t2它因为没锁所以不会被阻塞住,那么他就会执行代码,于是又发生交错执行。会出错。

基于面向对象的设计思想代码可以改进,把需要保护的共享变量放入一个类,

class Room {int value = 0;public void increment() {synchronized (this) {value++;}}public void decrement() {synchronized (this) {value--;}}public int get() {synchronized (this) {return value;}}
}

注意get也要加锁,因为不加锁可能读到中间变量。

3. 方法上的 synchronized

class Test {public synchronized void test() {}
}
等价于class Test {public void test() {synchronized (this) {}}
}class Test {public synchronized static void test() {}
}
等价于class Test {public static void test() {synchronized (Test.class) {}}
}

下面看八种情况

case1
@Slf4j(topic = "c.Number")
class Number {public synchronized void a() {        log.debug("1");    }public synchronized void b() {        log.debug("2");    }
}public static void main(String[] args) {Number n1 = new Number();new Thread(() -> {            n1.a();        }).start();new Thread(() -> {            n1.b();        }).start();}case2
class Number {public synchronized void a() {        sleep(1);        log.debug("1");}public synchronized void b() {        log.debug("2");    }public void c() {        log.debug("3");    }
}public static void main(String[] args) {Number n1 = new Number();new Thread(() -> {            n1.a();        }).start();new Thread(() -> {            n1.b();        }).start();new Thread(() -> {            n1.c();        }).start();}

case1 12或21    case 2: 3 1s 12或 23 1s 1 或 32 1s 1

case3 
@Slf4j(topic = "c.Number")
class Number {public static synchronized void a() {        sleep(1);        log.debug("1");}public synchronized void b() {        log.debug("2");    }
}public static void main(String[] args) {Number n1 = new Number();new Thread(() -> {            n1.a();        }).start();new Thread(() -> {            n1.b();        }).start();}
case4
@Slf4j(topic = "c.Number")
class Number{
public static synchronized void a() {sleep(1);log.debug("1");}
public static synchronized void b() {log.debug("2");}
}
public static void main(String[] args) {
Number n1 = new Number();
new Thread(()->{ n1.a(); }).start();
new Thread(()->{ n1.b(); }).start();
}
case5@Slf4j(topic = "c.Number")
class Number {public static synchronized void a() {sleep(1);log.debug("1");}public synchronized void b() {log.debug("2");}
}public static void main(String[] args) {Number n1 = new Number();Number n2 = new Number();new Thread(() -> {n1.a();}).start();new Thread(() -> {n2.b();}).start();}
case6
class Number {public static synchronized void a() {sleep(1);log.debug("1");}public static synchronized void b() {log.debug("2");}
}public static void main(String[] args) {Number n1 = new Number();Number n2 = new Number();new Thread(() -> {n1.a();}).start();new Thread(() -> {n2.b();}).start();}

case3 a方法加了static所以所住的是类,b方法所住的是this对象,所以不是同一把锁 2 1s 1

case4: 1s 后12, 或 2 1s后 1   case5:2 1s 1  case 6 :n1 n2虽然是不同的对象,但ab方法都加了static,所以锁住的都是number类,因此是同一把锁,1s 后12, 或 2 1s后 1。

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 数据仓库系列14:数据清洗和转换的常见方法有哪些?
  • 从地图信息实时检测路口的各向通行状况、红绿灯及溢出情况
  • 初识C++|list类的使用及模拟实现
  • Qt 调用MFC dll,动态库中有界面
  • windows C++ 并行编程-使用 Lambda 表达式
  • MYSQL数据库(四)
  • JavaScript学习文档(11):Window对象、本地存储、数组中一些方法、学生就业统计表案例
  • 15行为型设计模式——责任链模式
  • Java基础入门【第六章 static、继承、重写、多态】(二)
  • 数据合规性分析:守护信息安全的关键防线
  • MySQL的安装配置以及可视化工具的安装
  • 【拉取Git项目到本地,知识小记,后续再改】
  • 春秋云镜initial
  • 宏集MIRO-L230工业路由器: 一站式全球联网解决方案
  • C++ 图形框架 Duilib
  • nginx 配置多 域名 + 多 https
  • oldjun 检测网站的经验
  • Perseus-BERT——业内性能极致优化的BERT训练方案
  • tensorflow学习笔记3——MNIST应用篇
  • 分类模型——Logistics Regression
  • 前嗅ForeSpider中数据浏览界面介绍
  • 实现简单的正则表达式引擎
  • 突破自己的技术思维
  • 推荐一个React的管理后台框架
  • 一道闭包题引发的思考
  • 用jQuery怎么做到前后端分离
  • 在Mac OS X上安装 Ruby运行环境
  • ​TypeScript都不会用,也敢说会前端?
  • ​浅谈 Linux 中的 core dump 分析方法
  • # Apache SeaTunnel 究竟是什么?
  • #Linux(Source Insight安装及工程建立)
  • (C语言)fgets与fputs函数详解
  • (C语言)逆序输出字符串
  • (SpringBoot)第七章:SpringBoot日志文件
  • (动手学习深度学习)第13章 计算机视觉---图像增广与微调
  • (附源码)springboot课程在线考试系统 毕业设计 655127
  • (附源码)ssm经济信息门户网站 毕业设计 141634
  • (附源码)计算机毕业设计SSM保险客户管理系统
  • (更新)A股上市公司华证ESG评级得分稳健性校验ESG得分年均值中位数(2009-2023年.12)
  • (图文详解)小程序AppID申请以及在Hbuilderx中运行
  • (终章)[图像识别]13.OpenCV案例 自定义训练集分类器物体检测
  • (转)淘淘商城系列——使用Spring来管理Redis单机版和集群版
  • (转载)从 Java 代码到 Java 堆
  • *Django中的Ajax 纯js的书写样式1
  • ..回顾17,展望18
  • .NET Core日志内容详解,详解不同日志级别的区别和有关日志记录的实用工具和第三方库详解与示例
  • .Net FrameWork总结
  • /var/spool/postfix/maildrop 下有大量文件
  • @Autowired多个相同类型bean装配问题
  • @EnableWebSecurity 注解的用途及适用场景
  • @selector(..)警告提示
  • [145] 二叉树的后序遍历 js
  • [20190401]关于semtimedop函数调用.txt
  • [Android]通过PhoneLookup读取所有电话号码
  • [C#]OpenCvSharp 实现Bitmap和Mat的格式相互转换