Java 内存模型: 并发编程的基石

大家好,我是微赚淘客返利系统3.0的小编,是个冬天不穿秋裤,天冷也要风度的程序猿!

Java内存模型(Java Memory Model, JMM)是Java并发编程的核心,它定义了Java虚拟机(JVM)如何与计算机内存交互,以及如何管理线程间的数据可见性。本文将深入探讨Java内存模型的基本概念和特性。

内存模型的基本概念

JMM定义了一组规则,这些规则决定了在并发编程中,当多线程访问共享变量时,数据是如何在内存中进行同步的。

主内存与工作内存

在JMM中,所有的变量都存储在主内存(Main Memory)中。每个线程还有自己的工作内存(Working Memory),用于存储该线程使用的变量的副本。

package cn.juwatech.memory;

public class SharedVariable {
    private int number;

    public int getNumber() {
        return number;
    }

    public void setNumber(int number) {
        this.number = number;
    }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
原子性操作

原子性操作是指在执行过程中不会被其他线程中断的操作。Java中,对基本数据类型的赋值操作是原子的。

package cn.juwatech.memory;

public class AtomicOperationExample {
    private int count = 0;

    public void increment() {
        count++; // 原子操作
    }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
可见性

可见性是指当一个线程修改了共享变量的值,其他线程能够立即看到这个修改。

package cn.juwatech.memory;

public class VisibilityExample {
    private volatile boolean running = true;

    public void stop() {
        running = false;
    }

    public void checkRunning() {
        while (running) {
            // 循环直到running变为false
        }
    }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
有序性

在单线程环境中,代码的执行顺序是按照编写的顺序进行的。但在多线程环境中,由于编译器优化和处理器乱序执行,代码的执行可能不是按照编写的顺序。

package cn.juwatech.memory;

public class OrderlinessExample {
    private int a = 0;
    private int b = 0;

    public void writer() {
        a = 1;
        b = 2;
    }

    public void reader() {
        if (a == 1 && b == 2) {
            // 可能不会按预期执行
        }
    }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
锁与同步

锁和同步机制是保证原子性、可见性和有序性的重要手段。

package cn.juwatech.memory;

public class SynchronizedMethodExample {
    private int count = 0;

    public synchronized void increment() {
        count++; // 通过同步方法保证原子性
    }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
锁的释放与获取

在Java中,可以使用wait()notify()方法来控制线程间的协调。

package cn.juwatech.memory;

public class LockReleaseAndAcquisitionExample {
    private int number = 0;

    public synchronized void increase() {
        number++;
        notify();
    }

    public synchronized void waitUntilIncreased() {
        while (number == 0) {
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
锁的种类

Java提供了多种锁,包括内置锁、显式锁(如ReentrantLock)和读写锁(如ReadWriteLock)。

package cn.juwatech.memory;

import java.util.concurrent.locks.ReentrantLock;

public class LockExample {
    private final ReentrantLock lock = new ReentrantLock();

    public void performAction() {
        lock.lock();
        try {
            // 线程安全的代码
        } finally {
            lock.unlock();
        }
    }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
锁的粒度

锁的粒度是指锁的作用范围。细粒度锁可以减少锁的竞争,提高并发性。

package cn.juwatech.memory;

public class FineGrainedLockExample {
    private final Object lock1 = new Object();
    private final Object lock2 = new Object();

    public void method1() {
        synchronized (lock1) {
            // 操作1
        }
    }

    public void method2() {
        synchronized (lock2) {
            // 操作2
        }
    }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
锁的性能考虑

使用锁时需要考虑性能,避免死锁和活锁。

package cn.juwatech.memory;

public class DeadlockExample {
    private final Object resource1 = new Object();
    private final Object resource2 = new Object();

    public void method1() {
        synchronized (resource1) {
            synchronized (resource2) {
                // 可能发生死锁
            }
        }
    }

    public void method2() {
        synchronized (resource2) {
            synchronized (resource1) {
                // 避免死锁的顺序
            }
        }
    }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
结论

Java内存模型是理解和实现并发程序的基础。通过掌握原子性、可见性和有序性的概念,以及合理使用锁和同步机制,可以编写出高效且线程安全的并发程序。深入理解JMM,可以帮助我们避免并发编程中常见的问题,如死锁和竞态条件。