Java线程同步机制详解

2024/1/15

# Java线程同步机制详解

点击勘误issues (opens new window),哪吒感谢大家的阅读

# 1. 线程同步概述

# 1.1 为什么需要线程同步

在多线程环境中,多个线程可能同时访问共享资源,如果没有适当的同步机制,可能导致:

  • 数据不一致:多个线程同时修改同一数据
  • 竞态条件:程序结果依赖于线程执行的时序
  • 内存可见性问题:一个线程的修改对其他线程不可见

# 1.2 Java内存模型

public class MemoryVisibilityDemo {
    private static boolean flag = false;
    
    public static void main(String[] args) throws InterruptedException {
        Thread thread1 = new Thread(() -> {
            while (!flag) {
                // 可能永远不会结束,因为flag的修改对当前线程不可见
            }
            System.out.println("线程1结束");
        });
        
        Thread thread2 = new Thread(() -> {
            try {
                Thread.sleep(1000);
                flag = true;
                System.out.println("flag设置为true");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
        
        thread1.start();
        thread2.start();
        
        thread1.join();
        thread2.join();
    }
}

# 2. synchronized关键字

# 2.1 synchronized方法

public class SynchronizedMethodDemo {
    private int count = 0;
    
    // 同步实例方法
    public synchronized void increment() {
        count++;
    }
    
    // 同步静态方法
    public static synchronized void staticMethod() {
        System.out.println("静态同步方法");
    }
    
    public synchronized int getCount() {
        return count;
    }
    
    public static void main(String[] args) throws InterruptedException {
        SynchronizedMethodDemo demo = new SynchronizedMethodDemo();
        
        Thread[] threads = new Thread[10];
        for (int i = 0; i < 10; i++) {
            threads[i] = new Thread(() -> {
                for (int j = 0; j < 1000; j++) {
                    demo.increment();
                }
            });
        }
        
        for (Thread thread : threads) {
            thread.start();
        }
        
        for (Thread thread : threads) {
            thread.join();
        }
        
        System.out.println("最终计数: " + demo.getCount()); // 10000
    }
}

# 2.2 synchronized代码块

public class SynchronizedBlockDemo {
    private int count1 = 0;
    private int count2 = 0;
    private final Object lock1 = new Object();
    private final Object lock2 = new Object();
    
    public void method1() {
        synchronized (lock1) {
            count1++;
            System.out.println("Method1: " + count1);
        }
    }
    
    public void method2() {
        synchronized (lock2) {
            count2++;
            System.out.println("Method2: " + count2);
        }
    }
    
    // 细粒度锁,提高并发性能
    public void finegrainedLocking() {
        // 只同步必要的代码段
        int localVar = 0;
        // 一些不需要同步的操作
        localVar++;
        
        synchronized (lock1) {
            // 只有这部分需要同步
            count1 += localVar;
        }
    }
}

# 2.3 synchronized的实现原理

public class SynchronizedPrincipleDemo {
    private final Object monitor = new Object();
    
    public void demonstrateMonitor() {
        synchronized (monitor) {
            // 进入同步块时获取monitor锁
            System.out.println("在同步块中执行");
            // 退出同步块时释放monitor锁
        }
    }
    
    // 等价于上面的代码(概念上)
    public void equivalentCode() {
        // monitorenter
        try {
            System.out.println("在同步块中执行");
        } finally {
            // monitorexit
        }
    }
}

# 3. volatile关键字

# 3.1 volatile的作用

public class VolatileDemo {
    private volatile boolean flag = false;
    private volatile int count = 0;
    
    public void writer() {
        count = 42;
        flag = true; // volatile写,确保之前的写操作对其他线程可见
    }
    
    public void reader() {
        if (flag) { // volatile读,确保能看到最新值
            System.out.println("Count: " + count); // 保证能看到42
        }
    }
    
    public static void main(String[] args) {
        VolatileDemo demo = new VolatileDemo();
        
        Thread writerThread = new Thread(demo::writer);
        Thread readerThread = new Thread(() -> {
            while (!demo.flag) {
                // 等待flag变为true
            }
            demo.reader();
        });
        
        readerThread.start();
        writerThread.start();
    }
}

# 3.2 volatile vs synchronized

public class VolatileVsSynchronized {
    private volatile int volatileCount = 0;
    private int synchronizedCount = 0;
    
    // volatile不能保证原子性
    public void incrementVolatile() {
        volatileCount++; // 不是原子操作!
    }
    
    // synchronized保证原子性
    public synchronized void incrementSynchronized() {
        synchronizedCount++; // 原子操作
    }
    
    // 正确使用volatile的场景
    private volatile boolean shutdown = false;
    
    public void doWork() {
        while (!shutdown) {
            // 执行工作
        }
    }
    
    public void shutdown() {
        shutdown = true; // 简单的标志位设置
    }
}

# 4. wait()和notify()机制

# 4.1 基本用法

public class WaitNotifyDemo {
    private final Object lock = new Object();
    private boolean condition = false;
    
    public void waitingMethod() {
        synchronized (lock) {
            while (!condition) {
                try {
                    System.out.println("等待条件满足...");
                    lock.wait(); // 释放锁并等待
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                    return;
                }
            }
            System.out.println("条件满足,继续执行");
        }
    }
    
    public void notifyingMethod() {
        synchronized (lock) {
            condition = true;
            System.out.println("条件已设置,通知等待线程");
            lock.notify(); // 唤醒一个等待线程
            // lock.notifyAll(); // 唤醒所有等待线程
        }
    }
    
    public static void main(String[] args) throws InterruptedException {
        WaitNotifyDemo demo = new WaitNotifyDemo();
        
        Thread waitingThread = new Thread(demo::waitingMethod);
        Thread notifyingThread = new Thread(demo::notifyingMethod);
        
        waitingThread.start();
        Thread.sleep(1000); // 确保等待线程先启动
        notifyingThread.start();
        
        waitingThread.join();
        notifyingThread.join();
    }
}

# 4.2 生产者消费者模式

import java.util.LinkedList;
import java.util.Queue;

public class ProducerConsumerDemo {
    private final Queue<Integer> queue = new LinkedList<>();
    private final int capacity = 5;
    private final Object lock = new Object();
    
    public void produce() throws InterruptedException {
        int value = 0;
        while (true) {
            synchronized (lock) {
                while (queue.size() == capacity) {
                    System.out.println("队列已满,生产者等待...");
                    lock.wait();
                }
                
                queue.offer(value);
                System.out.println("生产: " + value);
                value++;
                
                lock.notifyAll(); // 通知消费者
            }
            Thread.sleep(1000);
        }
    }
    
    public void consume() throws InterruptedException {
        while (true) {
            synchronized (lock) {
                while (queue.isEmpty()) {
                    System.out.println("队列为空,消费者等待...");
                    lock.wait();
                }
                
                int value = queue.poll();
                System.out.println("消费: " + value);
                
                lock.notifyAll(); // 通知生产者
            }
            Thread.sleep(1500);
        }
    }
    
    public static void main(String[] args) {
        ProducerConsumerDemo demo = new ProducerConsumerDemo();
        
        Thread producer = new Thread(() -> {
            try {
                demo.produce();
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        });
        
        Thread consumer = new Thread(() -> {
            try {
                demo.consume();
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        });
        
        producer.start();
        consumer.start();
    }
}

# 5. Lock接口

# 5.1 ReentrantLock基本用法

import java.util.concurrent.locks.ReentrantLock;

public class ReentrantLockDemo {
    private final ReentrantLock lock = new ReentrantLock();
    private int count = 0;
    
    public void increment() {
        lock.lock();
        try {
            count++;
            System.out.println("Count: " + count);
        } finally {
            lock.unlock(); // 必须在finally中释放锁
        }
    }
    
    public boolean tryIncrement() {
        if (lock.tryLock()) {
            try {
                count++;
                System.out.println("Try increment success: " + count);
                return true;
            } finally {
                lock.unlock();
            }
        } else {
            System.out.println("无法获取锁,跳过操作");
            return false;
        }
    }
    
    public static void main(String[] args) {
        ReentrantLockDemo demo = new ReentrantLockDemo();
        
        // 创建多个线程测试
        for (int i = 0; i < 3; i++) {
            new Thread(() -> {
                for (int j = 0; j < 3; j++) {
                    demo.increment();
                    demo.tryIncrement();
                }
            }).start();
        }
    }
}

# 5.2 ReadWriteLock读写锁

import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

public class ReadWriteLockDemo {
    private final ReadWriteLock lock = new ReentrantReadWriteLock();
    private String data = "初始数据";
    
    public String read() {
        lock.readLock().lock();
        try {
            System.out.println(Thread.currentThread().getName() + " 正在读取: " + data);
            Thread.sleep(1000); // 模拟读取耗时
            return data;
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            return null;
        } finally {
            lock.readLock().unlock();
        }
    }
    
    public void write(String newData) {
        lock.writeLock().lock();
        try {
            System.out.println(Thread.currentThread().getName() + " 正在写入: " + newData);
            Thread.sleep(1000); // 模拟写入耗时
            this.data = newData;
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        } finally {
            lock.writeLock().unlock();
        }
    }
    
    public static void main(String[] args) {
        ReadWriteLockDemo demo = new ReadWriteLockDemo();
        
        // 创建多个读线程
        for (int i = 0; i < 3; i++) {
            new Thread(() -> demo.read(), "读线程-" + i).start();
        }
        
        // 创建写线程
        new Thread(() -> demo.write("新数据"), "写线程").start();
        
        // 再创建读线程
        for (int i = 3; i < 6; i++) {
            new Thread(() -> demo.read(), "读线程-" + i).start();
        }
    }
}

# 6. 条件变量Condition

# 6.1 Condition基本用法

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

public class ConditionDemo {
    private final ReentrantLock lock = new ReentrantLock();
    private final Condition condition = lock.newCondition();
    private boolean ready = false;
    
    public void waitForCondition() {
        lock.lock();
        try {
            while (!ready) {
                System.out.println("等待条件满足...");
                condition.await(); // 等待条件
            }
            System.out.println("条件满足,继续执行");
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        } finally {
            lock.unlock();
        }
    }
    
    public void signalCondition() {
        lock.lock();
        try {
            ready = true;
            System.out.println("条件已满足,发送信号");
            condition.signal(); // 唤醒等待线程
        } finally {
            lock.unlock();
        }
    }
    
    public static void main(String[] args) throws InterruptedException {
        ConditionDemo demo = new ConditionDemo();
        
        Thread waitingThread = new Thread(demo::waitForCondition);
        Thread signalingThread = new Thread(demo::signalCondition);
        
        waitingThread.start();
        Thread.sleep(1000);
        signalingThread.start();
        
        waitingThread.join();
        signalingThread.join();
    }
}

# 6.2 多条件变量示例

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

public class MultiConditionDemo {
    private final ReentrantLock lock = new ReentrantLock();
    private final Condition notFull = lock.newCondition();
    private final Condition notEmpty = lock.newCondition();
    
    private final Object[] items = new Object[10];
    private int putIndex = 0;
    private int takeIndex = 0;
    private int count = 0;
    
    public void put(Object item) throws InterruptedException {
        lock.lock();
        try {
            while (count == items.length) {
                notFull.await(); // 等待不满条件
            }
            items[putIndex] = item;
            putIndex = (putIndex + 1) % items.length;
            count++;
            System.out.println("放入元素: " + item + ", 当前数量: " + count);
            notEmpty.signal(); // 通知不空条件
        } finally {
            lock.unlock();
        }
    }
    
    public Object take() throws InterruptedException {
        lock.lock();
        try {
            while (count == 0) {
                notEmpty.await(); // 等待不空条件
            }
            Object item = items[takeIndex];
            items[takeIndex] = null;
            takeIndex = (takeIndex + 1) % items.length;
            count--;
            System.out.println("取出元素: " + item + ", 当前数量: " + count);
            notFull.signal(); // 通知不满条件
            return item;
        } finally {
            lock.unlock();
        }
    }
}

# 7. 同步机制比较

# 7.1 性能比较

import java.util.concurrent.locks.ReentrantLock;

public class SynchronizationComparison {
    private int synchronizedCount = 0;
    private int lockCount = 0;
    private final Object syncLock = new Object();
    private final ReentrantLock reentrantLock = new ReentrantLock();
    
    public void synchronizedIncrement() {
        synchronized (syncLock) {
            synchronizedCount++;
        }
    }
    
    public void lockIncrement() {
        reentrantLock.lock();
        try {
            lockCount++;
        } finally {
            reentrantLock.unlock();
        }
    }
    
    public static void performanceTest() {
        SynchronizationComparison demo = new SynchronizationComparison();
        int threadCount = 10;
        int iterations = 100000;
        
        // 测试synchronized性能
        long startTime = System.currentTimeMillis();
        Thread[] syncThreads = new Thread[threadCount];
        for (int i = 0; i < threadCount; i++) {
            syncThreads[i] = new Thread(() -> {
                for (int j = 0; j < iterations; j++) {
                    demo.synchronizedIncrement();
                }
            });
        }
        
        for (Thread thread : syncThreads) {
            thread.start();
        }
        
        for (Thread thread : syncThreads) {
            try {
                thread.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        
        long syncTime = System.currentTimeMillis() - startTime;
        System.out.println("Synchronized耗时: " + syncTime + "ms");
        
        // 测试ReentrantLock性能
        startTime = System.currentTimeMillis();
        Thread[] lockThreads = new Thread[threadCount];
        for (int i = 0; i < threadCount; i++) {
            lockThreads[i] = new Thread(() -> {
                for (int j = 0; j < iterations; j++) {
                    demo.lockIncrement();
                }
            });
        }
        
        for (Thread thread : lockThreads) {
            thread.start();
        }
        
        for (Thread thread : lockThreads) {
            try {
                thread.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        
        long lockTime = System.currentTimeMillis() - startTime;
        System.out.println("ReentrantLock耗时: " + lockTime + "ms");
    }
    
    public static void main(String[] args) {
        performanceTest();
    }
}

# 8. 最佳实践

# 8.1 避免死锁

public class DeadlockAvoidance {
    private final Object lock1 = new Object();
    private final Object lock2 = new Object();
    
    // 错误的做法 - 可能导致死锁
    public void badMethod1() {
        synchronized (lock1) {
            synchronized (lock2) {
                System.out.println("Method1执行");
            }
        }
    }
    
    public void badMethod2() {
        synchronized (lock2) {
            synchronized (lock1) {
                System.out.println("Method2执行");
            }
        }
    }
    
    // 正确的做法 - 按顺序获取锁
    public void goodMethod1() {
        synchronized (lock1) {
            synchronized (lock2) {
                System.out.println("GoodMethod1执行");
            }
        }
    }
    
    public void goodMethod2() {
        synchronized (lock1) { // 同样的顺序
            synchronized (lock2) {
                System.out.println("GoodMethod2执行");
            }
        }
    }
}

# 8.2 减少锁的粒度

import java.util.concurrent.ConcurrentHashMap;

public class LockGranularity {
    // 粗粒度锁 - 整个对象
    private final Object coarseLock = new Object();
    private int value1 = 0;
    private int value2 = 0;
    
    public void coarseGrainedMethod() {
        synchronized (coarseLock) {
            value1++;
            value2++;
        }
    }
    
    // 细粒度锁 - 分别保护不同的资源
    private final Object lock1 = new Object();
    private final Object lock2 = new Object();
    
    public void fineGrainedMethod1() {
        synchronized (lock1) {
            value1++;
        }
    }
    
    public void fineGrainedMethod2() {
        synchronized (lock2) {
            value2++;
        }
    }
    
    // 使用并发容器
    private final ConcurrentHashMap<String, Integer> concurrentMap = new ConcurrentHashMap<>();
    
    public void useConcurrentCollection() {
        concurrentMap.put("key1", 1);
        concurrentMap.put("key2", 2);
        // 不需要显式同步
    }
}

# 9. 总结

本文详细介绍了Java线程同步机制,包括:

  1. synchronized关键字:方法同步和代码块同步
  2. volatile关键字:保证内存可见性
  3. wait/notify机制:线程间通信
  4. Lock接口:更灵活的锁机制
  5. Condition条件变量:精确的线程控制
  6. 最佳实践:避免死锁、减少锁粒度

选择合适的同步机制对于编写高效、安全的多线程程序至关重要。在实际开发中,应该根据具体场景选择最适合的同步方式。

# 参考资料