在Java中,可以通过以下方式来保证线程对共享变量
的操作的原子性
和有序性
。
-
synchronized关键字:使用synchronized关键字可以保证代码块或方法在
同一时刻只能被一个线程执行
,从而保证了对共享变量的操作的原子性。当一个线程获取到锁时,其他线程必须等待锁释放后
才能继续执行。 -
使用锁:除了synchronized关键字外,还可以使用
显式锁(如ReentrantLock)
来保证对共享变量的操作的原子性。通过lock()和unlock()方法来控制临界区
的访问,确保只有一个线程能够执行临界区代码。 -
原子类:Java提供了一系列的原子类(如AtomicInteger、AtomicLong等),这些类提供了一些原子操作,可以保证对
共享变量的操作是原子的
,不会被线程干扰。 -
volatile关键字:volatile关键字可以保证
对共享变量的写操作
对其他线程是可见
的,同时也保证了对共享变量的操作是有序
的。但是,volatile关键字并不能保证原子性,只能保证可见性和有序性。 -
使用并发容器:Java提供了一些并发容器(如ConcurrentHashMap、ConcurrentLinkedQueue等),这些容器内部使用了一些锁或CAS(Compare and Swap)操作来保证对容器的操作是线程安全的。
当一个变量被声明为volatile时,Java编译器和运行时系统会确保对这个变量的读写操作都是有序的
。这意味着,volatile关键字可以保证在多线程环境下,对这个变量的操作不会乱序执行,而是按照代码的顺序进行。下面结合代码说明一下volatile关键字是如何保证有序性的:
java
public class VolatileOrderExample {
private volatile int number = 0;
private volatile boolean ready = false;
public void writer() {
number = 42;
ready = true;
}
public void reader() {
if (ready) {
System.out.println("Number: " + number);
}
}
public static void main(String[] args) {
VolatileOrderExample example = new VolatileOrderExample();
Thread writerThread = new Thread(() -> {
example.writer();
});
Thread readerThread = new Thread(() -> {
example.reader();
});
writerThread.start();
readerThread.start();
}
}
在上面的代码中,定义了一个包含两个volatile变量的类 VolatileOrderExample 。在 writer() 方法中,先将 number 赋值为42
,然后再将 ready 设置为true
。在 reader() 方法中,会检查 ready 的值,如果为true,则打印 number 的值。
由于 number 和 ready 都被声明为volatile,因此在多线程环境下,对这两个变量的操作是有序的
。在 writer() 方法中,先修改 number 的值,然后再修改 ready 的值
,这样可以保证在 reader() 方法中,只有当 ready 为true时
才会读取到正确的 number 的值
,不会出现乱序执行的情况。