synchronized的理解及使用

nliu / 2023-08-02 / 原文

synchronized 是 Java 中用于实现线程同步的关键字,它可以应用于方法或代码块上。它的作用是确保在同一时间只有一个线程可以执行被 synchronized 修饰的代码,从而避免多线程并发访问共享资源导致的数据不一致或冲突问题。


理解 synchronized 的关键概念是"互斥访问"和"可见性":

1.互斥访问:当一个线程获得了 synchronized 修饰的方法或代码块的锁,其他线程就无法进入该方法或代码块,必须等待当前线程释放锁才能执行。这确保了同一时间只有一个线程可以执行被 synchronized 修饰的代码,保证了数据的一致性。
2.可见性:synchronized 除了实现互斥访问外,还具有可见性的特性。当一个线程释放锁时(例如退出 synchronized 修饰的方法或代码块),它会将对共享变量的修改刷新到主内存,使得其他线程在获取锁时能够看到最新的值。这样可以避免线程之间读取过期的数据。

Synchronized 可以以不同的方式使用:

1.同步方法:可以使用 synchronized 修饰普通方法或静态方法。修饰普通方法时,默认的锁对象是该方法所属对象实例(即当前对象)。修饰静态方法时,默认的锁对象是该方法所属的 Class 对象。当一个线程访问该方法时,它会尝试获取锁对象,如果锁未被其他线程持有,则此线程获取锁并执行方法体,否则等待锁的释放。
2.同步代码块:可以使用 synchronized 修饰代码块。使用 synchronized 块时,需要指定一个锁对象,可以是任意对象。当一个线程进入 synchronized 块时,它会尝试获取锁对象,如果锁未被其他线程持有,则此线程获取锁并执行代码块,否则等待锁的释放。通常情况下,推荐使用 synchronized 块,因为它可以控制锁的粒度,减小锁的竞争范围,提高程序性能。

 

下面是一个使用 synchronized 的示例代码:

public class SynchronizedExample {
    private int count;

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

    public synchronized void decrement() {
        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.decrement();
            }
        });

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

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

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

在上面的示例中,increment() decrement() 方法都被修饰为 synchronized,确保每次只有一个线程可以执行这两个方法,从而保证了 count 的正确性。
需要注意的是,虽然 synchronized 能够确保线程安全性,但加锁也会引入一定的开销,并有可能导致线程的阻塞。因此,在使用 synchronized 时,需要根据实际情况进行合理的设计和权衡。
除了 synchronized,Java 还提供了其他的线程同步机制,如 LockReentrantLockReadWriteLock 等,它们提供了更多灵活和高级的特性,可以满足不同的并发控制需求。