当前位置:网站首页>并发之共享模型管程
并发之共享模型管程
2022-06-24 19:41:00 【七国的天下,我要九十九】
并发之共享模型管程
1 共享问题
两个线程对同一个数进行了加和减的操作,因为上下文切换,导致值没能更新,从而影响统计的结果.
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);
/* 运行结果: -2470 */
问题说明
以上的结果可能是正数、负数、零.因为 Java 中对静态变量的自增,自减并不是原子操作,从字节码来分析
对于i++操作,对应JVM字节码
getstatic i // 获取静态变量i的值
iconst_1 // 准备常量1
iadd // 自增
putstatic i // 将修改后的值存入静态变量i
对于i–操作
getstatic i // 获取静态变量i的值
iconst_1 // 准备常量1
isub // 自减
putstatic i // 将修改后的值存入静态变量i
Java 的内存模型如下,完成静态变量的自增,自减需要在主存和工作内存中进行数据交换:

在单线程中执行,没有问题;在多线程中,可能出现正数,负数.
临界区
- 一个程序运行多个线程本身是没有问题的
- 问题出在多个线程访问共享资源
- 问题出在多个线程访问共享资源
- 在多个线程对共享资源读写操作时发生指令交错,就会出现问题
- 一段代码块内如果存在对共享资源的多线程读写操作,称这段代码块为临界区
以下为例:
static int counter = 0;
static void increment()
// 临界区
{
counter++;
}
static void decrement()
// 临界区
{
counter--;
}
竞态条件
多个线程在临界区内执行,由于代码的执行序列不同而导致结果无法预测,即发生了竞态条件
2 synchronized
为了避免临界区的竞态条件发生,有多种手段可以达到目的
- 阻塞式的解决方案:synchronized,Lock
- 非阻塞式的解决方案:原子变量
synchronized,即俗称的【对象锁】,它采用互斥的方式让同一 时刻至多只有一个线程能持有【对象锁】,其它线程再想获取这个【对象锁】时就会阻塞住。这样就能保证拥有锁 的线程可以安全的执行临界区内的代码,不用担心线程上下文切换.
java 中互斥和同步都可以采用 synchronized 关键字来完成,区别如下:
互斥是保证临界区的竞态条件发生,同一时刻只能有一个线程执行临界区代码
同步是由于线程执行的先后、顺序不同、需要一个线程等待其它线程运行到某个点
语法
synchronized(对象) // 线程1, 线程2(blocked)
{
临界区
}
// 当线程1 进入临界区 此时发生上下文切换, 线程2 因为没有对象锁 阻塞在外面
案例
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 = {}",counter);
}
/* 运行结果: counter = 0 */
说明:
synchronized(对象) 中的对象,可以想象为一个房间(room),有唯一入口(门)房间只能一次进入一人 进行计算,线程 t1,t2 想象成两个人
当线程 t1 执行到 synchronized(room) 时就好比 t1 进入了这个房间,并锁住了门拿走了钥匙,在门内执行 count++ 代码
这时候如果 t2 也运行到了 synchronized(room) 时,它发现门被锁住了,只能在门外等待,发生了上下文切 换,阻塞住了
这中间即使 t1 的 cpu 时间片不幸用完,被踢出了门外(不要错误理解为锁住了对象就能一直执行下去哦), 这时门还是锁住的,t1 仍拿着钥匙,t2 线程还在阻塞状态进不来,只有下次轮到 t1 自己再次获得时间片时才 能开门进入
当 t1 执行完 synchronized{} 块内的代码,这时候才会从 obj 房间出来并解开门上的锁,唤醒 t2 线程把钥 匙给他。t2 线程这时才可以进入 obj 房间,锁住了门拿上钥匙,执行它的 count-- 代码
小结
synchronized 实际是用对象锁保证了临界区内代码的原子性,临界区内的代码对外是不可分割的,不会被线程切 换所打断.
如果把 synchronized(obj) 放在 for 循环的外面,如何理解?
- 那么整个for循环就是一个整体, 在循环结束前,其他线程不可运行,保证原子性.
如果 t1 synchronized(obj1) 而 t2 synchronized(obj2) 会怎样运作?
- sync是锁对象. 如上,使用两个不同对象,那么线程之间单独控制,不会因为锁互相影响阻塞.
如果 t1 synchronized(obj) 而 t2 没有加会怎么样?
- 那么线程t1可保证准确性,线程t2无法保证,最后结果也无法保证,sync锁是针对对象,需要关联所有线程都使用同一个对象.
案例改造之面向对象
// 用锁来控制 加 减 查询
class Room {
int value = 0;
public void increment() {
synchronized (this) {
value++;
}
}
public void decrement() {
synchronized (this) {
value--;
}
}
public int get() {
synchronized (this) {
return value;
}
}
}
测试类
@Slf4j
public class Test1 {
public static void main(String[] args) throws InterruptedException {
Room room = new Room();
Thread t1 = new Thread(() -> {
for (int j = 0; j < 5000; j++) {
room.increment();
}
}, "t1");
Thread t2 = new Thread(() -> {
for (int j = 0; j < 5000; j++) {
room.decrement();
}
}, "t2");
t1.start();
t2.start();
t1.join();
t2.join();
log.debug("count: {}" , room.get());
}
}
/* 运行结果: count: 0 */
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) {
}
}
}
线程八锁
- 结果12 或 21
@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();
}
- 1s后12,或 2 1s后 1
@Slf4j(topic = "c.Number")
class Number{
public 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();
}
- 3 1s 12 或 23 1s 1 或 32 1s 1
@Slf4j(topic = "c.Number")
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();
}
- 2 1s 后 1
@Slf4j(topic = "c.Number")
class Number{
public 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();
}
- 2 1s 后 1
@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();
}
- 1s 后12, 或 2 1s后 1
@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();
}
- 2 1s 后 1
@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();
}
- 1s 后12, 或 2 1s后 1
@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();
Number n2 = new Number();
new Thread(()->{
n1.a(); }).start();
new Thread(()->{
n2.b(); }).start();
}
边栏推荐
- Epics record reference 3 -- fields common to all records
- Development specification - parameter verification exception, exception return prompt section
- Research Report on solar battery charger industry - market status analysis and development prospect forecast
- Research Report on market evaluation and investment direction of Chinese dermatology drugs (2022 Edition)
- 23研考生注意啦!备考期间最容易中招的骗局,居然是它们?!
- See how sparksql supports enterprise data warehouse
- 非单文件组件
- Second IPO of Huafang group: grown up in Zanthoxylum bungeanum, trapped in Zanthoxylum bungeanum
- 结构体的内存对齐
- Recommended movies: Northeast tiger, licorice pizza
猜你喜欢

Leetcode: push domino (domino simulation)

2022年高压电工考试模拟100题及在线模拟考试

Epics record reference 4 -- fields for all input records and fields for all output records

【nvm】

See how sparksql supports enterprise data warehouse
![[laravel series 7.9] test](/img/49/4b470a8b309bab4a83eed930dcce65.png)
[laravel series 7.9] test

2022年安全员-A证考题及答案

Dynamic menu, auto align

结合源码剖析Oauth2分布式认证与授权的实现流程

【文本数据挖掘】中文命名实体识别:HMM模型+BiLSTM_CRF模型(Pytorch)【调研与实验分析】
随机推荐
2022安全员-B证考试题库及答案
剑指 Offer 13. 机器人的运动范围
关于某手滑块的一些更新(6-18,js逆向)
Epics record reference 3 -- fields common to all records
New, Huawei cloud Kaitian apaas
Programmers become gods by digging holes in one year, carrying flags in five years and becoming gods in ten years
ThreadLocal local thread
别再乱用了,这才是 @Validated 和 @Valid 的真正区别!!!
Solution to the login error of tangdou people
Research and investment strategy report on China's building steel structure anticorrosive coating industry (2022 Edition)
【Laravel系列7.9】测试
Learn more about the practical application of sentinel
How to integrate Huawei cloud function services in fluent
Wechat side: what is consistent hash? In what scenario? What problems have been solved?
01_SpingBoot 框架入门
Leetcode: calculate the number of elements less than the current element on the right (sortedlist+bisect\u left)
vulnhub DC: 2
Common sense of resolution
laravel 验证器的使用
laravel 宝塔安全配置