Skip to content

05_关键字_synchronized.md

synchronized 是 Java 中的一个关键字,用于实现线程同步。它可以用来修饰方法或代码块,以确保在同一时间只有一个线程能够执行被 synchronized 修饰的方法或代码块。这样可以避免多个线程同时访问共享资源时产生的并发问题。

作用

  • 互斥锁: synchronized 确保在同一时刻只有一个线程可以执行被其修饰的代码,从而实现互斥访问。
  • 内存可见性: synchronized 确保在一个线程释放锁之前,写入到共享变量的变化对另一个获得相同锁的线程可见。

原理

  • synchronized 的实现依赖于 Java 对象头中的锁标志位和 Monitor(监视器)机制。每个对象在 JVM 中都有一个与之关联的 Monitor。当一个线程进入一个 synchronized 方法或代码块时,它必须先获得与该对象关联的 Monitor。如果 Monitor 已被其他线程占用,那么当前线程将被阻塞,直到 Monitor 被释放。

使用案例

  1. 修饰实例方法
    • synchronized 修饰实例方法,确保同一对象的实例方法在同一时刻只能由一个线程执行。
java
public class SynchronizedExample {
    private int count = 0;

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

    public int getCount() {
        return count;
    }

    public static void main(String[] args) {
        SynchronizedExample example = new SynchronizedExample();

        Thread t1 = new Thread(() -> {
            for (int i = 0; i < 1000; i++) {
                example.increment();
            }
        });

        Thread t2 = new Thread(() -> {
            for (int i = 0; i < 1000; i++) {
                example.increment();
            }
        });

        t1.start();
        t2.start();

        try {
            t1.join();
            t2.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("Final count: " + example.getCount());
    }
}

在这个例子中,increment 方法被 synchronized 修饰,确保 count 变量的更新是线程安全的。

  1. 修饰静态方法 synchronized 修饰静态方法,确保同一类的静态方法在同一时刻只能由一个线程执行。
java
public class SynchronizedStaticExample {
    private static int count = 0;

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

    public static int getCount() {
        return count;
    }

    public static void main(String[] args) {
        Thread t1 = new Thread(() -> {
            for (int i = 0; i < 1000; i++) {
                SynchronizedStaticExample.increment();
            }
        });

        Thread t2 = new Thread(() -> {
            for (int i = 0; i < 1000; i++) {
                SynchronizedStaticExample.increment();
            }
        });

        t1.start();
        t2.start();

        try {
            t1.join();
            t2.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("Final count: " + SynchronizedStaticExample.getCount());
    }
}

在这个例子中,increment 静态方法被 synchronized 修饰,确保 count 变量的更新是线程安全的。 3. 修饰代码块

  • synchronized 修饰代码块,可以指定具体的对象作为锁,提供更细粒度的控制。
java
public class SynchronizedBlockExample {
    private final Object lock = new Object();
    private int count = 0;

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

    public int getCount() {
        return count;
    }

    public static void main(String[] args) {
        SynchronizedBlockExample example = new SynchronizedBlockExample();

        Thread t1 = new Thread(() -> {
            for (int i = 0; i < 1000; i++) {
                example.increment();
            }
        });

        Thread t2 = new Thread(() -> {
            for (int i = 0; i < 1000; i++) {
                example.increment();
            }
        });

        t1.start();
        t2.start();

        try {
            t1.join();
            t2.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("Final count: " + example.getCount());
    }
}

在这个例子中,increment 方法使用了 synchronized 代码块,并指定了 lock 对象作为锁,确保 count 变量的更新是线程安全的。

总结

  • 锁的范围:
    • synchronized修饰在方法上,锁的是当前对象
    • static synchronized修饰在方法上,锁的是当前类的Class对象
    • synchronized修饰在代码块上,锁的是括号里面的对象
  • synchronized 关键字在 Java 并发编程中用于确保线程安全,主要体现在以下几个方面:
    • 互斥锁: 确保同一时刻只有一个线程能够执行被 synchronized 修饰的代码。
    • 内存可见性: 确保一个线程在释放锁之前,对共享变量的修改对其他线程可见。