synchronized 关键字锁定的对象是什么?请解释其含义。

参考回答

在 Java 中,synchronized 关键字是用于实现线程同步的,它通过锁定对象来控制多个线程对共享资源的访问。锁定的对象决定了线程同步的粒度,也决定了哪些线程可以进入临界区。

锁定对象的核心概念

  1. 锁定的对象是同步代码块或方法中指定的对象

  2. 只有持有该对象锁的线程可以执行同步代码,其它线程必须等待,直到锁被释放。

  3. 锁的范围:

  • 静态方法锁住的是类的 Class 对象
  • 非静态方法锁住的是当前实例对象
  • 同步代码块锁住的是指定的对象

synchronized 的使用方式与锁定对象

1. 同步实例方法

synchronized 修饰一个实例方法时,锁定的是当前实例对象 (this)。

示例:

public class SynchronizedExample {
    public synchronized void methodA() {
        System.out.println(Thread.currentThread().getName() + " is executing methodA");
        try {
            Thread.sleep(1000); // 模拟耗时操作
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public synchronized void methodB() {
        System.out.println(Thread.currentThread().getName() + " is executing methodB");
    }
}

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

        Thread t1 = new Thread(() -> example.methodA(), "Thread-1");
        Thread t2 = new Thread(() -> example.methodB(), "Thread-2");

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

解释:

  • methodAmethodB 都是同步实例方法,锁定的是同一个对象 example
  • 两个线程会互相等待,只有一个线程可以进入同步方法。

2. 同步静态方法

synchronized 修饰静态方法时,锁定的是类的 Class 对象

示例:

public class SynchronizedExample {
    public static synchronized void staticMethodA() {
        System.out.println(Thread.currentThread().getName() + " is executing staticMethodA");
        try {
            Thread.sleep(1000); // 模拟耗时操作
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public static synchronized void staticMethodB() {
        System.out.println(Thread.currentThread().getName() + " is executing staticMethodB");
    }
}

public class Main {
    public static void main(String[] args) {
        Thread t1 = new Thread(SynchronizedExample::staticMethodA, "Thread-1");
        Thread t2 = new Thread(SynchronizedExample::staticMethodB, "Thread-2");

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

解释:

  • staticMethodAstaticMethodB 都是静态方法,锁定的是 SynchronizedExample.class
  • 无论多少实例对象,这些方法都会互相排斥。

3. 同步代码块

使用 synchronized 关键字锁定指定对象,灵活控制锁的范围。

示例:

public class SynchronizedExample {
    private final Object lock1 = new Object();
    private final Object lock2 = new Object();

    public void methodA() {
        synchronized (lock1) {
            System.out.println(Thread.currentThread().getName() + " is executing methodA");
            try {
                Thread.sleep(1000); // 模拟耗时操作
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public void methodB() {
        synchronized (lock2) {
            System.out.println(Thread.currentThread().getName() + " is executing methodB");
        }
    }
}

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

        Thread t1 = new Thread(() -> example.methodA(), "Thread-1");
        Thread t2 = new Thread(() -> example.methodB(), "Thread-2");

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

解释:

  • methodA 锁定 lock1methodB 锁定 lock2
  • 两个方法锁定不同对象,因此线程 t1t2 可以并发执行。

锁定对象的意义

1. 锁定对象决定同步的粒度

  • 锁的粒度越小,并发性能越高,但代码逻辑可能更复杂。
  • 锁的粒度越大,并发性能会降低,但代码更简单。

2. 锁定对象的唯一性

  • 如果两个线程锁定的是同一个对象,则会互斥。
  • 如果两个线程锁定的是不同对象,则可以并发执行。

3. 锁的类型

  • 实例锁:锁住当前实例,影响该对象的同步方法。
  • 类锁:锁住类的 Class 对象,影响所有同步静态方法。

注意事项与建议

  1. 避免锁粒度过大
    • 如果锁定的范围太大,会严重降低并发性能。
    • 应尽量缩小锁的范围,例如使用同步代码块锁定关键部分。
  2. 保证锁对象的唯一性
    • 锁对象应该是所有线程共享的,通常使用类的字段或 this
    • 不要使用局部变量作为锁对象,因为局部变量在线程间不可共享。
  3. 慎用类锁
    • 类锁会影响类的所有线程,适合需要全局同步的操作。
    • 如果只需要实例级别的同步,应该使用实例锁。
  4. 防止死锁
    • 确保锁的获取顺序一致,避免循环等待。

总结

  • synchronized锁定的对象由同步代码的修饰对象决定:
    • 实例方法:锁定当前实例(this)。
    • 静态方法:锁定类的 Class 对象。
    • 同步代码块:锁定代码块中指定的对象。
  • 使用时需根据具体场景选择合适的锁定对象和范围,确保线程安全的同时提高并发性能。

发表评论

后才能评论