一、原子性
原子性是指一个操作是不可中断的整体,要么全部执行成功,要么全部执行失败。在多线程环境下,原子性是保证数据正确的基础。
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;
}
}
}
结语
并发编程是一个复杂的问题,掌握并发三大特性对于正确使用多线程至关重要。