设计模式 Concurrency 之 Semaphore 信号量

本文介绍了一种基于计数的信号量Semaphore的基本概念及其应用场景,并通过一个水果店的多线程示例详细展示了如何使用Semaphore来控制资源的并发访问。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.定义

Semaphore是一种基于计数的信号量。它可以设定一个阈值,基于此,多个线程竞争获取许可信号,做完自己的申请后归还,超过阈值后,线程申请许可信号将会被阻塞。Semaphore可以用来构建一些对象池,资源池之类的,比如数据库连接池,我们也可以创建计数为1的Semaphore,将其作为一种类似互斥锁的机制,这也叫二元信号量,表示两种互斥状态

2. 适用场景

  • 保护一个重要[代码]部分 防止一次超过N个线程进入
  • 在两个线程之间发送信号

3. 例子

这里写图片描述

Lock

package com.hqq.concurrency.semaphone;

/**
 * Lock
 * Created by heqianqian on 2017/7/30.
 */
public interface Lock {

    void release() throws InterruptedException;

    void acquire() throws InterruptedException;

}

Semaphore

package com.hqq.concurrency.semaphone;

/**
 * Semaphore
 * Created by heqianqian on 2017/7/30.
 */
public class Semaphore implements Lock {

    private final int licenses;

    private int counter;

    public Semaphore(int counter) {
        this.counter = counter;
        this.licenses = counter;
    }

    public int getNumLicenses() {
        return this.licenses;
    }

    public int getAvailableLicenses() {
        return counter;
    }

    @Override
    public synchronized void acquire() throws InterruptedException {
        while (counter == 0) {
            wait();
        }
        counter = counter - 1;
    }

    @Override
    public synchronized void release(){
        if (counter < licenses) {
            counter = counter + 1;
            notify();
        }
    }
}

FruitType:

package com.hqq.concurrency.semaphone;

/**
 * FruitType
 * 水果类型
 * Created by heqianqian on 2017/7/30.
 */
public enum FruitType {

    APPLE, ORANGE, BANANA;

}

Fruit

package com.hqq.concurrency.semaphone;

/**
 * Fruit
 * <p>
 * Created by heqianqian on 2017/7/30.
 */
public class Fruit {

    private FruitType fruitType;

    public Fruit(FruitType fruitType) {
        this.fruitType = fruitType;
    }

    public FruitType getFruitType() {
        return fruitType;
    }

    @Override
    public String toString() {
        switch (fruitType) {
            case ORANGE:
                return "Orange";
            case APPLE:
                return "Apple";
            case BANANA:
                return "Banana";
            default:
                return "";
        }
    }
}

FruitBowl

package com.hqq.concurrency.semaphone;

import java.util.ArrayList;
import java.util.List;

/**
 * FruitBowl
 * Created by heqianqian on 2017/7/30.
 */
public class FruitBowl {

    private List<Fruit> fruitList = new ArrayList<>();

    public int countFruit() {
        return fruitList.size();
    }

    public void put(Fruit fruit) {
        fruitList.add(fruit);
    }

    public Fruit get() {
        if (fruitList.size() == 0) {
            return null;
        }
        return fruitList.remove(0);
    }
}

FruitShop

package com.hqq.concurrency.semaphone;

/**
 * FruitShop
 * Created by heqianqian on 2017/7/30.
 */
public class FruitShop {

    private FruitBowl[] fruitBowls = {
            new FruitBowl(),
            new FruitBowl(),
            new FruitBowl()
    };

    private boolean[] available = {
            true,
            true,
            true
    };

    private Semaphore semaphore;

    public FruitShop() {
        for (int i = 0; i < 100; i++) {
            fruitBowls[0].put(new Fruit(FruitType.APPLE));
            fruitBowls[0].put(new Fruit(FruitType.BANANA));
            fruitBowls[0].put(new Fruit(FruitType.ORANGE));
        }
        semaphore = new Semaphore(3);
    }

    public synchronized int countFruit() {
        return fruitBowls[0].countFruit() + fruitBowls[1].countFruit()
                + fruitBowls[2].countFruit();
    }

    public synchronized FruitBowl takeBowl() {
        FruitBowl fruitBowl = null;

        try {
            semaphore.acquire();
            if (available[0]) {
                fruitBowl = fruitBowls[0];
                available[0] = false;
            } else if (available[1]) {
                fruitBowl = fruitBowls[1];
                available[1] = false;
            } else if (available[2]) {
                fruitBowl = fruitBowls[2];
                available[2] = false;
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            semaphore.release();
        }
        return fruitBowl;
    }

    public synchronized void returnBowl(FruitBowl fruitBowl) {
        if (fruitBowl == fruitBowls[0]) {
            available[0] = true;
        } else if (fruitBowl == fruitBowls[1]) {
            available[1] = true;
        } else if (fruitBowl == fruitBowls[2]) {
            available[2] = true;
        }
    }

}

Customer

package com.hqq.concurrency.semaphone;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Customer
 * Created by heqianqian on 2017/7/30.
 */
public class Customer extends Thread {

    private static final Logger LOGGER = LoggerFactory.getLogger(Customer.class);

    private String name;

    private FruitShop fruitShop;

    private FruitBowl fruitBowl;

    public Customer(String name, FruitShop fruitShop) {
        this.name = name;
        this.fruitShop = fruitShop;
        this.fruitBowl = new FruitBowl();
    }

    @Override
    public void run() {
        while (fruitShop.countFruit() > 0) {
            FruitBowl bowl = fruitShop.takeBowl();
            Fruit fruit;

            if (bowl != null && (fruit = bowl.get()) != null) {
                LOGGER.info("{} took an {}", name, fruit);
                fruitBowl.put(fruit);
                fruitShop.returnBowl(bowl);
            }
        }

        LOGGER.info("{} took {}", name, fruitBowl);
    }
}

App

package com.hqq.concurrency.semaphone;

/**
 * App
 * Created by heqianqian on 2017/7/30.
 */
public class App {

    /**
     * main method
     *
     * @param args
     */
    public static void main(String[] args) {
        FruitShop shop = new FruitShop();
        new Customer("Peter", shop).start();
        new Customer("Paul", shop).start();
        new Customer("Mary", shop).start();
        new Customer("John", shop).start();
        new Customer("Ringo", shop).start();
        new Customer("George", shop).start();
    }
}

输出结果:

INFO  [2017-08-09 01:51:34,148] com.hqq.concurrency.semaphone.Customer: Paul took an Apple
INFO  [2017-08-09 01:51:34,148] com.hqq.concurrency.semaphone.Customer: Paul took an Banana
INFO  [2017-08-09 01:51:34,148] com.hqq.concurrency.semaphone.Customer: Mary took an Orange
INFO  [2017-08-09 01:51:34,148] com.hqq.concurrency.semaphone.Customer: Paul took an Apple
INFO  [2017-08-09 01:51:34,164] com.hqq.concurrency.semaphone.Customer: Mary took an Banana
INFO  [2017-08-09 01:51:34,164] com.hqq.concurrency.semaphone.Customer: Paul took an Orange
INFO  [2017-08-09 01:51:34,164] com.hqq.concurrency.semaphone.Customer: Mary took an Apple
INFO  [2017-08-09 01:51:34,164] com.hqq.concurrency.semaphone.Customer: Paul took an Banana
INFO  [2017-08-09 01:51:34,164] com.hqq.concurrency.semaphone.Customer: Mary took an Orange
INFO  [2017-08-09 01:51:34,164] com.hqq.concurrency.semaphone.Customer: Paul took an Apple
INFO  [2017-08-09 01:51:34,164] com.hqq.concurrency.semaphone.Customer: Mary took an Banana
...

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值