java

关注公众号 jb51net

关闭
首页 > 软件编程 > java > 多线程并发控制工具Semaphore使用

多线程并发控制工具Semaphore的使用详解

作者:找不到、了

这篇文章主要介绍了多线程并发控制工具Semaphore的使用,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教

当在多线程运行的场景,部分共享资源会存在资源的冲突和竞争,为了改善资源使用的方式,是否可以通过控制某个方法允许并发访问线程的数量?

如下图所示:

Semaphore可以有效的缓解这个问题。

1、Semaphore类

在jdk中提供了一个Semaphore类(信号量)

它提供了两个方法:

2、基本概念

2.1、信号量

Semaphore 维护了一个计数器(许可的数量),表示可以同时访问某个资源的线程数量。线程通过申请许可来访问资源。

2.2、计数器

信号量的计数器可以被设置为一个初始值,该值表示许可的数量。每当一个线程获取许可时,计数器减一;当释放许可时,计数器加一。

2.3、公平性

Semaphore 可以配置为公平或非公平。公平的信号量遵循 FIFO(先入先出)原则,非公平信号量则不保证获取的顺序。

代码示例:

import java.util.concurrent.Semaphore;

public class SemaphoreTest {

    public static void main(String[] args) {
        final DatabaseConnectionPool pool = new DatabaseConnectionPool(3);

        // 创建多个线程以模拟数据库连接
        Thread thread1 = new Thread(() -> pool.connect("Thread 1"));
        Thread thread2 = new Thread(() -> pool.connect("Thread 2"));
        Thread thread3 = new Thread(() -> pool.connect("Thread 3"));
        Thread thread4 = new Thread(() -> pool.connect("Thread 4"));
        Thread thread5 = new Thread(() -> pool.connect("Thread 5"));


        thread1.start();
        thread2.start();
        thread3.start();
        thread4.start();
        thread5.start();

    }
}


class DatabaseConnectionPool{
    private final Semaphore semaphore;

    DatabaseConnectionPool(int maxConnections) {
        this.semaphore = new Semaphore(maxConnections,true);
    }

    public void connect(String threadName) {
        try {
            System.out.println(threadName + " is trying to connect.");
            // 获取许可
            semaphore.acquire();
            System.out.println(threadName + " has connected to the database.");
            // 模拟使用连接
            Thread.sleep(5000); // 模拟数据库操作
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        } finally {
            // 释放许可
            System.out.println(threadName + " is releasing the connection.");
            semaphore.release();
        }
    }
}

代码解析

1.Semaphore 的创建

public DatabaseConnectionPool(int maxConnections) {
    this.semaphore = new Semaphore(maxConnections);
}

通过指定最大连接数来初始化信号量。

2.获取连接

semaphore.acquire();

线程尝试获取信号量的许可,如果没有可用的许可,则该线程会被阻塞,直到有许可可用。

3.释放连接

semaphore.release();

访问完成后,线程释放许可,让其他线程能够访问。

多线程模拟

使用多个线程来模拟多个连接请求,只有 3 个线程能同时获取许可。

3、使用场景

4、死锁

死锁是一种情况,其中两个或多个线程永远互相等待对方释放资源,从而导致程序无法继续执行。

了解更多死锁知识,可参考:有关Java死锁和活锁的联系

4.1、条件

  1. 互斥条件:至少有一个资源处于非共享模式,即某一时刻只能被一个线程使用。
  2. 保持并等待条件:一个线程保持至少一个资源并等待其他被其他线程占用的资源。
  3. 不剥夺条件:资源不能被强行夺走,只能由持有该资源的线程释放。
  4. 循环等待条件:存在一个线程的集合,使得每个线程都在等待下一个线程持有的资源。

4.2、解决策略

4.3、联系

虽然 Semaphore 可以在某种情况下帮助减少发生死锁的机会,但它并不是解决死锁问题的直接手段。

Semaphore 控制访问的方式可以导致某些设计上的改善,例如:

  1. 限制资源的同时访问Semaphore 可用于限制可同时访问某种资源的线程数量,从而减少复杂的资源使用模式。
  2. 避免持有过多的锁:通过合理设计线程的资源申请和释放逻辑,结合 Semaphore,可以减少因线程在持有多个资源时发生互斥和等待的可能性。

总结

Semaphore 是一个非常有用的并发控制工具,可以有效地控制对共享资源的访问。通过合理使用它,可以避免过多线程同时访问相同资源造成的竞争和冲突,从而提高并发程序的安全性和效率。

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

您可能感兴趣的文章:
阅读全文