以下示例代码将演示 ++ 操作是非线程安全的,具体看注释的解释,仔细阅读和多次对比执行结果时,也许还能体验到线程被调度执行的随机性,或许对某些读者会有帮助。
还同时对比演示了synchronized关键字的作用----即线程的执行时,需要排队等候调度才行,直到已经获得锁的线程释放此锁,并且自己获得锁才能执行此关键字包含的代码块(或许有点绕)。
package threadpart;
/**
* 验证 ++ 操作不是原子性的,即多线程下存在安全问题
* @Author gzx
* @create 2021-12-30
*/
public class AtomicOrNot {
public static void main(String[] args) {
Runnable task=new Runnable() {
private int i;
@Override
public void run() {
// 可以分别单独调用下面这两个私有方法的运行结果分析对比体验一下,同步的作用和 ++操作是否是原子操作(也即是否是多线程安全的),以及所谓的并发场景
asyn();
// sync();
}
// 非同步方法,执行结果随机,因为i++不是线程安全的(我理解为不是原子操作,即这个表达式完成需要取值、算数运算、赋值三步子操作),如果不同步,高并发
// 情况下就会出问题,比如一个线程取得i的值,还没进行运算操作,另外一个线程也取得i的值(第一个线程还未修改过的值),后面具体是哪个线程对i先进行算数运算
// 操作,亦或者对i重新赋值都是不确定的。更何况高并发岂止两个线程呢?
private void asyn() {
System.out.println(Thread.currentThread().getName()+"\t"+i++);
}
// 非同步方法,不过含有synchronized修饰的同步块,所以输出i的值总是固定顺序的,但多次运行时,你会发现,相同的结果不一定由同一线程(指线程名字相同,多次运行,即使线程名相同
// 也不是同一个线程对象啊)得出
private void sync() {
synchronized (this) {
System.out.println(Thread.currentThread().getName()+"\t"+i++);
}
}
};
for(int m=0;m<10;m++) {
new Thread(task, "t"+m).start();
}
}
}
和++操作类似的非原子操作有很多,一般都包含了三步操作(取值,远算,赋值),如--,+=,-=等。