类Semaphore的同步性

单词Semaphore的中文含义是信号、信号系统意思。此类的主要作用是限制线程并发的数量,如果不限制线程并发的数量。,则CPUD的资源很快就会被耗尽,每个线程执行的任务相当缓慢,因为CPU要把时间片分配给不同的线程对象,而且上下文切换也要耗时,最终造成系统运行效率大幅降低,所以限制并发线程的数量还是非常有必要的。

在生活中也存在这种场景,比如一个生产键盘的生产商,发布了10个代理销售许可,所有最多只有10个代理商来获得其中的一个许可,这样就限制了代理商的数量,同理也限制了线程的并发数量,这就是Semaphore类要达到的目的。

Semaphore类发放许可的计算方式是“减法”操作。

类Semaphore的同步性

多线程中的同步概念其实就是排着队去执行一个任务,执行任务是一个一个执行,并不能并行执行,这样的优点是有助于程序逻辑的正确性,不会出现非线程安全问题,保证软件系统功能上的运行稳定性。

创建实验用的项目SemaphoreTest1,类S而vice.java代码如下:

package com.yc.semephore;

import java.util.concurrent.Semaphore;

public class Service {
	private Semaphore semaphore = new Semaphore(1);
	
	public void test(){
		try {
			semaphore.acquire();
			System.out.println( Thread.currentThread().getName() + "\tbegin time:" + System.currentTimeMillis());
			
			Thread.sleep(5*1000);
			
			System.out.println( Thread.currentThread().getName() + "\tend time:" + System.currentTimeMillis());
			semaphore.release();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
}


类Semaphore的构造函数参数permits是许可的意思,代表同一时间内,最多允许多少个线程同时执行acquire()和release()之间的代码。

无参方法acquire()的作用是使用一个许可,是减法操作。

创建3个线程类如下:

ThreadA:

package com.yc.semephore;

public class ThreadA extends Thread{
	private Service service;
	
	public ThreadA(Service service){
		this.service = service;
	}

	@Override
	public void run() {
		service.test();
	}
}
ThreadB:

package com.yc.semephore;

public class ThreadB extends Thread{
	private Service service;
	
	public ThreadB(Service service){
		this.service = service;
	}

	@Override
	public void run() {
		service.test();
	}
	
	
}

ThreadC:

package com.yc.semephore;

public class ThreadC extends Thread{
	private Service service;
	
	public ThreadC(Service service){
		this.service = service;
	}

	@Override
	public void run() {
		service.test();
	}
	
	
}
运行类SemaphoreTest.java代码如下:

package com.yc.semephore;

public class SemaphoreTest {
	public static void main(String[] args) {
		Service service = new Service();
		
		ThreadA ta = new ThreadA(service);
		ThreadB tb = new ThreadB(service);
		ThreadC tc = new ThreadC(service);
		
		ta.setName("A");
		tb.setName("B");
		tc.setName("C");
		
		ta.start();
		tb.start();
		tc.start();
	}
}

运行结果为:
A begin time:1489640996583
A end time:1489641001583
B begin time:1489641001583
B end time:1489641006583
C begin time:1489641006583
C end time:1489641011584

private Semaphore semaphore = new Semaphore(1); 

来定义最多允许1个线程执行acquire()和release()之间的代码。



类Semaphore构造方法permits参数的作用

参数permits的作用是设置许可的个数,前面已经使用过代码:

private Semaphore semaphore = new Semaphore(1);

来进行程序的设计,使同一时间内最多只有一个线程可以执行acquire()和release()之间的代码,因为只有一个许可,其实还可以传入 >1 的许可,代表同一时间内,最多允许有 x 个线程可以执行acquire()和release()之间的代码。

创建实验用的项目SemaphoreTest2,将SemaphoreTest1项目中的所有源代码复制到SemaTest2中。并该类Service.java代码如下:

package com.yc.semephore;

import java.util.concurrent.Semaphore;

public class Service {
	private Semaphore semaphore = new Semaphore(2);
	
	public void test(){
		try {
			semaphore.acquire();
			System.out.println( Thread.currentThread().getName() + "\tbegin time:" + System.currentTimeMillis());
			
			Thread.sleep(5*1000);
			
			System.out.println( Thread.currentThread().getName() + "\tend time:" + System.currentTimeMillis());
			semaphore.release();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
}

某一次的运行结果如下:


A begin time:1489668385080
B begin time:1489668385080
A end time:1489668390081
C begin time:1489668390081
B end time:1489668390082
C end time:1489668395081

从它的输出结果来看,对Semaphore类的构造方法传递的参数permits值如果大于1时,该类并不能保证线程安全性,因为还是有可能会出现多个线程共同访问实例变量,导致出现脏数据的情况。



方法acquire(int permits)参数作用及动态添加permits许可数量

有参方法acquire(int permits)的功能就是每调用1次此方法,就使用 x 个许可
有参方法release(int permits)的功能就是每调用1次此方法,就释放 x 个许可

创建Java项目Semaphore_acquire_release,创建类Service,java代码如下:

package com.yc.semephore_1;

import java.util.concurrent.Semaphore;

public class Service {
	private Semaphore semaphore = new Semaphore(10);
	
	public void test(){
		try {
			semaphore.acquire(2);
			System.out.println( Thread.currentThread().getName() + "\tbegin time:" + System.currentTimeMillis());
			
			int value = (int) (Math.random() * 10000);
			Thread.sleep(value);
			System.out.println( Thread.currentThread().getName() + "\tsleep time:" + value);

			System.out.println( Thread.currentThread().getName() + "\tend time:" + System.currentTimeMillis());
			semaphore.release(2);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
}
 线程类ThreadA的代码如下:
package com.yc.semephore_1;

public class ThreadA extends Thread{
	private Service service;
	
	public ThreadA(Service service){
		this.service = service;
	}
	
	@Override
	public void run() {
		service.test();
	}

}

测试类SemaphoreAcquireRelease.java的代码如下:
package com.yc.semephore_1;

public class SemaphoreAcquireReleaseTest {
	public static void main(String[] args) {
		Service service = new Service();
		
		ThreadA[] threads = new ThreadA[10];
		
		for(int i = 0; i < threads.length; i ++){
			threads[i] = new ThreadA(service);
			threads[i].start();
		}
	}
}

由上面的测试代码得到了两组结果,如下面的图一和图二

-------------

在代码中一共有10个许可,每次执行semaphore.acquire(2);代码时耗费掉 2 个,所以一共可供 10/2 = 5 个线程同时访问acquire()和release()之间的代码。 如果多次调用Semaphore类的release()或release(int )方法时,还可以动态增加permits的个数。

创建测试用的项目addPermitsCount,类Run.java代码如下:

package com.yc.semephore_2;

import java.util.concurrent.Semaphore;

public class Run {
	public static void main(String[] args) {
		try {
			Semaphore semaphore = new Semaphore(5);
			semaphore.acquire();
			semaphore.acquire();
			semaphore.acquire();
			semaphore.acquire();
			semaphore.acquire();
			System.out.println(semaphore.availablePermits());
			
			semaphore.release();
			semaphore.release();
			semaphore.release();
			semaphore.release();
			semaphore.release();
			System.out.println(semaphore.availablePermits());
			
			semaphore.release(5);
			System.out.println(semaphore.availablePermits());
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		
	}
}

运行结果如下:
0
5
10

此实验说明构造参数new Semaphore(5);中的 5 并不是左最终的许可数量,仅仅是初始的状态值。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值