一、原子性
原子性是指一个操作是不可中断的整体,要么全部执行成功,要么全部执行失败。在多线程环境下,原子性是保证数据正确的基础。
Java提供了synchronized关键字和java.util.concurrent.atomic包下的原子类来实现原子操作。
public class Counter { private int count = 0; public synchronized void increment() { count++; } public int getCount() { return count; } }
在上面的代码中,通过synchronized关键字使increment方法变成原子操作,从而保证了count的正确性。
除此之外,还可以使用AtomicInteger这个原子类来实现计数器:
public class Counter { private AtomicInteger count = new AtomicInteger(); public void increment() { count.incrementAndGet(); } public int getCount() { return count.get(); } }
二、可见性
可见性是指当一个线程修改了共享变量时,另一个线程能够立即看到这个修改。在多线程环境下,可见性是保证数据一致性的基础。
Java提供了volatile关键字来实现可见性,它能够保证在读取和修改变量时,都是直接从内存中读取和写入,而不是从缓存中读取和写入。
public class Counter { private volatile int count = 0; public void increment() { count++; } public int getCount() { return count; } }
三、有序性
有序性是指程序执行的顺序与代码的先后顺序一致,Java中的指令重排可能会导致代码执行的顺序不一致,从而产生一系列问题。
Java提供了synchronized和volatile两种方式来保证有序性。
在下面的例子中,线程A和线程B执行的结果可能会不同:
public class OrderExample { private int x = 0; private boolean flag = false; public void write() { x = 1; flag = true; } public void read() { if (flag) { int y = x + 1; } } }
在上面的代码中,如果线程A先执行write方法,然后线程B执行read方法,在没有任何同步措施的情况下,y可能为0而不是2。
使用volatile关键字可以避免指令重排:
public class OrderExample { private volatile int x = 0; private volatile boolean flag = false; public void write() { x = 1; flag = true; } public void read() { if (flag) { int y = x + 1; } } }
结语
并发编程是一个复杂的问题,掌握并发三大特性对于正确使用多线程至关重要。