java 同步代码块
在Java中,同步代码块是一种使用synchronized关键字来实现的同步机制,用于确保多个线程在访问共享资源时按照特定的顺序执行,防止竞态条件和数据不一致性问题。
同步代码块的基本语法结构如下:
javasynchronized (lockObject) {
// 同步的代码块
// 在这里执行对共享资源的操作
}
其中,lockObject通常是一个用于控制访问共享资源的对象。多个线程在执行这段同步代码块时,会依次获取lockObject的锁,只有获取到锁的线程才能执行同步代码块中的代码,其他线程需要等待直到锁被释放。
javapublic 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 时,需要手动进行锁的获取和释放。
javaimport 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 块,以确保在发生异常时也能正确释放锁。