Java Semaphore实现线程池任务调度

关于Semaphore举例

以一个停车场运作为例。为了简单起见,假设停车场只有三个车位,一开始三个车位都是空的。这时如果同时来了五辆车,看门人允许其中三辆不受阻碍的进入,然后放下车拦,剩下的车则必须在入口等待,此后来的车也都不得不在入口处等待。这时,有一辆车离开停车场,看门人得知后,打开车拦,放入一辆,如果又离开两辆,则又可以放入两辆,如此往复。在这个停车场系统中,车位是公共资源,每辆车好比一个线程,看门人起的就是信号量的作用。

信号量的特性如下:

信号量是一个非负整数(车位数),所有通过它的线程(车辆)都会将该整数减一(通过它当然是为了使用资源),当该整数值为零时,所有试图通过它的线程都将处于等待状态。在信号量上我们定义两种操作: Wait(等待) 和 Release(释放)。 当一个线程调用Wait(等待)操作时,它要么通过然后将信号量减一,要么一直等下去,直到信号量大于一或超时。Release(释放)实际上是在信号量上执行加操作,对应于车辆离开停车场,该操作之所以叫做“释放”是因为加操作实际上是释放了由信号量守护的资源。

在java中,还可以设置该信号量是否采用公平模式,如果以公平方式执行,则线程将会按到达的顺序(FIFO)执行,如果是非公平,则可以后请求的有可能排在队列的头部。


JDK中定义如下:
Semaphore(int permits, boolean fair)

创建具有给定的许可数和给定的公平设置的Semaphore。

Semaphore当前在多线程环境下被扩放使用,操作系统的信号量是个很重要的概念,在进程控制方面都有应用。Java并发库Semaphore 可以很轻松完成信号量控制,Semaphore可以控制某个资源可被同时访问的个数,通过 acquire() 获取一个许可,如果没有就等待,而 release() 释放一个许可。比如在Windows下可以设置共享文件的最大客户端访问个数。

 

与Semaphore具有相同作用的调度工具有CountDownLatch、CyclicBarrier

 

一.多线程资源分配

1.一般模式

package com.tianwt.app;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;

public class SemaphoreThreadPool {
	
	public static int numbers = 20;
    public static void main(String[] args) {
    	// 允许2个线程同时访问  
        final Semaphore semaphore = new Semaphore(2);  
        ExecutorService executorService = Executors.newCachedThreadPool();  
       
        for (int i = 0; i < 20; i++) {  
            final int index = i+1;   
            executorService.execute(new Runnable() {  
                public void run() {  
                    try {  
                        semaphore.acquire();  
                        // 这里可能是业务代码  
                        numbers = numbers-1;
                        System.out.println("("+index+")."+"线程:" + Thread.currentThread().getName() + ",numbers=" + numbers);  
                        TimeUnit.SECONDS.sleep(1);  
                        semaphore.release();  
                        System.out.println("允许TASK个数:" + semaphore.availablePermits());    
                    } catch (InterruptedException e) {  
                        e.printStackTrace();  
                    }  
                }  
            });  
        }  
        executorService.shutdown();  
    }
   
}

执行结果如下:

(1).线程:pool-1-thread-1,numbers=18
(2).线程:pool-1-thread-2,numbers=18
(4).线程:pool-1-thread-4,numbers=16
(3).线程:pool-1-thread-3,numbers=16
允许TASK个数:0
允许TASK个数:0
允许TASK个数:2
允许TASK个数:0
(5).线程:pool-1-thread-5,numbers=14
(6).线程:pool-1-thread-6,numbers=14
(7).线程:pool-1-thread-7,numbers=13
允许TASK个数:0
(10).线程:pool-1-thread-10,numbers=12
允许TASK个数:0
允许TASK个数:1
允许TASK个数:1
(11).线程:pool-1-thread-11,numbers=10
(9).线程:pool-1-thread-9,numbers=10
允许TASK个数:0
(8).线程:pool-1-thread-8,numbers=8
允许TASK个数:1
(12).线程:pool-1-thread-12,numbers=9
允许TASK个数:2
允许TASK个数:2
(14).线程:pool-1-thread-14,numbers=6
(13).线程:pool-1-thread-13,numbers=6
允许TASK个数:2
允许TASK个数:0
(16).线程:pool-1-thread-16,numbers=4
(15).线程:pool-1-thread-15,numbers=4
允许TASK个数:2
允许TASK个数:2
(18).线程:pool-1-thread-18,numbers=2
(17).线程:pool-1-thread-17,numbers=2
(19).线程:pool-1-thread-19,numbers=1
(20).线程:pool-1-thread-20,numbers=0
允许TASK个数:0
允许TASK个数:0
允许TASK个数:1
允许TASK个数:2

从执行结果来看,CacheThreadPool连续创建了20个线程,同样也印证了Semaphore资源分配功能。遗憾的是的Semaphore使用公平模式初始化时,并未完全进行FIFO模式排队。另外一点,从numbers我们知道,Semaphore只是用来实现资源分配的,并不会资源同步

final Semaphore semaphore = new Semaphore(2,true);  

运行结果如下:

2.同步模式

(2).线程:pool-1-thread-2,numbers=18
(1).线程:pool-1-thread-1,numbers=18
允许TASK个数:0
(3).线程:pool-1-thread-3,numbers=16
允许TASK个数:0
(4).线程:pool-1-thread-4,numbers=17
允许TASK个数:2
允许TASK个数:0
(5).线程:pool-1-thread-5,numbers=14
(6).线程:pool-1-thread-6,numbers=14
允许TASK个数:0
(9).线程:pool-1-thread-9,numbers=12
(7).线程:pool-1-thread-7,numbers=13
允许TASK个数:0
(11).线程:pool-1-thread-11,numbers=10
允许TASK个数:0
允许TASK个数:0
(10).线程:pool-1-thread-10,numbers=10
(8).线程:pool-1-thread-8,numbers=8
允许TASK个数:2
允许TASK个数:0
(12).线程:pool-1-thread-12,numbers=8
允许TASK个数:2
(14).线程:pool-1-thread-14,numbers=6
允许TASK个数:2
(13).线程:pool-1-thread-13,numbers=6
允许TASK个数:0
允许TASK个数:0
(16).线程:pool-1-thread-16,numbers=4
(15).线程:pool-1-thread-15,numbers=5
允许TASK个数:0
(17).线程:pool-1-thread-17,numbers=3
(18).线程:pool-1-thread-18,numbers=2
允许TASK个数:0
允许TASK个数:1
允许TASK个数:2
(20).线程:pool-1-thread-20,numbers=0
(19).线程:pool-1-thread-19,numbers=1
允许TASK个数:2
允许TASK个数:2

我们看到,pool-1-thread-8和pool-1-thread-7相距很远。

2.同步模式

如何解决3个不足呢?

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;

public class SemaphoreThreadPool {
	
	public static int numbers = 20;
    public static void main(String[] args) {
    	// 允许2个线程同时访问  
        final Semaphore semaphore = new Semaphore(2,true);  
        final ExecutorService executorService = Executors.newFixedThreadPool(4);
       
        for (int i = 0; i < 20; i++) {  
            final int index = i+1;   
            executorService.execute(new Runnable() {  
                public void run() {  
                    try {  
                        semaphore.acquire();  
                        // 这里可能是业务代码  
                        synchronized (executorService) {
                        numbers = numbers-1;
                        System.out.println("("+index+")."+"线程:" + Thread.currentThread().getName() + ",numbers=" + numbers);  
                        TimeUnit.SECONDS.sleep(1);  
                        }
                        semaphore.release();  
                        System.out.println("允许TASK个数:" + semaphore.availablePermits());    
                    } catch (InterruptedException e) {  
                        e.printStackTrace();  
                    }  
                }  
            });  
        }  
        executorService.shutdown();  
    }
   
}

这样就可以解决线程池过多,FIFO问题以及同步问题。

二.线程等待

public static boolean isLocked = true;
    public static void main(String[] args) throws InterruptedException {
    	
    	final Semaphore semaphore = new Semaphore(0);
    	Thread t = new Thread(new Runnable() {
			@Override
			public void run() {
				try {
					TimeUnit.SECONDS.sleep(3);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}finally{
					System.out.println(Thread.currentThread().getName()+":start");
					/**
					 *  (●'◡'●)
					 * 注意,如果是重要的变量值修改,必须在semaphore调用release之前,否则有可能造成未知问题
					 *  (●'◡'●)
					 */
					isLocked = true; 
					semaphore.release(1);
					System.out.println(Thread.currentThread().getName()+":end");
				}
			}
		});
    	
    	t.start();
    	semaphore.acquire(); //因为初始信号量为0,因此这里阻塞等待
    	System.out.println("主线程执行!");
    	semaphore.release();
    
    }

之前在iOS开发中遇到变量值修改不同不得问题,在这里提醒开发者,如果是重要的变量值修改,必须在semaphore调用release之前,否则有可能造成未知问题

iOS dispatch信号量semaphore

转载于:https://my.oschina.net/ososchina/blog/732102

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值