学习Java内存模型JMM心得
什么是Java内存模型
Java内存模型(Java Memory Model,JMM)是一种用于保证在多线程情况下共享变量的可见性和有序性的机制。
JMM的核心概念
Java内存模型中有三个核心概念:原子性、可见性和有序性。
原子性
原子性指的是在同一时间只有一个线程可以访问共享变量。Java中的基本数据类型,如int、long等,都是具有原子性的。但是,对于复合操作,如i++,实际上是由多个步骤组成的,因此需要通过synchronized或者java.util.concurrent.locks中的锁机制来保证原子性。
可见性
可见性指的是一个线程对共享变量的修改,能够被其他线程及时地看到。在Java中,可以使用volatile关键字来保证可见性。通过volatile关键字,对该变量进行读操作时会直接从内存中读取最新的值,对该变量进行写操作时会立即将修改的值刷回到内存中。
有序性
有序性指的是指令执行的顺序,Java中通过happens-before的规则来保证有序性。happens-before的规则规定了一些时间上的先后顺序,例如在一个线程中,先发生的指令会先执行;在一个锁里,先释放锁的线程的操作happens-before于后获取锁的线程中的操作。
JMM的示例说明
示例1:通过volatile关键字保证变量可见性
下面是一段代码,其中共享变量flag未使用volatile关键字修饰:
class MyThread implements Runnable {
private boolean flag = false;
public void run() {
while (!flag) {
//Do something
}
System.out.println("Thread stopped.");
}
public void stop() {
this.flag = true;
}
}
public class VolatileTest {
public static void main(String[] args) throws InterruptedException {
MyThread myThread = new MyThread();
Thread t = new Thread(myThread);
t.start();
Thread.sleep(1000);
myThread.stop();
}
}
在上面代码中,flag被一个线程修改后,另一个线程无法及时地感知到其变化。因此,程序无法正常停止。现在我们通过将flag变量使用volatile关键字修饰,使其变为可见的。
class MyThread implements Runnable {
private volatile boolean flag = false;
public void run() {
while (!flag) {
//Do something
}
System.out.println("Thread stopped.");
}
public void stop() {
this.flag = true;
}
}
public class VolatileTest {
public static void main(String[] args) throws InterruptedException {
MyThread myThread = new MyThread();
Thread t = new Thread(myThread);
t.start();
Thread.sleep(1000);
myThread.stop();
}
}
示例2:使用锁机制保证原子性
下面是一个简单的例子,使用volatile关键字不足以保证原子性。
class MyRunnable implements Runnable {
private volatile int count = 0;
public void run() {
for (int i = 0; i < 100000; i++) {
count++;
}
System.out.println("count: " + count);
}
}
public class VolatileAtomicityTest {
public static void main(String[] args) throws InterruptedException {
MyRunnable myRunnable = new MyRunnable();
Thread t1 = new Thread(myRunnable);
Thread t2 = new Thread(myRunnable);
t1.start();
t2.start();
t1.join();
t2.join();
}
}
在上面的代码中,我们使用volatile关键字保证了count变量的可见性,但是由于count++操作是一个复合操作,因此并不具备原子性。运行上面的代码,就会发现输出的count的值并不是预期的200000。现在我们可以使用锁机制来保证原子性。
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
class MyRunnable implements Runnable {
private int count = 0;
private Lock lock = new ReentrantLock();
public void run() {
for (int i = 0; i < 100000; i++) {
lock.lock();
try {
count++;
} finally {
lock.unlock();
}
}
System.out.println("count: " + count);
}
}
public class LockAtomicityTest {
public static void main(String[] args) throws InterruptedException {
MyRunnable myRunnable = new MyRunnable();
Thread t1 = new Thread(myRunnable);
Thread t2 = new Thread(myRunnable);
t1.start();
t2.start();
t1.join();
t2.join();
}
}
在上述代码中,我们使用了Lock机制来保证了count变量的原子性。运行上面的代码,输出的count的值为200000,证明了操作的原子性。
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:学习Java内存模型JMM心得 - Python技术站