Skip to content

Java 线程同步

在Java中,Thread类和Runnable接口是实现多线程的主要方式。当多个线程同时访问共享资源时,可能会导致数据不一致或程序崩溃。为了控制线程的执行顺序并确保共享资源的安全访问,我们需要使用线程同步机制。

1. 使用 synchronized 关键字

synchronized 是一个关键字,用于定义方法或者代码块为 synchronized 块。它保证了在同一时间只有一个线程可以执行这个块或方法。

示例:用 synchronized 同步两个线程的输出
java
public class SynchronizationExample {
    public static void main(String[] args) {
        Runnable task = () -> {
            for (int i = 0; i < 10; i++) {
                synchronized (this) { // 使用 this 关键字作为锁对象
                    System.out.println(Thread.currentThread().getName() + " - " + i);
                }
            }
        };

        Thread thread1 = new Thread(task, "Thread-1");
        Thread thread2 = new Thread(task, "Thread-2");

        thread1.start();
        thread2.start();

        // 确保线程完成
        try {
            thread1.join();
            thread2.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

2. 使用 ReentrantLock

ReentrantLock 是一个可重入锁,它允许同一个线程多次获取同一把锁。与synchronized相比,它可以提供更灵活的锁定策略。

示例:用 ReentrantLock 实现同步
java
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class ReentrantLockExample {
    public static void main(String[] args) {
        final Lock lock = new ReentrantLock();
        Runnable task = () -> {
            for (int i = 0; i < 10; i++) {
                lock.lock(); // 获取锁
                try {
                    System.out.println(Thread.currentThread().getName() + " - " + i);
                } finally {
                    lock.unlock(); // 解锁
                }
            }
        };

        Thread thread1 = new Thread(task, "Thread-1");
        Thread thread2 = new Thread(task, "Thread-2");

        thread1.start();
        thread2.start();

        try {
            thread1.join();
            thread2.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

3. 避免死锁

死锁是指两个或多个线程无限期地等待对方释放资源的情况。为了避免死锁,可以采取以下措施:

  • 避免嵌套锁定:尽量不要让一个线程在获取一个锁的同时又去请求另一个锁。
  • 使用超时机制:在获取锁的时候设置超时时间,这样如果无法获得锁,则自动释放。
示例:用 ReentrantLock 设置等待超时
java
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class ReentrantLockWithTimeoutExample {
    public static void main(String[] args) {
        final Lock lock = new ReentrantLock();
        Runnable task = () -> {
            for (int i = 0; i < 10; i++) {
                try {
                    boolean acquired = lock.tryLock(1, TimeUnit.SECONDS);
                    if (!acquired) { // 如果无法获得锁,则继续循环
                        System.out.println(Thread.currentThread().getName() + " - 等待锁超时");
                        continue;
                    }
                    System.out.println(Thread.currentThread().getName() + " - 正在运行:" + i);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    lock.unlock();
                }
            }
        };

        Thread thread1 = new Thread(task, "Thread-1");
        Thread thread2 = new Thread(task, "Thread-2");

        thread1.start();
        thread2.start();

        try {
            thread1.join();
            thread2.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

4. 总结

  • synchronized:关键字,用于同步方法或代码块。
  • ReentrantLock:类,提供可重入锁机制,更灵活的锁定策略。
  • 死锁:避免嵌套锁定和使用超时机制。