Java 中使用同步线程的多种实现方式
作者:猿小蔡
本文主要介绍了Java 中使用同步线程的多种实现方式,包括synchronized关键字、ReentrantLock、ReentrantReadWriteLock、Atomic类、CyclicBarrier及Object的wait/notify方法,具有一定的参考价值,感兴趣的可以了解一下
1. 前序
在多线程编程中,线程同步是确保数据一致性和防止竞态条件的关键。Java 提供了多种用于线程同步的机制,以解决不同场景下的线程竞争问题。无论是最基本的 synchronized
关键字,还是更灵活的 ReentrantLock
、ReentrantReadWriteLock
,它们都为开发者提供了不同级别的锁和控制。
本文将逐一介绍 Java 中常见的同步机制,涵盖了 synchronized
、ReentrantLock
、Atomic
类等,同时给出每种机制的示例代码和适用场景,帮助你更好地理解并应用这些同步机制。
2.synchronized
2.1synchronized 关键字
- 描述:Java 中最基础的同步机制就是
synchronized
关键字,它可以用于方法或代码块,确保同一时刻只有一个线程可以访问共享资源。 - 示例代码:
/** * 示例类,演示如何使用 synchronized 方法进行线程同步 */ public class SynchronizedMethodExample { /** 共享计数器 */ private int counter = 0; /** * 同步递增计数器的方法,确保同一时刻只有一个线程可以执行 */ public synchronized void increment() { // 递增计数器 counter++; // 输出当前线程和计数器的值 System.out.println(Thread.currentThread().getName() + " - Counter: " + counter); } /** * 主程序入口,创建多个线程并运行 * @param args 默认参数 */ public static void main(String[] args) { SynchronizedMethodExample example = new SynchronizedMethodExample(); // 线程任务,调用 increment 方法 Runnable task = example::increment; // 创建两个线程 Thread t1 = new Thread(task, "Thread 1"); Thread t2 = new Thread(task, "Thread 2"); // 启动线程 t1.start(); t2.start(); } }
2.2 使用synchronized 代码块
- 描述:相比于
synchronized
方法,synchronized
代码块允许更细粒度地控制同步范围。可以指定特定的代码块进行同步,而不是整个方法,这样可以减少锁的竞争,提高效率。 - 示例代码:
/** * 示例类,演示如何使用 synchronized 代码块进行线程同步 */ public class SynchronizedBlockExample { /** 共享计数器 */ private int counter = 0; /** 自定义锁对象 */ private final Object lock = new Object(); /** * 同步递增计数器的方法,只锁定代码块 */ public void increment() { // 使用 synchronized 代码块确保锁定的粒度更小 synchronized (lock) { counter++; System.out.println(Thread.currentThread().getName() + " - Counter: " + counter); } } /** * 主程序入口,创建多个线程并运行 * @param args 默认参数 */ public static void main(String[] args) { SynchronizedBlockExample example = new SynchronizedBlockExample(); Runnable task = example::increment; Thread t1 = new Thread(task, "Thread 1"); Thread t2 = new Thread(task, "Thread 2"); t1.start(); t2.start(); } }
3.ReentrantLock
- 描述:
ReentrantLock
是Lock
接口的一个常用实现,它提供了更灵活的锁定机制,允许手动加锁和解锁。 - 示例代码:
import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; /** * 示例类,演示如何使用 ReentrantLock 进行线程同步 */ public class LockExample { /** 共享计数器 */ private int counter = 0; /** ReentrantLock 实例 */ private final Lock lock = new ReentrantLock(); /** * 同步递增计数器的方法,手动加锁和解锁 */ public void increment() { // 获取锁 lock.lock(); try { // 递增计数器 counter++; // 输出当前线程和计数器的值 System.out.println(Thread.currentThread().getName() + " - Counter: " + counter); } finally { // 确保锁在最后被释放 lock.unlock(); } } /** * 主程序入口,创建多个线程并运行 * @param args 默认参数 */ public static void main(String[] args) { LockExample example = new LockExample(); Runnable task = example::increment; Thread t1 = new Thread(task, "Thread 1"); Thread t2 = new Thread(task, "Thread 2"); t1.start(); t2.start(); } }
4.ReentrantReadWriteLock
- 描述:
ReentrantReadWriteLock
提供了读写锁机制,可以让多个线程并发读取,但在写入时只有一个线程可以操作。这样可以提高在读多写少场景下的性能。 - 示例代码:
import java.util.concurrent.locks.ReentrantReadWriteLock; /** * 示例类,演示如何使用 ReentrantReadWriteLock 进行线程同步 */ public class ReadWriteLockExample { /** 共享计数器 */ private int counter = 0; /** ReentrantReadWriteLock 实例 */ private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); /** * 获取写锁并递增计数器 */ public void increment() { // 获取写锁 lock.writeLock().lock(); try { // 递增计数器 counter++; System.out.println(Thread.currentThread().getName() + " - Write Counter: " + counter); } finally { // 释放写锁 lock.writeLock().unlock(); } } /** * 获取读锁并读取计数器 * @return 计数器值 */ public int getCounter() { // 获取读锁 lock.readLock().lock(); try { System.out.println(Thread.currentThread().getName() + " - Read Counter: " + counter); return counter; } finally { // 释放读锁 lock.readLock().unlock(); } } /** * 主程序入口,创建多个线程并运行 * @param args 默认参数 */ public static void main(String[] args) { ReadWriteLockExample example = new ReadWriteLockExample(); Runnable writeTask = example::increment; Runnable readTask = example::getCounter; Thread t1 = new Thread(writeTask, "Writer Thread"); Thread t2 = new Thread(readTask, "Reader Thread"); t1.start(); t2.start(); } }
5.Atomic 类
- 描述:
Atomic
类位于java.util.concurrent.atomic
包内,提供了常见的原子操作类(如AtomicInteger
),用于在无锁的情况下对单一变量进行线程安全的操作。 - 示例代码:
import java.util.concurrent.atomic.AtomicInteger; /** * 示例类,演示如何使用 AtomicInteger 进行线程同步 */ public class AtomicExample { /** 线程安全的 AtomicInteger */ private AtomicInteger counter = new AtomicInteger(0); /** * 原子性递增计数器的方法 */ public void increment() { // 原子递增 int newValue = counter.incrementAndGet(); System.out.println(Thread.currentThread().getName() + " - Counter: " + newValue); } /** * 主程序入口,创建多个线程并运行 * @param args 默认参数 */ public static void main(String[] args) { AtomicExample example = new AtomicExample(); Runnable task = example::increment; Thread t1 = new Thread(task, "Thread 1"); Thread t2 = new Thread(task, "Thread 2"); t1.start(); t2.start(); } }
6.CyclicBarrier
- 描述:
CyclicBarrier
是一种允许一组线程相互等待的同步机制,直到所有线程都到达某个共同的屏障点时,才能继续执行。它支持重用,即可以被多次使用。 - 示例代码:
import java.util.concurrent.BrokenBarrierException; import java.util.concurrent.CyclicBarrier; /** * 示例类,演示如何使用 CyclicBarrier 实现线程同步 */ public class CyclicBarrierExample { /** CyclicBarrier 实例,等待 3 个线程 */ private final CyclicBarrier barrier = new CyclicBarrier(3, () -> { // 所有线程到达屏障后执行的操作 System.out.println("All threads have reached the barrier. Barrier action executed."); }); /** * 线程任务,等待屏障点并继续执行 */ public void performTask() { System.out.println(Thread.currentThread().getName() + " is waiting at the barrier"); try { // 等待其他线程到达屏障点 barrier.await(); } catch (InterruptedException | BrokenBarrierException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + " has crossed the barrier"); } /** * 主程序入口,创建多个线程并运行 * @param args 默认参数 */ public static void main(String[] args) { CyclicBarrierExample example = new CyclicBarrierExample(); Thread t1 = new Thread(example::performTask, "Thread 1"); Thread t2 = new Thread(example::performTask, "Thread 2"); Thread t3 = new Thread(example::performTask, "Thread 3"); t1.start(); t2.start(); t3.start(); } }
7.Object 的wait() 和notify() 方法
- 描述:
Object
类的wait()
、notify()
和notifyAll()
方法允许线程在某个条件下进行等待和唤醒。与synchronized
搭配使用,可以实现类似于信号量的功能。 - 示例代码:
/** * 示例类,演示如何使用 wait() 和 notify() 进行线程同步 */ public class WaitNotifyExample { /** 自定义锁对象 */ private final Object lock = new Object(); /** 标志位,表示是否已经生产了数据 */ private boolean isProduced = false; /** * 生产者方法,等待消费者消费后生产新数据 * @throws InterruptedException 当线程被中断时抛出异常 */ public void produce() throws InterruptedException { synchronized (lock) { // 如果已经生产了数据,等待消费者消费 while (isProduced) { lock.wait(); } // 生产数据 System.out.println(Thread.currentThread().getName() + " produced data."); isProduced = true; // 通知消费者可以消费数据了 lock.notify(); } } /** * 消费者方法,等待生产者生产数据并进行消费 * @throws InterruptedException 当线程被中断时抛出异常 */ public void consume() throws InterruptedException { synchronized (lock) { // 如果还没有生产数据,等待生产者生产 while (!isProduced) { lock.wait(); } // 消费数据 System.out.println(Thread.currentThread().getName() + " consumed data."); isProduced = false; // 通知生产者可以继续生产数据了 lock.notify(); } } /** * 主程序入口,创建生产者和消费者线程并运行 * @param args 默认参数 */ public static void main(String[] args) { WaitNotifyExample example = new WaitNotifyExample(); // 创建生产者线程 Thread producer = new Thread(() -> { try { for (int i = 0; i < 5; i++) { example.produce(); } } catch (InterruptedException e) { e.printStackTrace(); } }, "Producer"); // 创建消费者线程 Thread consumer = new Thread(() -> { try { for (int i = 0; i < 5; i++) { example.consume(); } } catch (InterruptedException e) { e.printStackTrace(); } }, "Consumer"); producer.start(); consumer.start(); } }
8. 总结
Java 提供了多种用于线程同步的机制,包括 synchronized
、ReentrantLock
、ReentrantReadWriteLock
、Atomic
类、CyclicBarrier
以及 Object
的 wait()
/notify()
。每种方式都有其适用场景和优缺点。对于简单的同步需求,synchronized
是一种直接而有效的选择;对于复杂的并发控制,Lock
提供了更灵活的锁机制;而 wait()
和 notify()
可以实现线程之间的协调工作。
到此这篇关于Java 中使用同步线程的多种实现方式的文章就介绍到这了,更多相关Java 同步线程内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!