JUC学习笔记——共享模型之管程


实际代码体现

// 针对counter,我们一个线程++,一个线程--各运行5000次
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,经常为-5000~5000之间的数

我们可以从底层代码分析问题:

/*i++底层代码*/ 

getstatic i // 获取静态变量i的值
iconst_1 	// 准备常量1
iadd 		// 自增
putstatic i // 将修改后的值存入静态变量i
    
/*i--底层代码*/ 
    
getstatic i // 获取静态变量i的值
iconst_1 	// 准备常量1
isub 		// 自减
putstatic i // 将修改后的值存入静态变量i

我们会发现他们的底层代码并不是一步实现,而是多步操作一同实现

但是如果是多线程,就会因为上下文切换的缘由导致部分步骤出现交杂(我们给出正数示例):

临界区和竞态条件

首先我们来简单介绍一下临界区:

  • 一段代码块内如果存在对共享资源的多线程读写操作,称这段代码块为临界区
  • 例如我们上述共享问题中的i就是共享资源,而对i操作的i++和i–操作都可以被称为临界区

针对临界区我们需要注意以下内容:

  • 一个程序运行多个线程本身是没有问题的
  • 多个线程读共享资源其实也没有问题
  • 但是在多个线程对共享资源读写操作时发生指令交错,就会出现问题
  • 多个线程在临界区内执行,由于代码的执行序列不同而导致结果无法预测,称之为发生了竞态条件

共享问题解决方案

这小节我们将会介绍共享问题解决方案

共享问题解决方案总述

  • 阻塞式的解决方案:synchronized,Lock
  • 非阻塞式的解决方案:原子变量

synchronized简述

  • 俗称的【对象锁】,采用互斥的方式使目前至多只有一个线程能持有【对象锁】其它线程再想获取这个【对象锁】时就会阻塞住。
  • 这样就能保证拥有锁的线程可以安全的执行临界区内的代码,不用担心线程上下文切换

我们先来介绍synchronized的语法:

// 线程1, 线程2 都使用同一对象作为锁,这样一个运行,另一个处于blocked阻塞
synchronized(对象) 
{
    临界区
}

我们再给出相关代码示例:

// 我们创建一个room对象来作为锁,注意处理共享问题的线程需要绑定同一个锁

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对象相当于一个锁,这个锁控制着房间,而房间中放着所有对应的synchronized里面的代码
  • 多个线程谁先进入room就可以获得钥匙,然后如果想要进入这个房间操作,只有有钥匙的线程才可以
  • 当该线程操作结束后,就会主动将钥匙让出来,其他线程就可以进行抢夺钥匙,哪个线程获得钥匙就可以继续操作
  • 同时我们需要注意时间片结束并不意味着解开锁,就算轮到其他线程的时间片,他们也不能进入到房间里去执行他们的代码

本站声明:
1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;

2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;

3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;

4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;

5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

原创文章,作者:ItWorker,如若转载,请注明出处:https://blog.ytso.com/292999.html

(0)
上一篇 2022年11月12日
下一篇 2022年11月12日

相关推荐

发表回复

登录后才能评论