synchronized 关键字作为同步锁时有哪些用法?请举例说明。

参考回答

synchronized 关键字是 Java 提供的一种内置同步机制,用于解决多线程环境下的线程安全问题。它可以用来修饰方法或代码块,保证同一时间只有一个线程可以访问被同步的代码。

synchronized 的主要用法包括:

  1. 修饰实例方法:对当前实例对象加锁。
  2. 修饰静态方法:对类对象加锁。
  3. 修饰代码块:对指定对象加锁。

详细讲解与示例

1. 修饰实例方法

作用

  • 同步方法对当前实例对象加锁,多个线程访问同一实例的同步方法时会发生互斥。

示例

public class SynchronizedInstanceMethod {
    public synchronized void syncMethod() {
        System.out.println(Thread.currentThread().getName() + " 开始执行");
        try {
            Thread.sleep(1000); // 模拟耗时操作
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + " 执行结束");
    }

    public static void main(String[] args) {
        SynchronizedInstanceMethod obj = new SynchronizedInstanceMethod();

        Thread t1 = new Thread(() -> obj.syncMethod(), "线程1");
        Thread t2 = new Thread(() -> obj.syncMethod(), "线程2");

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

输出(线程互斥执行):

线程1 开始执行
线程1 执行结束
线程2 开始执行
线程2 执行结束

特点

  • syncMethod() 方法锁住的是当前实例对象 obj,同一时间只有一个线程可以执行方法。

2. 修饰静态方法

作用

  • 同步静态方法对类对象(Class)加锁,无论多少线程访问该类的实例,静态方法都只能被一个线程访问。

示例

public class SynchronizedStaticMethod {
    public static synchronized void syncStaticMethod() {
        System.out.println(Thread.currentThread().getName() + " 开始执行");
        try {
            Thread.sleep(1000); // 模拟耗时操作
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + " 执行结束");
    }

    public static void main(String[] args) {
        Thread t1 = new Thread(() -> SynchronizedStaticMethod.syncStaticMethod(), "线程1");
        Thread t2 = new Thread(() -> SynchronizedStaticMethod.syncStaticMethod(), "线程2");

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

输出(静态方法锁类对象):

线程1 开始执行
线程1 执行结束
线程2 开始执行
线程2 执行结束

特点

  • syncStaticMethod() 方法锁住的是类对象 SynchronizedStaticMethod.class
  • 无论是否使用同一实例调用静态方法,线程都会互斥。

3. 修饰代码块

作用

  • synchronized 代码块对指定的对象加锁,而不是方法或类。灵活性更高,可用于精确控制锁的范围。

示例

public class SynchronizedBlock {
    private final Object lock = new Object();

    public void syncBlock() {
        System.out.println(Thread.currentThread().getName() + " 尝试进入同步块");
        synchronized (lock) {
            System.out.println(Thread.currentThread().getName() + " 开始执行同步块");
            try {
                Thread.sleep(1000); // 模拟耗时操作
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + " 执行结束");
        }
    }

    public static void main(String[] args) {
        SynchronizedBlock obj = new SynchronizedBlock();

        Thread t1 = new Thread(() -> obj.syncBlock(), "线程1");
        Thread t2 = new Thread(() -> obj.syncBlock(), "线程2");

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

输出(代码块锁对象):

线程1 尝试进入同步块
线程1 开始执行同步块
线程1 执行结束
线程2 尝试进入同步块
线程2 开始执行同步块
线程2 执行结束

特点

  • 只对指定的代码块加锁,锁住的对象是 lock,线程必须持有 lock 的锁才能进入同步块。
  • 相比同步方法,锁的粒度更小,性能更高。

注意事项

  1. 对象锁与类锁不同
    • 实例方法加锁(synchronized 修饰实例方法)锁的是当前对象实例。
    • 静态方法加锁(synchronized 修饰静态方法)锁的是类对象。
    • 两者互不影响。

示例:

public class LockDifference {
    public synchronized void instanceMethod() {
        System.out.println(Thread.currentThread().getName() + " 执行实例方法");
    }

    public static synchronized void staticMethod() {
        System.out.println(Thread.currentThread().getName() + " 执行静态方法");
    }

    public static void main(String[] args) {
        LockDifference obj = new LockDifference();

        Thread t1 = new Thread(() -> obj.instanceMethod(), "线程1");
        Thread t2 = new Thread(() -> LockDifference.staticMethod(), "线程2");

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

输出:

  • 实例方法和静态方法分别锁住不同对象,可以并发执行:
线程1 执行实例方法
线程2 执行静态方法
  1. 避免死锁
    • 当多个线程同时请求多个锁时,可能会导致死锁。需要保证加锁顺序一致。

示例(容易死锁):

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

    public void method1() {
        synchronized (lock1) {
            System.out.println(Thread.currentThread().getName() + " 获得锁1,等待锁2");
            synchronized (lock2) {
                System.out.println(Thread.currentThread().getName() + " 获得锁2");
            }
        }
    }

    public void method2() {
        synchronized (lock2) {
            System.out.println(Thread.currentThread().getName() + " 获得锁2,等待锁1");
            synchronized (lock1) {
                System.out.println(Thread.currentThread().getName() + " 获得锁1");
            }
        }
    }

    public static void main(String[] args) {
        DeadlockExample obj = new DeadlockExample();

        Thread t1 = new Thread(() -> obj.method1(), "线程1");
        Thread t2 = new Thread(() -> obj.method2(), "线程2");

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

总结

synchronized 关键字的用法:

  1. 修饰实例方法:锁住当前实例对象。
  2. 修饰静态方法:锁住类对象。
  3. 修饰代码块:锁住指定对象,灵活性更高。

注意事项

  • 理解对象锁与类锁的区别。
  • 注意锁的使用顺序,避免死锁。
  • 选择合适的锁粒度,保证线程安全的同时优化性能。

发表评论

后才能评论