当多个线程并发执行同一段代码时,有可能会出现线程安全问题。而Java多线程的原子性,可见性和有序性是解决这些线程安全问题的关键。
- 原子性:原子性指的是一个操作不可中断,要么全部执行成功,要么全部执行失败。Java的基本数据类型的读取和赋值都是具有原子性的。但当多个线程同时对同一个变量进行运算时,就需要考虑原子性的问题。
示例说明:
public class AtomicityExample {
private int count = 0;
public void increment() {
count++;
}
public int getCount() {
return count;
}
public static void main(String[] args) {
AtomicityExample example = new AtomicityExample();
ExecutorService executorService = Executors.newCachedThreadPool();
for (int i = 0; i < 1000; i++) {
executorService.submit(() -> example.increment());
}
executorService.shutdown();
try {
executorService.awaitTermination(1, TimeUnit.MINUTES);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(example.getCount());
}
}
在这个示例中,我们通过线程池创建了1000个线程,每个线程调用increment方法对count进行自增。但实际上最终的结果并不是1000,而是一个小于1000的整数。这是因为对于非原子操作的count++,某个线程在读取count的时候可能被挂起,然后其他线程操作count,导致每个线程执行完毕后,实际上count的值并没有自增到预期的值。
为了解决这个问题,我们可以使用synchronized关键字或者Java提供的Atomic类来保证原子性。
- 可见性:可见性指的是当一个线程对共享变量进行修改时,其他线程能够立即看到这个修改。Java中使用volatile关键字来保证可见性。
示例说明:
public class VisibilityExample {
private volatile boolean flag = false;
public void setFlag(boolean flag) {
this.flag = flag;
}
public static void main(String[] args) throws InterruptedException {
VisibilityExample example = new VisibilityExample();
new Thread(() -> {
while (!example.flag) {
}
System.out.println(Thread.currentThread().getName() + ": flag is set to true");
}).start();
Thread.sleep(100);
example.setFlag(true);
System.out.println(Thread.currentThread().getName() + ": set flag to true");
}
}
在这个示例中,我们定义了一个带有volatile修饰的boolean类型的flag变量,然后创建了一个新线程一直在轮询flag变量,直到flag变量被设置为true之后才输出信息。主线程在启动新线程之后,等待一段时间之后将flag设置为true。这样可以保证新线程能够看到主线程修改flag变量的操作。因为flag变量的可见性被保证,所以新线程会在flag被设置为true后立即输出信息。
- 有序性:有序性指的是代码的执行顺序和程序编写的顺序一致。Java中使用synchronized关键字或者volatile关键字来保证有序性。
示例说明:
public class OrderExample {
private int i = 0;
private boolean flag = false;
public synchronized void write() {
i = 2;
flag = true;
}
public synchronized void read() {
if (flag) {
System.out.println(i);
}
}
public static void main(String[] args) {
OrderExample example = new OrderExample();
new Thread(() -> example.write()).start();
new Thread(() -> example.read()).start();
}
}
在这个示例中,我们定义了一个带有synchronized修饰的write方法和read方法。write方法会先将i设置为2,再将flag设置为true。read方法会判断flag的值,如果为true,则输出i的值。我们创建了两个线程分别调用write方法和read方法。由于write方法和read方法都使用了synchronized关键字修饰,所以它们的执行顺序可以保证和程序编写的顺序一致,即先执行write方法设置i和flag的值,然后再执行read方法输出i的值。通过这种方式,就可以保证代码的有序性。
综上所述,Java中的原子性、可见性和有序性是解决多线程并发执行中出现线程安全问题的关键。在编写多线程程序的时候,需要特别关注这些问题,以确保程序的正确性。
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:Java多线程的原子性,可见性,有序性你都了解吗 - Python技术站