在Java中,进行线程同步的方法有哪些?

目录

1. 使用 synchronized 关键字

1.1 同步实例方法

1.2 同步静态方法

1.3 同步代码块

2. 使用 ReentrantLock (显示锁)

3. 使用 ReadWriteLock

4. 使用 volatile 关键字

5. 使用 Atomic 类 (原子变量)

6. 使用 Semaphore (信号量)

7. 使用 CountDownLatch 和 CyclicBarrier

总结


1. 使用 synchronized 关键字

synchronized 是 Java 中最常用的同步机制,它可以用于方法或者代码块。它通过使用对象锁来确保只有一个线程能够访问同步块或同步方法。

1.1 同步实例方法

当一个实例方法使用 synchronized 修饰时,锁的是当前实例对象 this

public class Counter {
    private int count = 0;

    // 使用 synchronized 修饰实例方法,确保每次只有一个线程能调用此方法
    public synchronized void increment() {
        count++;
    }

    public synchronized int getCount() {
        return count;
    }
}

在上面的例子中,increment()getCount() 方法是同步的,多个线程同时访问时,只有一个线程能进入这些方法,其他线程需要等待。

1.2 同步静态方法

当一个静态方法使用 synchronized 修饰时,锁的是类的 Class 对象,而不是实例对象。

public class Counter {
    private static int count = 0;

    // 使用 synchronized 修饰静态方法,锁的是 Counter.class
    public static synchronized void increment() {
        count++;
    }

    public static synchronized int getCount() {
        return count;
    }
}

在这种情况下,所有线程都会被同一个类的锁(Counter.class)同步,所以即使是不同的实例,它们也会争抢同一个类的锁。

1.3 同步代码块

如果你只想同步方法中的一部分代码,可以使用同步代码块,指定一个对象作为锁对象。通常,synchronized 用于局部同步,锁定更细粒度的资源。

public class Counter {
    private int count = 0;
    
    public void increment() {
        synchronized (this) {  // 锁定当前实例
            count++;
        }
    }

    public int getCount() {
        synchronized (this) {  // 锁定当前实例
            return count;
        }
    }
}

在这种情况下,synchronized 只会锁定方法体中的一部分,而不是整个方法,这样可以减少同步的开销。

2. 使用 ReentrantLock (显示锁)

ReentrantLockjava.util.concurrent.locks 包中的一个类,提供了比 synchronized 更加灵活和强大的同步机制。它显式地获取和释放锁,相较于 synchronized 更加灵活,允许进行尝试锁定和中断锁定等操作。

import java.util.concurrent.locks.ReentrantLock;

public class Counter {
    private int count = 0;
    private final ReentrantLock lock = new ReentrantLock();

    public void increment() {
        lock.lock();  // 获取锁
        try {
            count++;
        } finally {
            lock.unlock();  // 确保释放锁
        }
    }

    public int getCount() {
        lock.lock();  // 获取锁
        try {
            return count;
        } finally {
            lock.unlock();  // 确保释放锁
        }
    }
}

3. 使用 ReadWriteLock

ReadWriteLock 是一种更为精细的锁,它允许多个线程同时读取共享资源,但在写操作时会阻塞所有的读线程和写线程。ReentrantReadWriteLockReadWriteLock 的常见实现。

import java.util.concurrent.locks.ReentrantReadWriteLock;

public class Counter {
    private int count = 0;
    private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();

    public void increment() {
        lock.writeLock().lock();  // 获取写锁
        try {
            count++;
        } finally {
            lock.writeLock().unlock();  // 释放写锁
        }
    }

    public int getCount() {
        lock.readLock().lock();  // 获取读锁
        try {
            return count;
        } finally {
            lock.readLock().unlock();  // 释放读锁
        }
    }
}

4. 使用 volatile 关键字

volatile 关键字确保变量的可见性。即,当一个线程修改了 volatile 变量的值,其他线程能够立即看到修改后的值。volatile 并不提供互斥的锁定,因此不能保证原子性,但它确保了数据的最新值被共享。

public class Counter {
    private volatile int count = 0;

    public void increment() {
        count++;
    }

    public int getCount() {
        return count;
    }
}

5. 使用 Atomic 类 (原子变量)

Java 提供了 java.util.concurrent.atomic 包中的一组类,如 AtomicIntegerAtomicLongAtomicReference 等,它们通过 CAS(比较并交换)机制提供了线程安全的操作。

import java.util.concurrent.atomic.AtomicInteger;

public class Counter {
    private AtomicInteger count = new AtomicInteger(0);

    public void increment() {
        count.incrementAndGet();  // 原子性操作
    }

    public int getCount() {
        return count.get();  // 获取原子值
    }
}

6. 使用 Semaphore (信号量)

Semaphore 是一个计数信号量,可以控制同时访问某个特定资源的线程数量。它在一定程度上提供了一种高级的同步机制,可以用于实现限流等功能。

import java.util.concurrent.Semaphore;

public class Resource {
    private final Semaphore semaphore = new Semaphore(1);  // 只有一个许可

    public void accessResource() throws InterruptedException {
        semaphore.acquire();  // 获取许可
        try {
            // 访问共享资源
        } finally {
            semaphore.release();  // 释放许可
        }
    }
}

7. 使用 CountDownLatch 和 CyclicBarrier

这两种类用于线程之间的协调,帮助实现线程的同步。

  • CountDownLatch:常用于一个线程等待其他线程完成后再继续执行。
  • CyclicBarrier:允许多个线程互相等待,直到所有线程都达到某个公共屏障点
import java.util.concurrent.CountDownLatch;

public class Example {
    public static void main(String[] args) throws InterruptedException {
        CountDownLatch latch = new CountDownLatch(3);

        // 启动多个线程
        for (int i = 0; i < 3; i++) {
            new Thread(() -> {
                System.out.println("Thread finished");
                latch.countDown();
            }).start();
        }

        latch.await();  // 主线程等待
        System.out.println("All threads finished");
    }
}

总结

在 Java 中,常见的线程同步方法包括:

  1. synchronized 关键字:最常用的同步方法,可以用于同步方法和同步代码块。
  2. ReentrantLock:显示锁,提供比 synchronized 更强大的功能。
  3. ReadWriteLock:用于处理读写锁,适合频繁读少量写的场景。
  4. volatile 关键字:保证变量的可见性,适用于不需要原子性的场景。
  5. Atomic 类:通过 CAS(比较并交换)实现原子性操作。
  6. Semaphore:用于控制并发访问的线程数。
  7. CountDownLatch 和 CyclicBarrier:用于线程协调。

选择不同的同步机制取决于应用场景的需求和性能考虑。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值