java 同步代码块

在Java中,同步代码块是一种使用synchronized关键字来实现的同步机制,用于确保多个线程在访问共享资源时按照特定的顺序执行,防止竞态条件和数据不一致性问题。

同步代码块的基本语法结构如下:

java
synchronized (lockObject) { // 同步的代码块 // 在这里执行对共享资源的操作 }

其中,lockObject通常是一个用于控制访问共享资源的对象。多个线程在执行这段同步代码块时,会依次获取lockObject的锁,只有获取到锁的线程才能执行同步代码块中的代码,其他线程需要等待直到锁被释放。

java
public class SynchronizedExample { private static int sharedCounter = 0; private static Object lockObject = new Object(); public static void main(String[] args) { // 创建两个线程,共享同一个资源 Thread thread1 = new Thread(() -> incrementCounter()); Thread thread2 = new Thread(() -> incrementCounter()); // 启动线程 thread1.start(); thread2.start(); // 等待两个线程执行完毕 try { thread1.join(); thread2.join(); } catch (InterruptedException e) { e.printStackTrace(); } // 打印最终的共享资源值 System.out.println("Final Counter Value: " + sharedCounter); } private static void incrementCounter() { for (int i = 0; i < 5; i++) { // 使用同步代码块,确保对共享资源的操作是线程安全的 synchronized (lockObject) { int currentValue = sharedCounter; System.out.println(Thread.currentThread().getName() + " - Current Value: " + currentValue); sharedCounter = currentValue + 1; } // 模拟其他耗时操作 try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } } } }

在这个例子中,两个线程同时对sharedCounter进行增加操作,但由于使用了同步代码块,确保了对sharedCounter的操作是线程安全的,最终输出的结果不会出现不一致的情况。

锁对象选择: 锁对象的选择很重要,它应该是所有线程共享的对象。在上面的例子中,使用了一个简单的对象 lock 作为锁对象。你也可以使用其他对象,例如 this,但要确保所有线程都在使用相同的锁对象。

锁的粒度: 同步代码块的粒度应该足够小,以减小竞争条件的概率,但同时也要确保保护了整个临界区。在上面的例子中,只有对 sharedVariable 的递增操作被同步,而不是整个方法。

避免死锁: 确保在程序中合理地获取和释放锁,以避免死锁的发生。死锁是指两个或多个线程被阻塞,因为每个线程都在等待另一个线程释放锁。

性能考虑: 过多的同步可能导致性能下降。在设计时,要权衡线程安全和性能,并仅在必要时使用同步。

ReentrantLock: 除了使用关键字 synchronized,Java还提供了 ReentrantLock 类,它是一个可重入锁。可重入锁允许线程在持有锁的情况下再次获取相同的锁,从而避免了死锁的问题。使用 ReentrantLock 时,需要手动进行锁的获取和释放。

java
import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; public class ReentrantLockExample { private int sharedVariable = 0; private final Lock lock = new ReentrantLock(); // 可重入锁 public void incrementSharedVariable() { lock.lock(); // 获取锁 try { sharedVariable++; System.out.println("Thread " + Thread.currentThread().getId() + " incremented sharedVariable to " + sharedVariable); } finally { lock.unlock(); // 释放锁 } } public static void main(String[] args) { ReentrantLockExample example = new ReentrantLockExample(); // 创建多个线程并启动 Thread thread1 = new Thread(() -> { for (int i = 0; i < 5; i++) { example.incrementSharedVariable(); } }); Thread thread2 = new Thread(() -> { for (int i = 0; i < 5; i++) { example.incrementSharedVariable(); } }); thread1.start(); thread2.start(); } }

在使用 ReentrantLock 时,要确保在 lock 后使用 try-finally 块,以确保在发生异常时也能正确释放锁。