正文
Java 体现共享资源的问题
两个线程对初始值为 0 的静态变量一个做自增,一个做自减,各做 5000 次,结果为 0 么?
public class Test2 {
static int j = 0;
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(() -> {
for (int i = 0; i < 1000; i++) {
j++;
}
});
Thread thread2 = new Thread(() -> {
for (int i = 0; i < 1000; i++) {
j++;
}
});
thread.start();
thread2.start();
thread.join();
thread2.join();
System.out.println(j);
}
}
运行结果
Disconnected from the target VM, address: '127.0.0.1:10483', transport: 'socket'
1543
Process finished with exit code 0
问题分析
因为 Java 中对静态变量的自增(减)并非原子操作,从字节码角度分析就可以知道原因了
以 j ++( j 为静态变量)为例,看起来是一条指令,实际上在 JVM是以下效果
getstatic j //获取静态变量 j 的值
iconst_1 //准备常量 1
iadd // 自增
putstatic // 将修改后的值存入静态变量
在 Java 的内存模型中,完成静态变量的自增(减),需要在主存和工作内存中进行数据交换的。
临界区
- 一个程序运行多个线程会发生的问题
- 访问共享资源,若多个线程对共享资源读写操作时发生指令交错,就会出现问题
- 一段代码块内如果存在对共享资源的多线程读写操作,称这段代码块为临界区
static int i = 0;
static void increment()
//临界区
{
//要知道,一个 i++ 在字节码层面是分为四个步骤的,包含读写
i++;
}
static void decrement()
//临界区
{
i--;
}
竞态条件
多个线程在临界区内执行,由于代码的执行序列不同(指令)而导致结果无法预测,称之为发生了竞态条件
解决
- 阻塞解决方案:synchronized,Lock
- 非阻塞解决方案:原子变量
注意
Java 中互斥和同步都可采用 synchronized ,但还是有区别的
- 互斥是保证临界区的竞态条件发生,同一时刻只能有一个线程执行临界区代码
- 同步是由于线程执行的先后、顺序不同,需要一个线程等待其他线程运行到某个点
synchronized
语法–加对象
synchronized(对象,任意的java对象)
{
//临界区
}
synchronized 实际是用对象锁爆炸了临界区内代码的原子性,临界区内的代码对外是不可分割的,不会被线程切换所打断。
语法–加方法
class Test{
public synchronized void test(){
}
}
等价于
class Test{
public void test(){
synchronized(this){
}
}
}
不加 synchronized 的方法,不需要获取锁,无视阻塞,不遵守规则的人

博客围绕Java共享资源问题展开。以两个线程对静态变量自增自减为例,指出Java中静态变量自增(减)非原子操作会引发问题。介绍了临界区和竞态条件的概念,给出阻塞(synchronized、Lock)和非阻塞(原子变量)两种解决方法,还说明了synchronized在互斥和同步上的区别。
&spm=1001.2101.3001.5002&articleId=111346359&d=1&t=3&u=2c5687513a734229b03c58b7464137d1)
2559

被折叠的 条评论
为什么被折叠?



