Java中的同步机制

134 阅读4分钟

6.1 引言

同步机制在并发编程中的作用 在并发编程中,多线程同时访问和修改共享资源时可能会导致数据不一致或竞争条件。为了确保线程安全,Java 提供了多种同步机制来协调线程对共享资源的访问。同步机制在多线程环境中起着至关重要的作用,能够防止数据竞态条件,保证数据的一致性和正确性。

本文的内容结构 本文将介绍 Java 中常用的同步机制,主要内容包括:

使用 synchronized 关键字进行线程同步 显式锁的使用 读写锁的概念和应用

6.2 synchronized关键字

使用synchronized进行线程同步 synchronized 关键字是 Java 提供的最基础的同步机制,用于确保同一时间只有一个线程可以访问同步代码块或方法。可以通过两种方式使用 synchronized 关键字:

同步方法:在方法声明中使用 synchronized 关键字。 同步代码块:在方法内部使用 synchronized 代码块。 同步方法示例

public class SynchronizedMethodDemo {
    private int count = 0;
 
    public synchronized void increment() {
        count++;
    }
 
    public static void main(String[] args) throws InterruptedException {
        SynchronizedMethodDemo demo = new SynchronizedMethodDemo();
 
        Thread thread1 = new Thread(demo::increment);
        Thread thread2 = new Thread(demo::increment);
 
        thread1.start();
        thread2.start();
 
        thread1.join();
        thread2.join();
 
        System.out.println("Final count: " + demo.count);
    }
}

同步代码块示例

public class SynchronizedBlockDemo {
    private int count = 0;
    private final Object lock = new Object();
 
    public void increment() {
        synchronized (lock) {
            count++;
        }
    }
 
    public static void main(String[] args) throws InterruptedException {
        SynchronizedBlockDemo demo = new SynchronizedBlockDemo();
 
        Thread thread1 = new Thread(demo::increment);
        Thread thread2 = new Thread(demo::increment);
 
        thread1.start();
        thread2.start();
 
        thread1.join();
        thread2.join();
 
        System.out.println("Final count: " + demo.count);
    }
}

synchronized的底层实现 synchronized 关键字的底层实现是基于对象头中的监视器锁(Monitor Lock)。每个对象都有一个监视器,当线程进入同步代码块或方法时,会尝试获取对象的监视器锁。如果监视器锁已经被其他线程持有,则当前线程会被阻塞,直到监视器锁被释放。

Java 虚拟机(JVM)通过以下几种锁优化技术来提高 synchronized 的性能:

偏向锁(Biased Locking):偏向锁是为了优化线程多次获得同一锁的场景,减少不必要的同步操作。 轻量级锁(Lightweight Locking):轻量级锁在锁竞争不激烈时,通过使用CAS操作避免重量级锁的开销。 重量级锁(Heavyweight Locking):当锁竞争激烈时,JVM 会使用重量级锁来保证线程安全。

6.3 显式锁

ReentrantLock的使用和优势 ReentrantLock 是 java.util.concurrent.locks 包中的一个显式锁类,它提供了比 synchronized 更加灵活的同步机制。ReentrantLock 支持可重入、可中断、公平锁等特性。

ReentrantLock示例

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
 
public class ReentrantLockDemo {
    private int count = 0;
    private final Lock lock = new ReentrantLock();
 
    public void increment() {
        lock.lock();
        try {
            count++;
        } finally {
            lock.unlock();
        }
    }
 
    public static void main(String[] args) throws InterruptedException {
        ReentrantLockDemo demo = new ReentrantLockDemo();
 
        Thread thread1 = new Thread(demo::increment);
        Thread thread2 = new Thread(demo::increment);
 
        thread1.start();
        thread2.start();
 
        thread1.join();
        thread2.join();
 
        System.out.println("Final count: " + demo.count);
    }
}

Condition接口和多条件等待的示例 Condition 接口提供了比 Object 类的 wait、notify 和 notifyAll 方法更加强大的线程等待/通知机制。通过使用 Condition,可以实现多条件等待。

Condition示例

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
 
public class ConditionDemo {
    private final Lock lock = new ReentrantLock();
    private final Condition condition = lock.newCondition();
    private boolean ready = false;
 
    public void await() throws InterruptedException {
        lock.lock();
        try {
            while (!ready) {
                condition.await();
            }
            System.out.println("Thread is proceeding...");
        } finally {
            lock.unlock();
        }
    }
 
    public void signal() {
        lock.lock();
        try {
            ready = true;
            condition.signal();
        } finally {
            lock.unlock();
        }
    }
 
    public static void main(String[] args) throws InterruptedException {
        ConditionDemo demo = new ConditionDemo();
 
        Thread thread1 = new Thread(() -> {
            try {
                demo.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
 
        Thread thread2 = new Thread(demo::signal);
 
        thread1.start();
        Thread.sleep(1000); // 确保thread1先启动并等待
        thread2.start();
 
        thread1.join();
        thread2.join();
    }
}

6.4 读写锁

ReadWriteLock的概念和应用 ReadWriteLock 提供了一种分离读写锁的机制,允许多个读线程同时访问共享资源,但在写线程访问共享资源时,所有的读线程和其他写线程都将被阻塞。这种机制可以提高读多写少场景下的并发性能。

ReentrantReadWriteLock的示例代码 ReentrantReadWriteLock 是 ReadWriteLock 接口的一个具体实现类,提供了可重入的读写锁。

ReentrantReadWriteLock示例

import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
 
public class ReadWriteLockDemo {
    private int value = 0;
    private final ReadWriteLock lock = new ReentrantReadWriteLock();
 
    public void read() {
        lock.readLock().lock();
        try {
            System.out.println("Read value: " + value);
        } finally {
            lock.readLock().unlock();
        }
    }
 
    public void write(int newValue) {
        lock.writeLock().lock();
        try {
            value = newValue;
            System.out.println("Written value: " + value);
        } finally {
            lock.writeLock().unlock();
        }
    }
 
    public static void main(String[] args) throws InterruptedException {
        ReadWriteLockDemo demo = new ReadWriteLockDemo();
 
        Thread writer = new Thread(() -> demo.write(42));
        Thread reader1 = new Thread(demo::read);
        Thread reader2 = new Thread(demo::read);
 
        writer.start();
        writer.join(); // 确保写线程先执行
 
        reader1.start();
        reader2.start();
 
        reader1.join();
        reader2.join();
    }
}

在这个示例中,ReadWriteLock 确保了在写操作进行时没有读操作进行,而读操作可以并发执行。

结论

本文详细介绍了Java中的同步机制,包括synchronized关键字、显式锁ReentrantLock、以及读写锁ReentrantReadWriteLock。通过这些同步机制,开发者可以有效地协调多线程对共享资源的访问,确保线程安全。在实际开发中,根据具体需求选择合适的同步机制,可以大大提升并发编程的效率和可靠性。