您的位置:首页 > 编程语言 > Java开发

Java多线程之信号量(Semaphore)的使用

2017-03-21 16:37 645 查看
      

Semaphore是什么?

           Semaphore是Java提供的同步对象类,实现了经典的信号量,信号量是通过计数器控制对共享资源的访问。如果计数器大于0,允许访问资源,为0则拒绝访问。计数器计数允许共享资源的许可证,所以要访问资源线程必须保证获取信号量的许可证。通常,要使用信号量需要去访问共享资源的线程得尝试取得许可证,如果信号量的计算器大于0,就说明线程取得了许可证,这会导致信号量计数减小,否则,线程会被阻塞,直到能够获取许可证为止。 如果线程不需要访问共享资源时需要释放许可证。

           Semaphore类有下面两个构造方法:

                  Semaphore(int num)

                  Semaphore(int num,boolean how)

            其中,num指定了初始许可证的计数大小,比如num=1则表示任意时刻只有一个线程能够访问资源。默认情况下,等待线程以未定义的顺序获得许可证。通过将how设置为true,可以确定等待线程以它们要求的访问的顺序获得许可证。

           得到许可证可以通过调用acquire()方法,该方法有如下两张方式:

           void acquire()  throws InterrupetedException

           void acquire(int num)  throws InterrupetedException

           不传num表示获取一个许可证,传则表示获取num个许可证,

           释放许可证可以调用release()方法,如下:

           void release()

           void release(int num)

          

          线程在使用信号量的时候,必须先调用acquire()  ,当线程使用完资源后必须调用release(),具体看下面demo:

public class SemaphoreTest {

//共享资源类
static class Shared{

static int count = 0;

}

static class ThreadOne implements Runnable{

String name;
Semaphore semaphore;

public ThreadOne(String name,Semaphore sem) {
this.name = name;
this.semaphore = sem;
new Thread(this).start();
}

@Override
public void run() {

System.out.println("开始:"+name);
try {
System.out.println(name + "等待获取一个许可证");
semaphore.acquire();
System.out.println(name + "获取了一个许可证");

for (int i = 0; i <5 ; i++) {
Shared.count++;
System.out.println(name+ ":"+Shared.count);
Thread.sleep(10);
}
}catch (Exception e){

}
System.out.println(name + "释放许可证");
semaphore.release();

}
}

static class ThreadTwo implements Runnable{

String name;
Semaphore semaphore;

public ThreadTwo(String name,Semaphore sem) {
this.name = name;
this.semaphore = sem;
new Thread(this).start();
}

@Override
public void run() {

System.out.println("开始:"+name);
try {
System.out.println(name + "等待获取一个许可证");
semaphore.acquire();
System.out.println(name + "获取了一个许可证");

for (int i = 0; i <5 ; i++) {
Shared.count--;
System.out.println(name+ ":"+Shared.count);
Thread.sleep(10);
}
}catch (Exception e){

}
System.out.println(name + "释放许可证");
semaphore.release();

}
}
}

 打印结果:
开始:线程One

线程One等待获取一个许可证

线程One获取了一个许可证

开始:线程Two

线程Two等待获取一个许可证

线程One:1

线程One:2

线程One:3

线程One:4

线程One:5

线程One释放许可证

线程Two获取了一个许可证

线程Two:4

线程Two:3

线程Two:2

线程Two:1

线程Two:0

线程Two释放许可证

使用信号量实现生产者与消费者

          一般情况下多线程间的同步协作会使用synchronized关键字,并且结合wait()与notify()来实现同步,如下代码实现:

public class ThreadTest {

static class Queue{

int count;
boolean valueSet ;

synchronized int get(){
while (!valueSet){
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("消费:"+ count);
valueSet = false;
notify();
return count;
}

synchronized void put(int count){
while (valueSet){
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
this.count = count;
System.out.println("生产:"+ count);
valueSet = true;
notify();
}

}

static class Consumer implements Runnable{

Queue q;

public Consumer(Queue q) {
this.q = q;
new Thread(this,"消费者").start();
}

@Override
public void run() {
for (int i = 0; i < 10; i++) {
q.get();
}
}
}

static class Product implements Runnable{

Queue q;
public Product(Queue q) {
this.q = q;
new Thread(this,"生产者:").start();
}

@Override
public void run() {

for (int i = 0; i < 10; i++) {
q.put(i);
}
}
}

public static void main(String[] args) {
Queue q = new Queue();

new Product(q);
new Consumer(q);
}
}


输出结果:
生产:0

消费:0

生产:1

消费:1

生产:2

消费:2

生产:3

消费:3

生产:4

消费:4

....

使用信号量实现代码如下:

public class SemaphoreTest {

static class Queue{

int count;

Semaphore conSem = new Semaphore(0);
Semaphore proSem = new Semaphore(1);

void get(){
try {
conSem.acquire();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("消费:"+ count);
proSem.release();
}

void put(int count){
try {
proSem.acquire();
} catch (InterruptedException e) {
e.printStackTrace();
}

this.count = count;
System.out.println("生产:"+ count);
conSem.release();
}

}

static class Consumer implements Runnable{

Queue q;

public Consumer(Queue q) {
this.q = q;
new Thread(this,"消费者").start();
}

@Override
public void run() {
for (int i = 0; i < 10; i++) {
q.get();
}
}
}

static class Product implements Runnable{

Queue q;
public Product(Queue q) {
this.q = q;
new Thread(this,"生产者:").start();
}

@Override
public void run() {

for (int i = 0; i < 10; i++) {
q.put(i);
}
}
}

public static void main(String[] args) {
Queue q = new Queue();

new Product(q);
new Consumer(q);
}
}


打印的结果和上面一样。

注意:conSem初始化时没有设置许可证,这样可以保证首页执行put()。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息