Java多线程并发编程:并发三大要素
多线程编程本质上就是并发编程,而对于并发编程,有三个重要的要素:原子性、可见性和有序性。
原子性
原子性指的是一个操作是不可被打断的,即要么执行成功,要么执行失败,不会存在执行一半的情况。在多线程环境下,多个线程同时访问同一个变量时,可能会发生数据竞争。数据竞争常常发生在复合操作时,例如i++这样的简单操作,看似只有一行代码,但是其中包含了读取i的值、计算i+1、将i+1的值写入内存、刷新缓存等多个操作,这些操作不是原子性的,因此可能会出现数据竞争。为了解决数据竞争,Java提供了synchronized和lock关键字来保证线程安全。
下面是一个简单的数据竞争示例,未加锁对共享资源进行修改:
public class Counter extends Thread{
private static int count = 0;
public static void main(String[] args) throws InterruptedException {
Counter c1 = new Counter();
Counter c2 = new Counter();
c1.start();
c2.start();
c1.join();
c2.join();
System.out.println("count = " + count);
}
public void run() {
for(int i=0; i<10000; i++) {
count++;
}
}
}
在多次运行后会发现结果并不稳定,产生了数据竞争。如果对count++加锁,可以解决数据竞争的问题。
可见性
可见性指的是多个线程访问同一个变量时,变量在内存中的值应该保持一致。但是,由于JVM为了提高执行效率,会对变量进行优化,在执行过程中将变量缓存在CPU的寄存器或者CPU Cache中;此时,其他线程可能无法立即看到该变量的变化。这就导致线程之间对同一个变量的值是不一致的。为了解决可见性问题,Java提供了volitile关键字来保证共享变量的修改对线程的可见性。
下面是一个可见性问题的示例:
public class VisibilityDemo extends Thread {
private boolean stop = false;
public void stopMe() {
stop = true;
}
public void run() {
int i = 0;
while (!stop) {
i++;
}
System.out.println("Thread stoped." + i);
}
public static void main(String[] args) throws InterruptedException {
VisibilityDemo thread = new VisibilityDemo();
thread.start();
Thread.sleep(1000);
thread.stopMe();
System.out.println("stop flag stoped.");
}
}
该程序会启动一个线程,并且标志位stop的初始值为false。在主线程中,让线程睡眠1000ms后,将stop标志位设置为true,但由于可见性问题,线程并未停止。如果将stop标志位加上volatile修饰符,可避免可见性问题。
有序性
有序性指的是在多线程执行时,指令不能乱序执行,必须按照代码的原有顺序执行。在Java内存模型中,有一些指令是具有同步性质的,例如synchronized、volatile和Lock等关键字。在这些指令的前后,会自动加入内存屏障,保证指令按照代码原有顺序执行。在Java中,可以使用synchronized关键字或者volatile关键字保证内存屏障的插入。
示例代码:
import java.util.concurrent.atomic.AtomicInteger;
public class OrderDemo extends Thread {
static AtomicInteger x = new AtomicInteger(0);
static AtomicInteger y = new AtomicInteger(0);
static AtomicInteger a = new AtomicInteger(0);
static AtomicInteger b = new AtomicInteger(0);
public void run() {
int i = a.get();
x.set(i);
b.set(1);
}
public static void main(String[] args) throws InterruptedException {
Thread one = new Thread(new Runnable() {
public void run() {
a.set(1);
y.set(b.get());
}
});
Thread other = new OrderDemo();
one.start();
other.start();
one.join();
other.join();
System.out.println("(" + x.get() + "," + y.get() + ")");
}
}
该代码中,one线程中,先将a的值设置为1,再将b的值设置为1。在other线程中,a的值被获取赋值给i,再将i的值给x,最后将1赋值给b,程序输出结果会出现不同的情况,有可能输出(0,1),有时候输出(0,0)。加上synchronized关键字或者volatile关键字后,可以避免乱序执行问题,使输出值恒定为(1,1)。
以上就是Java多线程并发编程中的三大要素:原子性、可见性和有序性的详细讲解以及示例分析。
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:Java多线程并发编程 并发三大要素 - Python技术站