如何实现信号量

How to implement a semaphore

所以我创建了一个程序,试图显示使用共享变量的危险,所以我有 3 个 class 主要的 DangersOfSharedVariables 和一个 IncrementerDecrementer class.

所以想法是同时有两个线程运行,都调用各自的方法,所以Decrementer class会调用decrementShared()中的方法main 和 Incrementer class 将调用 main.

中的 incrementShared() 方法

主要方法如下:

/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package dangersofsharedvariables;

import java.util.logging.Level;
import java.util.logging.Logger;

/**
 *
 */
public class DangersOfSharedVariables {

    /**
     * @param args the command line arguments
     */
    private static int sharedValue =0;
    private static int numberOfCycles = 2000000000;

    public static void main(String[] args) {
        // TODO code application logic here
        Incrementer in = new Incrementer(numberOfCycles);
        Decrementer de = new Decrementer(numberOfCycles);
        Semaphore sem = new Semaphore(1);


        in.start();
        try {
            in.join();
        } catch (InterruptedException ex) {}
        de.start();
        try {
            de.join();
        } catch (InterruptedException ex) {}
        System.out.println(sharedValue);
    }

    public void decrementShared(){
        sharedValue -=10;
    }

    public void incrementShared(){
        sharedValue +=10;
    }

}

这是增量器class

/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package dangersofsharedvariables;

/**
 *
 * 
 */
public class Incrementer extends Thread {

    private int numberOfIncrements;

    public Incrementer(int numberOfIncrements) {
        this.numberOfIncrements = numberOfIncrements;

    }

    @Override
    public void run() {
        DangersOfSharedVariables in = new   DangersOfSharedVariables();
        for(int i = 0; i < numberOfIncrements; i++){
            in.incrementShared();
        }
    }
}

递减器Class:

/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package dangersofsharedvariables;

/**
 *
 * 
 */
public class Decrementer extends Thread {
    private int numberOfDecrements;
    public Decrementer(int numberOfDecrements){
        this.numberOfDecrements = numberOfDecrements;

    }

    @Override
    public void run(){
        DangersOfSharedVariables d = new DangersOfSharedVariables();
        for(int i = 0; i < numberOfDecrements; i++){
            d.decrementShared();
        }
    }
}

我在谷歌上搜索,更安全的方法是使用 Sempaphore class。所以我自己决定使用我发现的信号量模板,但不确定我将如何实现它。

信号量Class:

package dangersofsharedvariables;

public class Semaphore {

    // *************************************************************
    // Class properties.
    // Allow for both counting or mutex semaphores.
    private int count;

    // *************************************************************
    // Constructor
    public Semaphore(int n) {
        count = n;
    }

    // *************************************************************
    // Public class methods.
    // Only the standard up and down operators are allowed.
    public synchronized void down() {

        while (count == 0) {

            try {
                wait(); // Blocking call.
            } catch (InterruptedException exception) {
                exception.printStackTrace();
            }
        }
        count--;
    }

    public synchronized void up() {
        count++;
        notify();
    }

}

您想要的名称是 "mutex",它是 "Mutual Exclusion" 的缩写。互斥体是一段代码,一次只能由一个线程执行。

Java语言语句synchronized (foo) { ... }实现了互斥。 foo 是产生某个对象的表达式(有时称为 锁定对象 ),而 ... 是要保护的语句。 Java 语言保证不允许两个线程同时 synchronize 同一个锁对象。


Semaphore可以用来提供互斥,但是比较麻烦,而且已经过时了。

Semaphore 是在计算机具有用于线程同步的硬件原语之前发明的。它应该是 "primitive" 可以构建其他同步构造(例如,互斥锁)的操作。

今天,Semaphore 的 Java 实现实际上是建立在与 synchronized 语句相同的硬件原语之上。

根据您的查询,以下是关于信号量数据结构的简要说明。信号量可用于解决各种同步问题。这个概念是由 Dijkstra (1968) 引入的,他在其中引入了信号量的概念作为操作系统的一部分,以便使进程彼此同步以及与硬件同步。

典型的信号量结构包括4个阶段:

  1. 非临界区
  2. 入门协议
  3. 临界区
  4. 退出协议

非临界区是任何可以由2-n个线程并发执行的代码。

入口协议是进程在进入临界区之前必须执行的代码。它旨在防止进程在另一个进程已经在使用共享资源时进入临界区。

临界区是访问共享资源的代码段。

退出协议是进程在其临界区完成时必须立即执行的代码。

信号量可以有不同的用途:

  1. 用于对单个共享资源的互斥访问,在这种情况下,信号量称为 二进制信号量
  2. 保护对资源的多个实例的访问(计数信号量
  3. 同步两个进程(一个阻塞信号量

信号量机制的多功能性是通过正确的初始化实现的。

出于演示目的,请参阅下面的示例,该示例展示了最简单的二进制信号量实现:

信号量:

package BinarySemaphore;

public class Semaphore{
    private static Semaphore semaphore;
    private static int resource = 1;

    private Semaphore(){}

    public synchronized void increment(){
        while(isAvailable()){
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        resource += 1;

        report();

        this.notifyAll();
    }

    public synchronized void decrement(){
        while(!isAvailable()){
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        resource -= 1;

        report();

        this.notifyAll();
    }

    public synchronized final static boolean isAvailable(){
        return resource == 1 ? true : false;
    }

    public synchronized final static void report(){
        System.out.println("Resource value: " + resource);
    }

    public final static Semaphore getInstance(){
        if(semaphore == null){
            semaphore = new Semaphore();
        }
        return semaphore;
    }
}

增量:

package semaphore;

import BinarySemaphore.Semaphore;

public class Incrementer implements Runnable{
    private static Semaphore semaphore = null;

public Incrementer(Semaphore s){
    semaphore = s;
}

@Override
public void run() {
    for(int i = 0; i < 10; i++){
        System.out.println("Incrementing...");
        semaphore.increment();
    }
}

}

递减器:

package semaphore;

import BinarySemaphore.Semaphore;

public class Decrementer implements Runnable{
    private static Semaphore semaphore = null;

    public Decrementer(Semaphore s) {
        semaphore = s;
    }

    @Override
    public void run() {
        for(int i = 0; i < 10; i++){
            System.out.println("Decrementing...");
            semaphore.decrement();
        }
    }
}

主线:

package semaphore;

import BinarySemaphore.Semaphore;

public class Main {
    public static void main(String[] args){
        Thread iIncrement = new Thread(new Incrementer(Semaphore.getInstance()));
        Thread iDecrement = new Thread(new Decrementer(Semaphore.getInstance()));

        iIncrement.start();
        iDecrement.start();
    }
}

输出:

Decrementing...
Incrementing...
Resource value: 0
Decrementing...
Resource value: 1
Incrementing...
Resource value: 0
Decrementing...
Resource value: 1
Incrementing...
Resource value: 0
Decrementing...
Resource value: 1
Incrementing...
Resource value: 0
Decrementing...
Resource value: 1
Incrementing...
Resource value: 0
Decrementing...
Resource value: 1
Incrementing...
Resource value: 0
Decrementing...
Resource value: 1
Incrementing...
Resource value: 0
Decrementing...
Resource value: 1
Incrementing...
Resource value: 0
Decrementing...
Resource value: 1
Incrementing...
Resource value: 0
Decrementing...
Resource value: 1
Incrementing...
Resource value: 0
Resource value: 1