Javaweb---线程(3)

[重难点]

线程安全问题:

在这里插入图片描述
针对上面这个代码:按照正常情况下判断输出的count应该为10000 但是并不是这样
在这里插入图片描述
多次运行可以发现这个count的值一直在变化.这就是线程不安全导致的因素!
串行执行:(此时运行结果正确)
在这里插入图片描述
在这里插入图片描述
并行执行:线程1进行++一半的时候 线程2也在++
明明自增两次但是结果却还是1
在这里插入图片描述
在这里插入图片描述

线程不安全的原因:

1.线程之间是抢占式执行的(线程不安全的根源 并且无法人为改变)
抢占式:

  1. 线程之间的调度完全由内核决定 用户根本感知不道 也无法控制
  2. 线程谁先执行谁后执行谁执行到哪里从CPU上下来 用户同样无法感知
    系统负载: 衡量服务器繁忙程度的重要指标(数目越大越繁忙)
    当线程/进程数目越多的时候 调度的开销就会越大 线程/进程被第二次调上CPU执行的时间间隔会特别长

2.自增操作不是原子性
每次++都能拆分成3个步骤 (但是这些步骤不是一气呵成执行完的 执行到哪步就没了 会让给其他线程执行)

  1. 从内存中读取数据到CPU中
  2. 在CPU中数据++
  3. 把计算结束的数据返回到内存中
    原子的:对于前置++; 后置++ ;+= ;-= ;*= ;/=全不是原子的
    直接赋值操作不一定 内置类型是原子的(64位CPU)
    引用类型不一定

3.多个线程尝试修改 同一个变量
一个线程修改一个变量 线程安全
多个线程修改不同变量 线程安全
多个线程修改同一变量 线程安全
(如果有多个线程 一个读数据 一个修改数据也可能是线程不安全的
都读数据是线程安全的)

4.内存可见性导致的线程不安全性
5.指令重排序(编译器会对指令进行优化 调整先后顺序 保证原有逻辑不变的情况下 提高运行效率)

解决线程不安全的问题:

1.抢占式执行的方法:是内核决定的 我们无法改变
2.非原子: 可以给自增操作加锁
3.不一定有办法 得看具体的需求

锁:(synchronized)

锁 : 就像是我们进ATM机取钱(这个银行只有一个ATM机 其他都坏了) 当我们进去的时候得把锁锁上 其他想取钱的人就只能巴巴的在外面等 等我出来了才能进去
锁的特点: 互斥的 同一个时刻只能有一个线程获取该锁
其他线程如果想获取该锁就会触发线程等待 得等到刚才的线程将 锁释放了剩下的线程才能竞争刚才的锁

在这里插入图片描述
进入increase()之前先加锁 increase()方法执行完毕后会自动解锁synchronized加锁解锁全部包办(好处:可以解决忘记解锁的问题)
加锁的操作不一定会立刻成功 当锁被占用的时候会发生阻塞,此时线程等待之前的锁被释放之后才可能获取到这个锁

加上锁之后结果就正确了
在这里插入图片描述
图示解析锁的功能:
在这里插入图片描述
synchronized的具体使用: 可以指定给某个对象加锁而不是仅仅把锁加到某个方法上 如果把synchronized写到方法前 相当于给当前对象(this)加锁

代码示例:
在这里插入图片描述
synchronized的几种常见用法:
1.加到普通方法前 :表示锁this
在这里插入图片描述
2.加到静态方法前: 表示锁当前类的类对象
3.加到某个代码块之前: 显示指定给某个对象加锁
在这里插入图片描述
两个线程获取同一把锁就会产生线程阻塞
在这里插入图片描述
两个线程获取不同的锁就不会发生互斥了
在这里插入图片描述
两个锁引用同一个类对象
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值