Java并发二(线程安全)

Java并发二(线程安全)

-2022.3.27 -BDY

猛猪猪语录:争取早点学完JUC,太恶心了


前言

承接上文


一、线程安全问题

1. 生动形象的故事

请添加图片描述
请添加图片描述
请添加图片描述
请添加图片描述

2.线程问题原因

public class Test {
   
	static int count = 0;
	public static void main(String[] args) throws InterruptedException {
   
	    Thread t1 = new Thread(()->{
   
	        for (int i = 1; i < 5000; i++){
   
	            count++;
	        }
	    });
	    Thread t2 =new Thread(()->{
   
	        for (int i = 1; i < 5000; i++){
   
	            count--;
	        }
	    });
	    t1.start();
	    t2.start();
	    t1.join(); // 主线程等待t1线程执行完
	    t2.join(); // 主线程等待t2线程执行完
	    
	    // main线程只有等待t1, t2线程都执行完之后, 才能打印count, 否则main线程不会等待t1,t2
	    // 直接就打印count的值为0
	    log.debug("count的值是{}",count);
	}
}

// 打印: 并不是我们期望的0值, 为什么呢? 看下文分析
09:42:42.921 guizy.ThreadLocalDemo [main] - count的值是511 

原因:
java静态变量自增自减不是原子操作
(想想什么是静态变量,什么是原子操作)
(想想多线程并发的情况,抢夺cpu)

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

可以看到count++ 和 count-- 操作实际都是需要这个4个指令完成的

过程示例图:
请添加图片描述

3.问题进一步探究

  • 临界区

一个程序运行多线程本身是没有问题的
问题出现在多个线程共享资源(临界资源)的时候
多个线程同时对共享资源进行读操作本身也没有问题 - 对读操作没问题
问题出现在对对共享资源同时进行读写操作时就有问题了 - 同时读写操作有问题
先定义一个叫做临界区的概念:一段代码内如果存在对共享资源的多线程读写操作,那么称这段代码为临界区; 共享资源也成为临界资源

static int counter = 0;
static void increment() 
// 临界区 
{
      
   counter++; 
}

static void decrement() 
// 临界区 
{
    
   counter--; 
}
  • 竞态条件
    多个线程在临界区执行,那么由于代码指令的执行不确定而导致的结果问题,称为竞态条件
    (所以出现问题的原因就是临界资源被多个线程调用,)

4.synchronized(对象锁) 解决方案

为了避免临界区中的竞态条件发生,由多种手段可以达到
阻塞式解决方案: synchronized , Lock (ReentrantLock)
非阻塞式解决方案: 原子变量 (CAS)

采用互斥的方式让同一时刻至多只有一个线程持有对象锁,其他线程如果想获取这个锁就会阻塞住,这样就能保证拥有锁的线程可以安全的执行临界区内的代码,不用担心线程上下文切换

1.语法

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<
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值