Java中的synchronized关键字详解

Java中的synchronized关键字详解

1. 引言

在Java编程中,多线程是提高应用性能的重要手段之一。然而,多线程环境下共享资源的访问控制成为必须面对的问题。synchronized关键字作为Java语言提供的一种同步机制,能够有效地解决这一问题。本文将深入探讨synchronized的用法和最佳实践。

2. Java并发基础

Java并发编程是现代软件开发中不可或缺的一部分,特别是在构建高性能和高可用的应用程序时。为了深入理解synchronized关键字,我们需要首先了解Java并发的基础知识。

2.1 线程的基本概念

线程是程序执行的最小单元,Java中的线程由Thread类或实现Runnable接口的类创建。线程可以并发执行,共享同一个进程的资源。

2.2 线程的生命周期

Java线程有多种状态,包括新建、就绪、运行、阻塞和死亡。理解这些状态对于编写正确的并发程序至关重要。

2.3 线程同步

在多线程环境中,多个线程可能会访问共享数据。如果这些访问不是线程安全的,就可能产生不可预测的结果。线程同步是确保多个线程在访问共享资源时能够正确协调的一种机制。

2.4 线程安全

线程安全是指在多线程环境中,代码能够正确地处理并发访问,保证数据的一致性和完整性。

2.5 并发工具类

Java提供了多种并发工具类,如ExecutorServiceCountDownLatchCyclicBarrierSemaphoreConcurrentHashMap等,这些工具类帮助开发者更容易地编写并发程序。

2.6 示例:线程创建和执行

下面是一个简单的示例,展示如何在Java中创建和启动线程:

public class ThreadExample {public static void main(String[] args) {Thread thread = new Thread(() -> {System.out.println("线程启动");});thread.start(); // 启动线程}
}

2.7 示例:线程同步

以下示例展示了两个线程如何同步访问共享资源:

public class Counter {private int count = 0;public synchronized void increment() {count++;}public int getCount() {return count;}public static void main(String[] args) {Counter counter = new Counter();Thread thread1 = new Thread(() -> {for (int i = 0; i < 1000; i++) {counter.increment();}});Thread thread2 = new Thread(() -> {for (int i = 0; i < 1000; i++) {counter.increment();}});thread1.start();thread2.start();try {thread1.join();thread2.join();} catch (InterruptedException e) {e.printStackTrace();}System.out.println("最终计数: " + counter.getCount()); // 应该输出2000}
}

在这个示例中,Counter类有一个increment方法,它通过synchronized关键字确保线程安全。两个线程分别对计数器进行1000次递增操作,最终的计数结果应该是2000。

2.8 线程通信

线程通信是并发编程中的另一个重要概念。Java提供了多种线程间通信的方式,例如使用waitnotifynotifyAll方法。

2.9 示例:线程间通信

以下示例展示了两个线程如何通过waitnotify进行通信:

public class CommunicationExample {private boolean ready = false;public synchronized void waitForReady() throws InterruptedException {while (!ready) {wait();}}public synchronized void setReady() {ready = true;notifyAll();}public static void main(String[] args) throws InterruptedException {CommunicationExample example = new CommunicationExample();Thread thread1 = new Thread(() -> {try {example.waitForReady();System.out.println("线程1: 准备就绪");} catch (InterruptedException e) {e.printStackTrace();}});Thread thread2 = new Thread(() -> {example.setReady();System.out.println("线程2: 设置准备状态");});thread1.start();Thread.sleep(1000); // 等待thread1准备就绪thread2.start();}
}

在这个示例中,thread1首先调用waitForReady方法,它将等待ready变量变为truethread2稍后启动,并调用setReady方法来设置ready变量并唤醒等待的线程。

3. synchronized关键字详解

synchronized关键字是Java并发编程中的核心概念之一,它用于控制对共享资源的访问,以确保线程安全。本节将深入探讨synchronized的用法、原理以及示例。

3.1 语法介绍

synchronized可以用于修饰方法或者代码块,确保同一时间只有一个线程可以执行该段代码。

3.1.1 修饰实例方法

synchronized用于实例方法时,锁是当前实例对象(this)。

public class SynchronizedMethodExample {private int count = 0;public synchronized void increment() {count++;}public int getCount() {return count;}
}
3.1.2 修饰静态方法

synchronized用于静态方法时,锁是当前类的Class对象。

public class SynchronizedStaticMethodExample {private static int count = 0;public static synchronized void increment() {count++;}public static int getCount() {return count;}
}

3.2 作用域

synchronized的作用域可以是整个方法或者方法内部的特定代码块。

3.2.1 同步整个方法

整个方法被同步,适用于方法体内所有代码都需要同步的情况。

public synchronized void someMethod() {// 整个方法体都是同步的
}
3.2.2 同步代码块

只有部分代码需要同步,可以在这部分代码前后加上同步块。

public void someMethod() {synchronized(this) {// 只有这个代码块是同步的}
}

3.3 锁的概念

synchronized关键字背后的核心是锁的概念。锁可以是对象锁或者类锁。

3.3.1 对象锁

每个Java对象都有一个内置的锁,称为对象锁。当一个线程访问一个对象的同步实例方法时,它会自动获取该对象的对象锁。

public class ObjectLockExample {private int value;public synchronized void setValue(int value) {this.value = value;}public synchronized int getValue() {return this.value;}
}
3.3.2 类锁

类锁与类的Class对象相关联,用于控制对静态成员的访问。

public class ClassLockExample {private static int value;public static synchronized void setValue(int value) {ClassLockExample.value = value;}public static synchronized int getValue() {return ClassLockExample.value;}
}

3.4 使用synchronized的示例

以下是一些使用synchronized的示例,展示了如何在实际编程中应用同步机制。

3.4.1 同步访问共享资源
public class Counter {private int count = 0;public synchronized void increment() {count++;}public synchronized int getCount() {return count;}
}
3.4.2 同步方法与代码块的比较
public class ComparisonExample {private int count = 0;public void incrementMethod() {synchronized(this) {count++;}}public synchronized void incrementBlock() {count++;}
}

3.5 synchronized的局限性

尽管synchronized非常有用,但它也有一些局限性,比如可能导致死锁和性能问题。

3.5.1 死锁示例
public class DeadlockExample {private static final Object lock1 = new Object();private static final Object lock2 = new Object();public void method1() {synchronized(lock1) {System.out.println("Lock 1 acquired");synchronized(lock2) {System.out.println("Lock 2 acquired");}}}public void method2() {synchronized(lock2) {System.out.println("Lock 2 acquired");synchronized(lock1) {System.out.println("Lock 1 acquired");}}}
}

在这个示例中,如果method1method2同时运行,它们会尝试以不同的顺序获取两个锁,从而导致死锁。

3.6 高级主题

深入理解synchronized的内部机制,包括锁的升级过程和优化策略。

3.6.1 锁的升级

Java虚拟机(JVM)内部对锁有多种实现,包括偏向锁、轻量级锁、重量级锁等。了解这些锁的升级过程有助于优化性能。

3.7 与synchronized相关的其他并发工具

Java的并发API提供了许多其他工具,如ReentrantLockSemaphore等,它们提供了比synchronized更灵活的同步机制。

3.7.1 使用ReentrantLock
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;public class ReentrantLockExample {private final Lock lock = new ReentrantLock();private int count = 0;public void increment() {lock.lock();try {count++;} finally {lock.unlock();}}public int getCount() {return count;}
}

4. 使用synchronized的示例

synchronized关键字在Java中用于实现线程同步,确保共享资源在同一时间只能被一个线程访问。本节将通过多个示例,展示synchronized在实际编程中的应用。

4.1 同步实例方法

当一个实例方法被synchronized修饰时,它锁定了实例对象,确保同一时间只有一个线程可以执行该实例的所有同步实例方法。

示例:同步计数器
public class SynchronizedCounter {private int count = 0;// 同步实例方法public synchronized void increment() {count++;}public synchronized int getCount() {return count;}
}

在这个示例中,increment方法通过synchronized确保了线程安全,即使多个线程同时访问也不会导致数据不一致。

4.2 同步静态方法

当一个静态方法被synchronized修饰时,它锁定了整个类的Class对象,确保同一时间只有一个线程可以执行该类的所有同步静态方法。

示例:同步访问类属性
public class SynchronizedResource {private static int sharedCount = 0;// 同步静态方法public static synchronized void incrementSharedCount() {sharedCount++;}public static int getSharedCount() {return sharedCount;}
}

在这个示例中,incrementSharedCount方法通过synchronized确保了对sharedCount变量的同步访问。

4.3 同步代码块

在某些情况下,我们只需要同步方法的一部分代码,而不是整个方法。这时,可以使用同步代码块。

示例:同步特定代码段
public class SynchronizedBlock {private int count = 0;public void increment() {synchronized(this) {count++;}}public int getCount() {return count;}
}

在这个示例中,只有increment方法中的特定代码段被同步,而不是整个方法。

4.4 同步集合访问

在多线程环境中,直接访问集合类(如ListMap等)可能会导致不一致的问题。通过同步代码块,可以确保集合的线程安全。

示例:同步访问集合
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;public class SynchronizedCollection {private Map<String, Integer> map = Collections.synchronizedMap(new HashMap<>());public void incrementValue(String key) {synchronized(map) {Integer value = map.get(key);if (value == null) {map.put(key, 1);} else {map.put(key, value + 1);}}}public int getValue(String key) {return map.get(key);}
}

在这个示例中,通过在incrementValue方法中使用同步代码块,确保了对map集合的线程安全访问。

4.5 避免死锁

在使用synchronized时,如果不当心,可能会引起死锁。

示例:避免死锁
public class NoDeadlockExample {private final Object lock1 = new Object();private final Object lock2 = new Object();public void method1() {synchronized(lock1) {// 执行一些操作synchronized(lock2) {// 执行一些操作}}}public void method2() {synchronized(lock2) {// 执行一些操作synchronized(lock1) {// 执行一些操作}}}
}

在这个示例中,method1method2以相同的顺序获取lock1lock2,从而避免了死锁。

4.6 性能考虑

虽然synchronized提供了线程安全,但它也可能成为性能瓶颈。在某些情况下,可以考虑使用其他并发工具来提高性能。

示例:使用ReentrantLock代替synchronized
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;public class ReentrantLockExample {private final Lock lock = new ReentrantLock();private int count = 0;public void increment() {lock.lock();try {count++;} finally {lock.unlock();}}public int getCount() {return count;}
}

在这个示例中,使用ReentrantLock代替了synchronized,提供了更细粒度的锁控制,有助于提高性能。

5. synchronized的局限性

尽管synchronized关键字为Java并发编程提供了一种简单有效的同步机制,但它也存在一些局限性和潜在的问题。本节将详细讨论这些问题,并提供一些示例来说明如何在实践中避免这些问题。

5.1 性能问题

synchronized可以导致性能瓶颈,因为它在竞争激烈的情况下可能会导致线程阻塞和上下文切换。

示例:性能瓶颈
public class PerformanceIssueExample {private int count = 0;public synchronized void increment() {count++; // 模拟一些计算}
}

在高并发场景下,所有线程都会竞争同一个锁,这可能导致性能下降。

5.2 死锁

使用synchronized时,如果不当心,可能会引起死锁,即两个或多个线程互相等待对方释放锁。

示例:死锁
public class DeadlockExample {private final Object resource1 = new Object();private final Object resource2 = new Object();public void method1() {synchronized (resource1) {try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}synchronized (resource2) {// 执行操作}}}public void method2() {synchronized (resource2) {synchronized (resource1) {// 执行操作}}}
}

如果method1method2同时运行,它们以不同的顺序获取资源锁,这将导致死锁。

5.3 可扩展性问题

synchronized通常不适用于高并发的场景,因为它不支持多个线程并发访问共享资源。

示例:可扩展性问题
public class ScalabilityIssueExample {private List<Integer> list = new ArrayList<>();public synchronized void add(Integer item) {list.add(item);}public synchronized boolean contains(Integer item) {return list.contains(item);}
}

在这个例子中,addcontains方法都是同步的,这意味着即使它们可以并行执行,它们也会被串行化。

5.4 锁粗化和锁细化

锁粗化和锁细化是JVM为了优化性能而进行的锁操作,但在某些情况下,这可能导致问题。

示例:锁粗化问题
public class LockCoarseningExample {private int sharedValue = 0;public void incrementA() {synchronized (this) {sharedValue++;}}public void incrementB() {synchronized (this) {sharedValue++;}}
}

JVM可能会将两个方法中的锁合并为一个,这在某些情况下可能不是我们想要的行为。

5.5 锁的可见性

synchronized确保了内存的可见性,但如果没有正确使用,仍然可能导致可见性问题。

示例:可见性问题
public class VisibilityExample {private int sharedValue;public synchronized void setValue(int value) {sharedValue = value;}public int getValue() {return sharedValue;}
}

如果setValuegetValue方法没有被正确同步,其他线程可能看不到最新的sharedValue值。

5.6 替代方案

由于synchronized的局限性,Java提供了其他并发工具作为替代,如ReentrantLockSemaphoreCountDownLatch等。

示例:使用ReentrantLock
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;public class ReentrantLockExample {private final Lock lock = new ReentrantLock();private int count = 0;public void increment() {lock.lock();try {count++;} finally {lock.unlock();}}public int getCount() {return count;}
}

在这个示例中,ReentrantLock提供了比synchronized更灵活的锁操作,例如尝试非阻塞获取锁。

通过这些示例和讨论,我们可以看到synchronized虽然强大,但在某些情况下可能会引起问题。理解这些局限性并知道何时以及如何使用替代方案是编写高效、可扩展和线程安全代码的关键。

6. 高级主题

在深入理解了synchronized的基本用法之后,我们可以探索一些更高级的主题,这些主题将帮助我们更有效地使用synchronized,同时也会介绍一些Java并发API中的高级特性。

6.1 锁的升级过程

Java虚拟机(JVM)内部对锁有多种实现,随着锁的竞争情况,锁的状态会从偏向锁升级到轻量级锁、重量级锁。

示例:锁的升级过程
public class LockUpgradeExample {private int count = 0;public void increment() {synchronized (this) {count++;}}
}

在这个示例中,随着多个线程对increment方法的访问,JVM可能会自动将锁从偏向锁升级到轻量级锁,再到重量级锁。

6.2 锁消除

锁消除是JVM中的一个优化,它会在编译时检查是否可以安全地去除不必要的锁。

示例:锁消除
public class LockEliminationExample {private int value;public void setValue(int value) {// 编译器可以确定这里没有共享资源竞争,可能会消除这个锁synchronized (this) {this.value = value;}}
}

在这个示例中,如果setValue方法被确定为没有共享资源竞争,JVM可能会执行锁消除。

6.3 锁粗化

锁粗化是将多个连续的锁操作合并为一个锁操作的过程。

示例:锁粗化
public class LockCoarseningExample {private int value;public void updateValue() {synchronized (this) {value++;}synchronized (this) {value++;}// JVM可能会将上面的两个锁操作合并为一个}
}

在这个示例中,JVM可能会识别出连续的锁操作可以合并,从而减少锁的开销。

6.4 自旋锁

自旋锁是一种锁机制,当预计线程会在很短时间内获得锁时,线程不会立即阻塞,而是在当前位置“自旋”,直到获得锁。

示例:自旋锁
public class SpinLockExample {private volatile int lock = 0;public void spinLockMethod() {while (true) {int expected = 0;if (lock == expected) {if (lock == 0 && (lock == (expected = 1))) {break;}}// 自旋等待}// 临界区try {// 执行操作} finally {lock = 0;}}
}

在这个示例中,spinLockMethod展示了如何实现一个简单的自旋锁。

6.5 锁分段

锁分段是一种技术,通过将数据结构分成多个段,并对每个段使用不同的锁,从而提高并发性。

示例:锁分段
public class SegmentedLockExample {private final int SEGMENTS = 100;private final ReentrantLock[] locks = new ReentrantLock[SEGMENTS];public SegmentedLockExample() {for (int i = 0; i < SEGMENTS; i++) {locks[i] = new ReentrantLock();}}public void access(int index) {locks[index].lock();try {// 执行操作} finally {locks[index].unlock();}}
}

在这个示例中,我们创建了一个锁数组,每个索引对应一个锁,这样可以减少锁的竞争。

6.6 条件变量

条件变量用于线程间的协调,允许一个线程等待某些条件为真,而另一个线程在条件为真时唤醒等待的线程。

示例:条件变量
public class ConditionVariableExample {private int resource = 0;private final Object lock = new Object();public void waitForResource() {synchronized (lock) {while (resource == 0) {try {lock.wait();} catch (InterruptedException e) {e.printStackTrace();}}}}public void produceResource() {synchronized (lock) {resource++;lock.notifyAll();}}
}

在这个示例中,waitForResource方法使用条件变量等待资源变为非零值,而produceResource方法在资源准备好时通知等待的线程。

7. 与synchronized相关的其他并发工具

Java并发API提供了多种工具来帮助开发者编写线程安全的代码。这些工具与synchronized相比,提供了更多的灵活性和控制能力。本节将介绍一些常用的并发工具,并展示如何使用它们来替代或与synchronized结合使用。

7.1 ReentrantLock

ReentrantLock是一个可重入的互斥锁,与synchronized相比,它提供了更多的灵活性。

示例:使用ReentrantLock
import java.util.concurrent.locks.ReentrantLock;public class ReentrantLockExample {private final ReentrantLock lock = new ReentrantLock();private int count = 0;public void increment() {lock.lock();try {count++;} finally {lock.unlock();}}public int getCount() {return count;}
}

在这个示例中,我们使用ReentrantLock来控制对共享资源count的访问。

7.2 ReadWriteLock

ReadWriteLock允许多个读操作同时进行,但写操作是排他的。

示例:使用ReadWriteLock
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;public class ReadWriteLockExample {private int data;private final ReadWriteLock lock = new ReentrantReadWriteLock();public void updateData(int newData) {lock.writeLock().lock();try {data = newData;} finally {lock.writeLock().unlock();}}public int getData() {lock.readLock().lock();try {return data;} finally {lock.readLock().unlock();}}
}

在这个示例中,updateData方法需要写锁,而getData方法只需要读锁。

7.3 Semaphore

Semaphore是一个计数信号量,可以用来控制同时访问某个特定资源的线程数量。

示例:使用Semaphore
import java.util.concurrent.Semaphore;public class SemaphoreExample {private final Semaphore semaphore = new Semaphore(3);public void accessResource() {semaphore.acquireUninterruptibly();try {// 访问资源} finally {semaphore.release();}}
}

在这个示例中,我们使用Semaphore来限制同时访问资源的线程数量。

7.4 CountDownLatch

CountDownLatch是一个同步辅助工具,允许一个或多个线程等待一组操作在其他线程中完成。

示例:使用CountDownLatch
import java.util.concurrent.CountDownLatch;public class CountDownLatchExample {private final CountDownLatch latch = new CountDownLatch(1);public void completeInOneSecond() {new Thread(() -> {try {Thread.sleep(1000);} catch (InterruptedException e) {Thread.currentThread().interrupt();}latch.countDown();}).start();}public void waitForCompletion() {latch.await();// 继续执行,因为latch已经计数到0}
}

在这个示例中,waitForCompletion方法会等待completeInOneSecond方法完成。

7.5 CyclicBarrier

CyclicBarrier是一个同步辅助工具,它允许一组线程相互等待,直到所有线程都到达一个公共屏障点。

示例:使用CyclicBarrier
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.BrokenBarrierException;public class CyclicBarrierExample {private final CyclicBarrier barrier = new CyclicBarrier(2);public void phase1() {try {// 第一阶段的操作barrier.await();// 第二阶段的操作} catch (InterruptedException | BrokenBarrierException e) {e.printStackTrace();}}public void phase2() {try {// 第一阶段的操作barrier.await();// 第二阶段的操作} catch (InterruptedException | BrokenBarrierException e) {e.printStackTrace();}}
}

在这个示例中,phase1phase2方法都需要到达屏障点才能继续执行第二阶段的操作。

7.6 Phaser

PhaserCyclicBarrierCountDownLatch的结合体,它提供了更灵活的线程同步机制。

示例:使用Phaser
import java.util.concurrent.Phaser;public class PhaserExample {private final Phaser phaser = new Phaser(2);public void arriveAndAwaitAdvance() {phaser.arriveAndAwaitAdvance();// 继续执行,因为phaser已经前进到下一个阶段}public void onAdvance() {phaser.onAdvance(1);}
}

在这个示例中,arriveAndAwaitAdvance方法会等待其他线程到达当前阶段,然后onAdvance方法会触发进入下一个阶段。

7.7 ConcurrentHashMap

ConcurrentHashMap是一个线程安全的哈希表,它提供了更好的并发性能。

示例:使用ConcurrentHashMap
import java.util.concurrent.ConcurrentHashMap;public class ConcurrentHashMapExample {private final ConcurrentHashMap<Key, Value> map = new ConcurrentHashMap<>();public Value get(Object key) {return map.get(key);}public Value put(Key key, Value value) {return map.put(key, value);}
}

在这个示例中,ConcurrentHashMap提供了线程安全的getput操作。

8. 最佳实践

在使用synchronized关键字时,遵循最佳实践是非常重要的。这不仅可以帮助我们避免常见的陷阱,还可以提高代码的性能和可维护性。以下是一些使用synchronized时的最佳实践,以及相关的示例。

8.1 最小化同步块

尽量缩小同步块的范围,只对需要同步的代码进行同步,以减少锁的争用。

示例:最小化同步块
public class MinimizeSyncBlock {private int count = 0;private final Object lock = new Object();public void increment() {synchronized (lock) {count++; // 只有这一行需要同步}}
}

在这个示例中,我们只同步了递增操作,而不是整个方法。

8.2 避免在同步块中执行长时间操作

在同步块中执行长时间操作可能会导致其他线程长时间等待,从而影响性能。

示例:避免长时间操作
public class AvoidLongOperationsInSyncBlock {private int count = 0;private final Object lock = new Object();public void increment() {synchronized (lock) {count++;}// 执行一些计算密集型操作,但不在同步块中performComputation();}private void performComputation() {// 执行计算}
}

在这个示例中,我们避免了在同步块中执行计算密集型操作。

8.3 使用更细粒度的锁

如果可能,使用更细粒度的锁来代替粗粒度的锁,以减少锁的争用。

示例:使用细粒度锁
public class FineGrainedLocks {private final Map<String, Object> resources = new HashMap<>();private final Map<String, Object> locks = new HashMap<>();public void operateResource(String key) {Object lock = locks.computeIfAbsent(key, k -> new Object());synchronized (lock) {// 操作资源}}
}

在这个示例中,我们为每个资源分配了一个独立的锁,而不是使用一个全局锁。

8.4 考虑使用并发集合

对于集合操作,考虑使用Java并发API提供的并发集合,如ConcurrentHashMap

示例:使用并发集合
import java.util.concurrent.ConcurrentHashMap;public class ConcurrentCollection {private final ConcurrentHashMap<Key, Value> map = new ConcurrentHashMap<>();public void put(Key key, Value value) {map.put(key, value); // 自动线程安全}public Value get(Key key) {return map.get(key); // 自动线程安全}
}

在这个示例中,我们使用了ConcurrentHashMap来避免手动同步集合操作。

8.5 使用volatile关键字

对于需要保证可见性的场景,考虑使用volatile关键字,而不是synchronized

示例:使用volatile关键字
public class VolatileExample {private volatile int flag = 0;public void setFlag() {flag = 1; // 保证可见性}public void checkFlag() {if (flag == 1) {// 执行操作}}
}

在这个示例中,volatile关键字确保了flag变量的修改对所有线程立即可见。

8.6 避免死锁

在使用多个锁时,总是以相同的顺序获取锁,以避免死锁。

示例:避免死锁
public class AvoidDeadlock {private final Object lock1 = new Object();private final Object lock2 = new Object();public void avoidDeadlock() {synchronized (lock1) {synchronized (lock2) {// 操作资源}}}
}

在这个示例中,我们总是先获取lock1,然后获取lock2,以避免死锁。

8.7 使用Lock接口

考虑使用java.util.concurrent.locks.Lock接口,它提供了比synchronized更丰富的锁操作。

示例:使用Lock接口
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;public class LockInterfaceExample {private final Lock lock = new ReentrantLock();public void performAction() {lock.lock();try {// 执行操作} finally {lock.unlock();}}
}

在这个示例中,我们使用了ReentrantLock来提供更灵活的锁控制。

8.8 考虑使用java.util.concurrent

java.util.concurrent包提供了许多并发工具,如ExecutorServiceFutureCallable等,它们可以帮助我们编写更高效的并发代码。

示例:使用ExecutorService
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;public class ExecutorServiceExample {private final ExecutorService executor = Executors.newFixedThreadPool(10);public void performTask(Runnable task) {executor.submit(task); // 提交任务到线程池}
}

在这个示例中,我们使用了ExecutorService来管理线程池和任务执行。

9. 案例研究

在本节中,我们将通过一系列案例研究来展示synchronized的实际应用。这些案例将涵盖不同的场景,包括常见的问题和解决方案,以及如何使用synchronized来提高程序的线程安全性。

9.1 多线程累加器

问题描述

在多线程环境中,多个线程需要对一个共享计数器进行递增操作,但直接操作会导致竞争条件。

使用synchronized的解决方案
public class ThreadSafeCounter {private int count = 0;// synchronized方法确保原子性public synchronized void increment() {count++;}public synchronized int getCount() {return count;}
}

在这个案例中,通过将incrementgetCount方法声明为synchronized,我们确保了对count变量的访问是线程安全的。

9.2 共享资源的线程安全访问

问题描述

多个线程需要访问和修改共享资源,如数据库连接池中的连接。

使用synchronized的解决方案
public class ConnectionPool {private final List<Connection> connections = new ArrayList<>();private final Object lock = new Object();public Connection getConnection() {synchronized (lock) {if (!connections.isEmpty()) {return connections.remove(connections.size() - 1);}return null;}}public void returnConnection(Connection connection) {synchronized (lock) {connections.add(connection);}}
}

在这个案例中,我们使用一个锁对象lock来同步对连接池的操作,确保了线程安全。

9.3 多线程环境下的资源缓存

问题描述

在高并发环境下,需要缓存一些昂贵的资源,如数据库查询结果。

使用synchronized的解决方案
public class ResourceCache {private final Map<Key, Resource> cache = Collections.synchronizedMap(new HashMap<>());public Resource getResource(Key key) {Resource resource = cache.get(key);if (resource == null) {synchronized (this) {resource = cache.get(key); // 再次检查,防止在等待锁时资源被创建if (resource == null) {resource = createExpensiveResource(key);cache.put(key, resource);}}}return resource;}private Resource createExpensiveResource(Key key) {// 创建资源的逻辑return new Resource();}
}

在这个案例中,我们使用Collections.synchronizedMap来创建线程安全的缓存,并在创建资源时使用synchronized块来避免重复创建。

9.4 多线程的日志记录

问题描述

在多线程应用程序中,需要记录日志,但直接记录可能会导致日志消息交错。

使用synchronized的解决方案
public class ThreadSafeLogger {private final List<String> log = Collections.synchronizedList(new ArrayList<>());public void logMessage(String message) {synchronized (log) {log.add(message);}}public void displayLog() {synchronized (log) {for (String message : log) {System.out.println(message);}}}
}

在这个案例中,我们对日志列表log进行同步,以确保添加和显示日志消息的线程安全。

9.5 多线程环境下的UI更新

问题描述

在图形用户界面(GUI)应用程序中,多个线程可能需要更新UI组件。

使用synchronized的解决方案
public class ThreadSafeUIUpdater {private final JLabel label;public ThreadSafeUIUpdater(JLabel label) {this.label = label;}public void updateLabel(String text) {synchronized (label) {label.setText(text);}}
}

在这个案例中,我们使用synchronized块来确保UI组件label的更新是线程安全的。

9.6 多线程的数据处理

问题描述

在数据处理应用程序中,多个线程需要读取、处理和写入数据。

使用synchronized的解决方案
public class DataProcessor {private final List<Data> data = Collections.synchronizedList(new ArrayList<>());public void processData(Data data) {synchronized (data) {process(data);data.add(data);}}private void process(Data data) {// 处理数据的逻辑}
}

在这个案例中,我们对数据列表data进行同步,以确保数据的读取、处理和写入是线程安全的。

通过这些案例研究,我们可以看到synchronized在多线程编程中的广泛应用,以及如何根据不同场景采取合适的同步策略来确保线程安全。这些示例和解决方案为处理实际问题提供了有价值的参考。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/20784.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

施耐德 BAS PLC 基本操作指南

CPU 型号 项目使用的 PLC 型号为&#xff1a;施耐德昆腾 Quantum 140 CPU 67160 P266 CPU &#xff0c;支持热备冗余&#xff0c;内部存储 1024K&#xff0c;支持 2 个 PCMCIA 扩展卡槽CPU 模块自带接口&#xff1a;MB 串口接口、MB 串口接口、USB 接口、以太网接口&#xff…

MATLAB算法实战应用案例精讲-【数模应用】联合分析(附python和MATLAB代码实现)

目录 前言 算法原理 什么是联合分析? 联合分析的基本原理与步骤

【HarmonyOS】List组件多层对象嵌套ForEach渲染更新的处理

【HarmonyOS】List组件多层对象嵌套ForEach渲染更新的处理 问题背景&#xff1a; 在鸿蒙中UI更新渲染的机制&#xff0c;与传统的Android IOS应用开发相比。开发会简单许多&#xff0c;开发效率提升显著。 一般传统应用开发的流程处理分为三步&#xff1a;1.画UI&#xff0c;…

TiDB-从0到1-分布式存储

TiDB从0到1系列 TiDB-从0到1-体系结构TiDB-从0到1-分布式存储TiDB-从0到1-分布式事务TiDB-从0到1-MVCC 一、TiDB-DML语句执行流程&#xff08;增删改&#xff09; DML流程概要 1、协议验证 用户连接到TiDB Server后首先工作的是Protocol Layer模块&#xff0c;该模块会对用…

mysql表字段超过多少影响性能 mysql表多少效率会下降

一直有传言说&#xff0c;MySQL 表的数据只要超过 2000 万行&#xff0c;其性能就会下降。而本文作者用实验分析证明&#xff1a;至少在 2023 年&#xff0c;这已不再是 MySQL 表的有效软限制。 传言 互联网上有一则传言说&#xff0c;我们应该避免单个 MySQL 表中的数据超过 …

内网渗透-在HTTP协议层面绕过WAF

进入正题&#xff0c;随着安全意思增强&#xff0c;各企业对自己的网站也更加注重安全性。但很多web应用因为老旧&#xff0c;或贪图方便想以最小代价保证应用安全&#xff0c;就只仅仅给服务器安装waf。 本次从协议层面绕过waf实验用sql注入演示&#xff0c;但不限于实际应用…

[数据集][目标检测]轮胎检测数据集VOC+YOLO格式439张1类别

数据集格式&#xff1a;Pascal VOC格式YOLO格式(不包含分割路径的txt文件&#xff0c;仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数)&#xff1a;439 标注数量(xml文件个数)&#xff1a;439 标注数量(txt文件个数)&#xff1a;439 标注类别…

mysql怎么部署双机

MySQL的双机部署是为了实现数据的高可用性和容错性。以下是MySQL双机热备部署的基本步骤&#xff0c;我会尽量清晰地分点表示和归纳&#xff1a; 1. 环境准备 安装MySQL&#xff1a;在两台服务器上分别安装MySQL数据库。确保版本兼容。 网络配置&#xff1a;确保两台服务器之…

题目:判断一个素数能被几个9整除

题目&#xff1a;判断一个素数能被几个9整除 There is no nutrition in the blog content. After reading it, you will not only suffer from malnutrition, but also impotence. The blog content is all parallel goods. Those who are worried about being cheated should …

颠仆流离学二叉树2 (Java篇)

本篇会加入个人的所谓鱼式疯言 ❤️❤️❤️鱼式疯言:❤️❤️❤️此疯言非彼疯言 而是理解过并总结出来通俗易懂的大白话, 小编会尽可能的在每个概念后插入鱼式疯言,帮助大家理解的. &#x1f92d;&#x1f92d;&#x1f92d;可能说的不是那么严谨.但小编初心是能让更多人…

泛型知识汇总

演示代码&#xff1a; package exercise;import java.util.Arrays;public class MyArrayList<E> {Object[] obj new Object[10];int size;public boolean add(E e) {obj[size] e;size;return true;}public E get(int index) {return (E) obj[index];}//没有这个函数&a…

现代信号处理12_谱估计的4种方法(CSDN_20240602)

Slepian Spectral Estimator(1950) 做谱估计的目标是尽可能看清楚信号功率谱在某一个频率上的情况&#xff0c;假设我们想了解零频时的分布&#xff0c;最理想的情况是滤波器的传递函数H(ω) 是一个冲激函数&#xff0c;这样就没有旁瓣&#xff0c;也就没有泄漏&#xff1b;其次…

【OpenHarmony】TypeScript 语法 ③ ( 条件语句 | if else 语句 | switch case 语句 )

文章目录 一、条件语句1、if else 语句2、switch case 语句 参考文档 : <HarmonyOS第一课>ArkTS开发语言介绍 一、条件语句 1、if else 语句 TypeScript 中的 if 语句 / if else 语句 用法 , 与 JavaScript 语言中的 if 语句 / if else 语句 语法 基本相同 ; if else 语…

使用Java构建RESTful API:实现灵活、可扩展的Web服务

RESTful API已经成为构建现代Web应用的标准之一&#xff0c;它通过简单的HTTP协议进行通信&#xff0c;提供了一种轻量级、灵活、可扩展的方式来构建和管理Web服务。Java作为一种强大的编程语言&#xff0c;提供了许多框架和库来帮助开发者构建高效的RESTful API。本文将探讨如…

项目质量管理

目录 1.概述 2.三个关键过程 2.1.规划质量管理&#xff08;Plan Quality Management&#xff09; 2.2.管理质量&#xff08;Manage Quality&#xff09; 2.3.控制质量&#xff08;Control Quality&#xff09; 3.应用场景 3.1.十个应用场景 3.2.产品设计与开发 4.小结…

使用PyCharm 开发工具创建工程

一. 简介 前面学习了 安装 python解释器。如何安装python的一种开发工具 PyCharm。 本文来简单学习一下&#xff0c;如何使用 PyCharm 开发工具创建一个简单的 python工程。 二. PyCharm 开发工具创建一个工程 1. 首先&#xff0c;首先打开PyCharm 开发工具。选择 创建一…

Docker部署SiYuan笔记-Unraid

使用unraid的docker部署SiYuan笔记&#xff0c;简单记录 笔记说明 Siyuan笔记是一款基于markdown语法的笔记工具&#xff0c;具有活跃的社区和多设备支持。大部分功能都是免费&#xff0c;源代码开源&#xff0c;支持插件安装&#xff0c;具有很不错的使用体验。 Docker地址&a…

linux---生产者和消费者模型

生产者消费者模式就是通过一个容器来解决生产者和消费者的强耦合问题。生产者和消费者彼此之间不直接通讯&#xff0c;而通过阻塞队列来进行通讯&#xff0c;所以生产者生产完数据之后不用等待消费者处理&#xff0c;直接扔给阻塞队列&#xff0c;消费者不找生产者要数据&#…

2024年海南省三支一扶报名指南,照片要求

2024年海南省三支一扶报名指南&#xff0c;照片要求 一、考试时间安排&#xff1a; 报名时间&#xff1a;6月1日8:00至6月7日18:00 准考证打印时间&#xff1a;6月17日8:00 考试时间&#xff1a;6月22日 二、招聘人数 海南省计划招募390名高校毕业生

STM32_IIC

1、IIC简介 I2C&#xff0c;即Inter IC Bus。是由Philips公司开发的一种串行通用数据总线&#xff0c;主要用于近距离、低速的芯片之间的通信&#xff1b;有两根通信线&#xff1a;SCL&#xff08;Serial Clock&#xff09;用于通信双方时钟的同步、SDA&#xff08;Serial Data…