Java多线程下写同一个变量

date
Jan 25, 2018
slug
Java多线程下写同一个变量
status
Published
tags
Java
多线程
summary
type
Post

场景

为了提高程序并发效率,多线程是必不可少的方案。 不过,多线程虽好处很多,但是,复杂度也会上一个数量级。本篇要分析的场景就是:多线程修改同一个对象的本地属性。

问题点

java多线程存在线程本地内存、主存同步的问题:线程会先加载主存变量到本地内存,然后再做处理。如果不做同步处理,不同的线程修改的可能是线程本地内存,这会导致最终线程本地内存、主存同步时互相覆盖。

案例评析

不作任何同步措施

public class Exam implements Runnable {
    Integer n = 0;
	
    @Override
    public void run() {
        for (int i=0; i<10000; i++) {
			n++;
        }
    }

    public static void main(String[] args) throws InterruptedException {
        Exam exam = new Exam();
        Thread a = new Thread(exam);
        Thread b = new Thread(exam);
        a.start();
        b.start();
        a.join();
        b.join();
        System.out.println(exam.n);
    }
}
这种写法,完全没考虑到多线程的同步问题,结果肯定会出问题的

使用volatile

public class Exam implements Runnable {
    volatile Integer n = 0;
	
    @Override
    public void run() {
        for (int i=0; i<10000; i++) {
			n++;
        }
    }

    public static void main(String[] args) throws InterruptedException {
        Exam exam = new Exam();
        Thread a = new Thread(exam);
        Thread b = new Thread(exam);
        a.start();
        b.start();
        a.join();
        b.join();
        System.out.println(exam.n);
    }
}
volatile修饰的变量,修改时会同时更新主存,看似没问题。这里我们要get到一个点:n++并不是原子操作,这个语句是分两步执行的,先读取,再修改。这其中有两个原子操作。

使用synchronized

public class Exam implements Runnable {
    Integer n = 0;
	
    @Override
    public void run() {
        for (int i=0; i<10000; i++) {
            synchronized (this) {
                n++;
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
        Exam exam = new Exam();
        Thread a = new Thread(exam);
        Thread b = new Thread(exam);
        a.start();
        b.start();
        a.join();
        b.join();
        System.out.println(exam.n);
    }
}
这个方案是可行的,synchronized代码块同时只有一个线程进来。当线程进来,读取变量、更新变量时,其他线程已经更新完变量。 这里,synchronized (this)不能换成synchronized (n),原因大家可以去探究一下

© XieZhichao 2022 - 2024