Java中的同步机制概述

在Java多线程编程中,同步机制是一种用于协调多个线程对共享资源访问的机制。当多个线程同时访问共享资源时,可能会出现数据不一致、程序运行结果不符合预期等问题,同步机制的主要目的就是避免这些线程安全问题,确保在同一时刻只有一个线程可以访问共享资源,从而保证数据的一致性和完整性。

实现同步的方式

1. synchronized 关键字

  • 同步方法:当一个方法被 synchronized 修饰时,同一时刻只有一个线程可以调用该方法。如果是实例方法,锁的是当前对象;如果是静态方法,锁的是当前类的 Class 对象。
public class SynchronizedMethodExample {
    private int count = 0;

    public synchronized void increment() {
        count++;
    }
}
  • 同步代码块:可以指定要锁定的对象,更加灵活。只有获取到指定对象的锁的线程才能执行同步代码块中的代码。
public class SynchronizedBlockExample {
    private int count = 0;
    private final Object lock = new Object();

    public void increment() {
        synchronized (lock) {
            count++;
        }
    }
}

2. Lock 接口及其实现类

Lock 是 Java 并发包(java.util.concurrent.locks)中定义的一个接口,常用的实现类有 ReentrantLock

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

public class LockExample {
    private int count = 0;
    private final Lock lock = new ReentrantLock();

    public void increment() {
        lock.lock();
        try {
            count++;
        } finally {
            lock.unlock();
        }
    }
}

3. ReadWriteLock 接口及其实现类

ReadWriteLock 提供了读锁和写锁,允许多个线程同时进行读操作,但写操作是互斥的。常用的实现类是 ReentrantReadWriteLock

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

public class ReadWriteLockExample {
    private int data = 0;
    private final ReadWriteLock rwLock = new ReentrantReadWriteLock();

    public void writeData(int newData) {
        rwLock.writeLock().lock();
        try {
            data = newData;
        } finally {
            rwLock.writeLock().unlock();
        }
    }

    public int readData() {
        rwLock.readLock().lock();
        try {
            return data;
        } finally {
            rwLock.readLock().unlock();
        }
    }
}

区别

1. 语法和使用方式

  • synchronized 关键字:是 Java 语言内置的同步机制,使用起来比较简单,不需要手动释放锁,当同步方法或同步代码块执行完毕后,锁会自动释放。
  • Lock 接口:需要手动加锁和解锁,通过调用 lock() 方法加锁,unlock() 方法解锁,通常需要在 finally 块中调用 unlock() 以确保锁一定会被释放。

2. 锁的获取和释放的灵活性

  • synchronized 关键字:锁的获取和释放是隐式的,无法中断一个正在等待获取锁的线程,也无法尝试获取锁(如果锁不可用,线程只能一直等待)。
  • Lock 接口:提供了更灵活的锁获取方式,例如可以使用 tryLock() 方法尝试获取锁,如果锁不可用,线程可以继续执行其他操作;还可以使用 lockInterruptibly() 方法允许线程在等待锁的过程中被中断。

3. 锁的类型

  • synchronized 关键字:只有一种锁类型,即排他锁,同一时刻只允许一个线程访问共享资源。
  • ReadWriteLock 接口:提供了读锁和写锁,读锁允许多个线程同时进行读操作,提高了并发性能,适用于读多写少的场景。

4. 性能

  • synchronized 关键字:在 JDK 1.6 之前,性能相对较低,但在 JDK 1.6 及以后,对 synchronized 进行了大量的优化,性能有了很大的提升,在大多数情况下与 Lock 接口的性能相当。
  • Lock 接口:在高并发场景下,尤其是需要更灵活的锁控制时,Lock 接口的性能可能会更好。