Semaphore简单使用

2022年12月31日11:27:15

 

1、简介:

Semaphore是Java的并发操作,用于多线程中,用于控制同时访问某个资源数,或某个操作的数量;

初始许可数可以通过new Semaphore的构造函数指定,release释放许可,acquire阻塞,如果没有释放,该方法会一直阻塞直到有许可,或操作超时

2、通过打印机的互斥demo来简单使用Semaphor,也能更加理解Semaphore

import java.util.concurrent.Semaphore;

/**
 * semaphore的使用
 */
public class MutrxPrint {

    /**
     * 定义初始值为1的信号量(会先允许有一个线程执行,后来的会阻塞等待)
     */
    private static final Semaphore mSemaphore = new Semaphore(1);


    private static void print() throws InterruptedException {
        System.out.println("print-----1");
        //先等待,请求许可
        mSemaphore.acquire();
        System.out.println("print-----2");
        System.out.println("print-----3--" + Thread.currentThread().getName() + "---Enter");
        Thread.sleep(1000);
        System.out.println("print-----4--" + Thread.currentThread().getName() + "---正在打印.....");
        Thread.sleep(1000);
        System.out.println("print-----5--" + Thread.currentThread().getName() + "---outer.....");
        mSemaphore.release();
    }

    public static void main(String[] args){
        //开启10个线程抢占
        for (int i = 0; i < 10; i++) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        print();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }).start();
        }
    }
}

 输出结果:

print-----3--Thread-1---Enter
print-----1
print-----1
print-----1
print-----1
print-----1
print-----1
print-----1
print-----1
print-----1
print-----4--Thread-1---正在打印.....
print-----5--Thread-1---outer.....
print-----2
print-----3--Thread-3---Enter
print-----4--Thread-3---正在打印.....
print-----5--Thread-3---outer.....
print-----2

 print---1说明是其他线程进来,由于acquire方法在阻塞,

代码11行创建一个为1的许可数信号量,在调用print方法时代码17行会先判断是否还有剩余信号量许可数,有的话hui

会继续往下执行,没有的话会阻塞,等到其他线程执行完并释放,也就是代码24行

mSemaphore.release();之前阻塞的会继续执行

3、连接池的模拟实现

在项目中处理高并发时,一般数据库都会使用数据库连接池,假设现在数据库连接池最大连接数为10,当10个连接都分配出去以后,现在有用户继续请求连接,可能的处理:

a、手动抛出异常,用户界面显示,服务器忙,稍后再试

b、阻塞,等待其他连接的释放

从用户体验上来说,更好的选择当然是阻塞,等待其他连接的释放,用户只会觉得稍微慢了一点,并不影响他的操作。下面使用Semaphore模拟实现一个数据库连接池:

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Semaphore;

/**
 * 连接池使用Semaphore
 */
public class ConnectPool {
    class Conn{}
    private List<Conn> pools = new ArrayList<>(3);
    private Semaphore mSemaphore = new Semaphore(3);
    public ConnectPool() {
        pools.add(new Conn());
        pools.add(new Conn());
        pools.add(new Conn());
    }

    /**
     * 获取连接池的一个连接
     * @return 连接池
     */
    private Conn getConn() throws InterruptedException {
        mSemaphore.acquire();
        System.out.println("------getConn--1--" + Thread.currentThread().getName());
        Conn c = null;
        //加这个同步的原因是为了防止两个线程同时走到这里,加入sync就可以防止将同一个conn 同时分配给这两个线程了
        synchronized (ConnectPool.class){
            c = pools.remove(0);
        }
        System.out.println("------getConn--2--" + Thread.currentThread().getName() + "---" + c);
        return c;
    }

    /**
     *
     * @param c 释放连接池中的连接
     */
    private void releaseConn(Conn c){
        pools.add(c);
        System.out.println(Thread.currentThread().getName()+" release a conn " + c);
        mSemaphore.release();
    }

    public static void main(String[] args){
        ConnectPool mConnectPools = new ConnectPool();
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    Conn conn = mConnectPools.getConn();
                    Thread.sleep(3000);
                    mConnectPools.releaseConn(conn);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();

        for (int i = 0; i < 3; i++) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        Conn conn = mConnectPools.getConn();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }).start();
        }
    }
}

输出日志:

------getConn--1--Thread-1
------getConn--2--Thread-1---com.fengyu.cn.cha11.ConnectPool$Conn@2e4e6427
------getConn--1--Thread-3
------getConn--2--Thread-3---com.fengyu.cn.cha11.ConnectPool$Conn@1232cc0
------getConn--1--Thread-0
------getConn--2--Thread-0---com.fengyu.cn.cha11.ConnectPool$Conn@2eb0c802
Thread-0 release a conn com.fengyu.cn.cha11.ConnectPool$Conn@2eb0c802
------getConn--1--Thread-2
------getConn--2--Thread-2---com.fengyu.cn.cha11.ConnectPool$Conn@2eb0c802

测试时,让线程Thread-1获得连接池一个3秒的连接,同时又有3个线程去连接池获取连接,由于连接池中只有2个连接,所有有一个线程会等待,直到Thread-1释放连接池中的连接,他才会跟连接池连接上;

好了文章有点啰嗦,有错误希望各位提出一起探讨,学习

文章转载:https://blog.csdn.net/lmj623565791/article/details/26810813

  • 作者:隐形人007
  • 原文链接:https://blog.csdn.net/hujiaxi222/article/details/82416846
    更新时间:2022年12月31日11:27:15 ,共 3634 字。