21xrx.com
2024-11-25 06:07:21 Monday
登录
文章检索 我的文章 写文章
Java内存模型:从面试角度剖析
2023-06-16 13:04:20 深夜i     --     --
Java内存模型 多线程 synchronized关键字 volatile关键字 原子变量类

Java作为一门面向对象的高级编程语言,其在开发中的使用广泛,涉及到许多问题,其中最常见的就是内存模型。Java内存模型定义了在多个线程下内存如何被分配、访问和管理,是Java并发编程的重要部分。针对这一问题,本文从面试角度来剖析Java内存模型,帮助读者更好地理解和掌握这一知识点。

从JVM的角度来看,JVM中的内存分为堆内存、栈内存、方法区和直接内存。在多线程编程中,需要考虑并发访问带来的线程安全问题。为了解决这个问题,Java内存模型在并发编程过程中,引入了一系列保证多线程访问的原子性、有序性和可见性的机制。这些机制包括synchronized关键字、volatile关键字、原子变量类和锁等机制。

接下来,我们通过以下代码案例来说明Java内存模型中这些机制的使用:

 java

public class Test {

  // 此变量共享

  public static volatile int a = 0;

  public static void main(String[] args) {

    // 创建两个线程

    Thread thread1 = new Thread(() -> {

      for (int i = 0; i < 1000; i++) {

        // a自增操作

        a++;

      }

    });

    Thread thread2 = new Thread(() -> {

      for (int i = 0; i < 1000; i++) {

        // a自增操作

        a++;

      }

    });

    // 启动两个线程

    thread1.start();

    thread2.start();

    try {

      // 等待两个线程执行完成

      thread1.join();

      thread2.join();

    } catch (InterruptedException e) {

      e.printStackTrace();

    }

    // 输出a的值

    System.out.println("a=" + a);

  }

}

在以上代码中,我们定义了一个共享变量a,并创建了两个线程,分别对a进行1000次自增操作。如果不考虑内存可见性的问题,在多次运行代码时,有可能输出的a的值小于2000。这是因为在两个线程对a进行操作时,缓存中的a的值可能不会及时更新到主存中,导致两个线程出现竞争问题而导致结果不准确。

为了解决这个竞争问题,可以使用Java内存模型中的volatile关键字,表示该变量具有可见性。在上述代码中,将变量a声明为volatile可以保证多线程在访问变量a时,拿到的都是主存中最新的值。重新运行代码后,输出的a的值将一定等于2000。

除了volatile关键字,在Java内存模型中,还可以使用synchronized关键字、原子变量类和锁等机制来保证多线程访问时的线程安全问题。例如,在需要对临界区进行同步访问时,可以使用synchronized关键字锁住该部分代码:

 java

public class Test {

  // 此变量共享

  public static int a = 0;

  public static void main(String[] args) {

    // 创建两个线程

    Thread thread1 = new Thread(() -> {

      synchronized (Test.class) {

        for (int i = 0; i < 1000; i++) {

          // a自增操作

          a++;

        }

      }

    });

    Thread thread2 = new Thread(() -> {

      synchronized (Test.class) {

        for (int i = 0; i < 1000; i++) {

          // a自增操作

          a++;

        }

      }

    });

    // 启动两个线程

    thread1.start();

    thread2.start();

    try {

      // 等待两个线程执行完成

      thread1.join();

      thread2.join();

    } catch (InterruptedException e) {

      e.printStackTrace();

    }

    // 输出a的值

    System.out.println("a=" + a);

  }

}

在以上代码中,我们使用synchronized关键字将线程访问的临界区代码进行同步,保证了多线程对变量a的访问的线程安全问题。

  
  

评论区

{{item['qq_nickname']}}
()
回复
回复