在多线程编程中,一个不可避免的也是最需要关注的问题就是线程安全。与进程拥有独立的进程空间不一样,一个进程内的多个线程是共享该进程的资源的。由于CPU通常为多核架构,且线程又是并发执行的,所以可能会出现同一时刻存在多个线程同时访问同一个资源的现象。

为了解决以上线程对共享资源的并发操作而导致的数据不一致性问题,在JDK层面即可解决并发访问、修改的问题,从而实现线程安全。

synchronized 关键字

Java 提供了 synchronized 关键字来实现互斥锁,保证多个线程对共享资源的互斥访问。

synchronized 关键字可以用在方法或代码块中。

  • 类的静态方法(对当前类加锁)
  • 类的成员方法(对当前对象实例加锁)
  • 代码块(对指定的对象或类加锁)
public class SynchronizedDemo {
    // 使用SynchronizedDemo类对象自身作为监视器对象
    public static synchronized void testStaticMethod() {

    }

    // 使用SynchronizedDemo的对象实例作为监视器对象
    public synchronized void testMemberMethod() {

    }

    // 代码块的监视器对象
    private Object monitor = new Object();
    public void testCodeBlock() {
        synchronized (monitor) {
        }
    }
}

volatile 关键字

volatile 关键字修饰变量,用来实现共享变量的线程可见性,还可以防止指令的重排。但是无法保证操作的原子性。

final 关键字

如果多个线程只是对共享资源进行读操作,则不会存在线程安全问题,可以使用 final 关键字修饰,使用 final 修饰某个对象后,该对象是只读、不可变的,不需要额外的加锁操作,性能更高。

ThreadLocal 线程本地变量

空间换时间,通过使用 ThreadLocal 对共享变量进行包装,使得每个线程都包含这个共享变量的一个副本,实现了共享变量对每个线程的独立性,不需要通过加锁实现线程安全。

在使用 ThreadLocal 时,徐奥衡量该共享变量的内存占用问题,一般是整数等简单的数据类型使用。