所以我在编程过程中偶然发现了信号量,并且最近我自己设计了一个复制快餐连锁店的程序。
我将尽我所能尽力解释,如果我需要更加透彻,请在评论部分告诉我
计划:
有一个 生产者 和一个 消费者 (tills和worker)所以tills接受订单(Place他们在一个缓冲区,所以一个圆形数组...)和工人处理该顺序(从圆形数组中取出订单)
我正在尝试实现信号量,以便在订单生成时,在工人处理订单之前,不能使用该特定的信号。并且还使用信号量,以便官员一次只能取出一个订单
以下是主要方法:
public class FastFood {
/**
* @param args the command line arguments
*/
static Buffer buff = new Buffer(2);
static Semaphore semWorker = new Semaphore(1);
static Semaphore semTills = new Semaphore(1);
static int totalOrders = 10;
static int startOrders = 0;
static int processedOrders = 0;
public static void main(String[] args) {
// TODO code application logic here
int numberOfWorkers = 2;
int numberOfTills = 3;
int numberOfFoodChoices = 4;
Random rand = new Random();
Tills[] tills = new Tills[numberOfTills];
Worker[] workers = new Worker[numberOfWorkers];
//int tillId, int foodId, Buffer buff
for (int i = 0; i < tills.length; i++) {
int foodId = rand.nextInt(numberOfFoodChoices) + 1;
tills[i] = new Tills(i, foodId, buff);
tills[i].start();
}
//int workerId, Buffer buff
for (int i = 0; i < workers.length; i++) {
workers[i] = new Worker(i, buff);
workers[i].start();
}
for (Tills till : tills) {
try {
till.join();
}catch (InterruptedException ex) {
System.out.println(ex);
}
}
for (Worker worker : workers) {
try {
worker.join();
}catch (InterruptedException ex) {
System.out.println(ex);
}
}
因此,您可以通过main方法看到我正在循环并运行worker和tills的线程数组。
这是tills类。所以这就创造了秩序。您将能够看到我正在使用FastFood.semTills.down()和FastFood.semTills.up()这是使用信号量。所以向下是获取信号量而向上是释放信号量。 然而问题是我对这些信号量下降和定位的逻辑。
public class Tills extends Thread {
private final Buffer buff;
private final int foodId;
private final int tillId;
public Tills(int tillId, int foodId, Buffer buff) {
this.tillId = tillId;
this.foodId = foodId;
this.buff = buff;
}
@Override
public void run(){
FastFood.semTills.down();
while(FastFood.startOrders < FastFood.totalOrders){
FastFood.semTills.up();
buff.acquire();
while(buff.isFull()){
try {
buff.release();
sleep(100);
} catch (InterruptedException ex) {
Logger.getLogger(Tills.class.getName()).log(Level.SEVERE, null, ex);
}
}
FastFood.startOrders++;
Order v = new Order(foodId, tillId);
System.out.println(v.toString());
try {
Random n = new Random();
int time = n.nextInt(100) + 1;
buff.release();
sleep(time);
buff.insert(v);
} catch (InterruptedException ex) {
System.out.println(ex);
}
}
}
工人类有点相同,但我想确保一次只能处理一个特定订单的工人(可以让多个工人启用多个订单)
public class Worker extends Thread{
private final int workerId;
private final Buffer buff;
public Worker(int workerId, Buffer buff) {
this.workerId = workerId;
this.buff = buff;
}
public void run(){
FastFood.semWorker.down();
while(FastFood.totalOrders>FastFood.processedOrders){
buff.acquire();
while(buff.isEmpty()){
FastFood.semWorker.up();
try {
buff.release();
sleep(100);
} catch (InterruptedException ex) {
Logger.getLogger(Worker.class.getName()).log(Level.SEVERE, null, ex);
}
}
FastFood.processedOrders++;
System.out.print("Worker: " + workerId);
buff.remove();
buff.release();
try {
Random n = new Random();
int time = n.nextInt(100) + 1;
sleep(time);
} catch (InterruptedException ex) {
System.out.println(ex);
}
}
FastFood.semWorker.up();
}
这是我得到的输出,你可以看到它不是在等待处理订单,因此我的信号量的定位必定是错误的,我已经尝试了各种各样的可能性:
run:
FoodId: 3 TillId : 1 Order Count : 0
FoodId: 3 TillId : 0 Order Count : 1
FoodId: 4 TillId : 2 Order Count : 2
FoodId: 4 TillId : 2 Order Count : 3
FoodId: 4 TillId : 2 Order Count : 4
FoodId: 3 TillId : 0 Order Count : 5
FoodId: 3 TillId : 0 Order Count : 6
Worker: 1 Food: 3 TillId: 0
Worker: 0 Food: 3 TillId: 0
FoodId: 3 TillId : 0 Order Count : 7
FoodId: 3 TillId : 0 Order Count : 8
Worker: 1 Food: 3 TillId: 0
FoodId: 3 TillId : 0 Order Count : 9
FoodId: 3 TillId : 1 Order Count : 10
Worker: 0 Food: 3 TillId: 0
Worker: 1 Food: 3 TillId: 1
Worker: 0 Food: 3 TillId: 0
Worker: 1 Food: 4 TillId: 2
Worker: 0 Food: 3 TillId: 0
Worker: 1 Food: 4 TillId: 2
Worker: 0 Food: 3 TillId: 0
10
快速课程简介:
FastFood:Main,创建线程 缓冲区:用于圆形阵列 订购:存储什么食物直到什么 Tills:创建订单 工人:处理订单
信号量:
package fastfood;
public class Semaphore {
private int count;
public Semaphore(int n) {
count = n;
}
public synchronized void down() {
while (count == 0) {
try {
wait(); // Blocking call.
} catch (InterruptedException exception) {
exception.printStackTrace();
}
}
count--;
}
public synchronized void up() {
count++;
notify();
}
}
缓冲液:
public class Buffer {
private int size;
private int inPtr = 0;
private int outPtr = 0;
private int counter = 0;
private Order[] data;
private Semaphore sem = new Semaphore(1);
public Buffer(int size) {
this.size = size;
this.data = new Order[size];
}
public Order remove(){
// removes the revote for the officer
Order out;
out = data[outPtr];
System.out.println(" Food: " + out.getFoodId() + " TillId: " +
out.getTillId());
outPtr = (outPtr+1)%size;
counter--;
return out;
}
public void insert(Order i){
// inserts a new vote
data[inPtr] = i;
inPtr = (inPtr+1)%size;
counter++;
}
public boolean isEmpty(){
// returns true if empty
return counter==0;
}
public boolean isFull(){
// returns true if full
return counter==size;
}
public void acquire(){
sem.down();
}
public void release(){
sem.up();
}
}
变化:
改变2:
工人阶级:
public void run() {
while(FastFood.processedOrders < FastFood.totalOrders){
try{
buff.acquire();
FastFood.semWorker.down();
while(buff.isEmpty()){
try {
sleep(100);
} catch (InterruptedException ex) {
Logger.getLogger(Worker.class.getName()).log(Level.SEVERE, null, ex);
}
}
try{
Order o = buff.remove();
System.out.println(o.toString() + " FoodId: " + o.getFoodId()
+ " TillId: " + o.getTillId());
FastFood.processedOrders++;
}catch(Exception e){
System.out.println(e);
}
}finally{
buff.release();
FastFood.semTills.up();
}
}
Tills Class:
while (FastFood.startOrders < FastFood.totalOrders) {
try {
buff.acquire();
FastFood.semTills.down();
while (buff.isFull()) {
try {
sleep(100);
} catch (InterruptedException ex) {
Logger.getLogger(Tills.class.getName()).log(Level.SEVERE, null, ex);
}
}
try {
Order o = new Order(foodId, tillId);
System.out.println(o.toString());
buff.insert(o);
FastFood.startOrders++;
} catch (Exception e) {
System.out.println(e);
}
} finally {
buff.release();
FastFood.semWorker.up();
}
订单:
public class Order {
private final int foodId;
private final int tillId;
static int count = 0;
private int orderCount=0;
public Order(int foodId, int tillId){
this.foodId = foodId;
this.tillId = tillId;
this.orderCount = count++;
}
public int getFoodId() {
return foodId;
}
public int getTillId() {
return tillId;
}
public static int getCount() {
return count;
}
public int getOrderCount() {
return orderCount;
}
@Override
public String toString() {
return "FoodId: " +foodId+" TillId : "+tillId+" Order Count : "+ orderCount;
}
答案 0 :(得分:1)
你有没有理由在你的while循环后立即释放你的锁?在您检查while循环后,您的tills正在释放它们。这看起来很混乱。你希望你的直销线程在订单完成后才能入睡,只有在订单完成后才会被唤醒?而且您希望您的员工只专门处理某个订单?只要在缓冲区中有等待命令,工人是否应该能够处理任何订单?对不起,我无法发表评论,因为我没有50个代表。
快餐
import java.util.*;
public class FastFood {
/**
* @param args the command line arguments
*/
static Buffer buff = new Buffer(2);
static Semaphore semWorker = new Semaphore(2);
static Semaphore semTills = new Semaphore(2);
static int totalOrders = 10;
static int startOrders = 0;
static int processedOrders = 0;
public static void main(String[] args) {
// TODO code application logic here
int numberOfWorkers = 2;
int numberOfTills = 3;
int numberOfFoodChoices =4;
Random rand = new Random();
Tills[] tills = new Tills[numberOfTills];
Worker[] workers = new Worker[numberOfWorkers];
//int tillId, int foodId, Buffer buff
for (int i = 0; i < tills.length; i++) {
int foodId = rand.nextInt(numberOfFoodChoices) + 1;
tills[i] = new Tills(i, foodId, buff);
tills[i].start();
}
//int workerId, Buffer buff
for (int i = 0; i < workers.length; i++) {
workers[i] = new Worker(i, buff);
workers[i].start();
}
for (Tills till : tills) {
try {
till.join();
}catch (InterruptedException ex) {
System.out.println(ex);
}
}
for (Worker worker : workers) {
try {
worker.join();
}catch (InterruptedException ex) {
System.out.println(ex);
}
}
}
}
缓冲
public class Buffer {
private int size;
private int inPtr = 0;
private int outPtr = 0;
private int counter = 0;
private Order[] data;
public Buffer(int size) {
this.size = size;
this.data = new Order[size];
}
public synchronized String remove(){
// removes the revote for the officer
Order out;
out = data[outPtr];
outPtr = (outPtr+1)%size;
counter--;
return " Food: " + out.getFoodId() + " ordered by TillId: " + out.getTillId();
}
public synchronized void insert(Order i){
// inserts a new vote
data[inPtr] = i;
inPtr = (inPtr+1)%size;
counter++;
}
public synchronized boolean isEmpty(){
// returns true if empty
return counter==0;
}
public synchronized boolean isFull(){
// returns true if full
return counter==size;
}
}
分隔间
public class Tills extends Thread {
private final Buffer buff;
private final int foodId;
private final int tillId;
public Tills(int tillId, int foodId, Buffer buff) {
this.tillId = tillId;
this.foodId = foodId;
this.buff = buff;
}
public void run(){
while(FastFood.startOrders < FastFood.totalOrders){
FastFood.semTills.down();
while(buff.isFull()){
try {
sleep(100);
} catch (InterruptedException ex) {
//Logger.getLogger(Tills.class.getName()).log(Level.SEVERE, null, ex);
}
}
FastFood.startOrders++;
Order v = new Order(foodId, tillId);
buff.insert(v);
System.out.println("Till number " + tillId + " created a new order " + foodId + " to be processed");
FastFood.semWorker.up();
}
}
}
工人
public class Worker extends Thread{
private final int workerId;
private final Buffer buff;
public Worker(int workerId, Buffer buff) {
this.workerId = workerId;
this.buff = buff;
}
public void run() {
while (FastFood.totalOrders > FastFood.processedOrders) {
FastFood.semWorker.down();
while (buff.isEmpty()) {
try {
sleep(100);
} catch (InterruptedException ex) {
//Logger.getLogger(Worker.class.getName()).log(Level.SEVERE, null, ex);
}
}
//FastFood.processedOrders++;
System.out.println("Worker: " + workerId + " completed order number " + buff.remove() + " total orders processed so far: " + FastFood.processedOrders++);
FastFood.semTills.up();
try {
Random n = new Random();
int time = n.nextInt(100) + 1;
sleep(time);
} catch (InterruptedException ex) {
System.out.println(ex);
}
}
}
}
不确定这是否与您的订单类相似
public class Order{
private int tillID;
private int foodID;
public Order(int food, int till){
tillID = till;
foodID = food;
}
int getFoodId(){
return foodID;
}
int getTillId(){
return tillID;
}
}
在您尝试之前请注意它并非100%正确。我摆脱了缓冲区中的信号量,只是使方法同步。如果您在快餐中将信号量值更改为1,则它将无法完全运行,因为并非所有线程都能够在最后唤醒以加入以退出程序。
此外,使用totalOrders和processessOrders的静态变量作为控制线程何时停止运行的方式似乎很令人担忧,因为我认为每个线程都有自己的副本,因此它也可能导致竞争条件。我可能错了。我不确定你还没看到什么,但我认为this has some good information that might help
答案 1 :(得分:1)
当Worker
等待订单时,实际上会等待多个事件之一:
buffer
变为非空。Tills
完成了他们的工作,因此不会生成任何订单。最好将所有这些事件组合成单一保护(在您的情况下为Semaphore
)。并将等待实现移至Buffer
类:
public class Buffer {
private int size;
private int inPtr = 0;
private int outPtr = 0;
private int counter = 0;
private Order[] data;
private Semaphore sem = new Semaphore(1);
private bool isFinished; /* Whether no futher Orders will be added */
public Buffer(int size) {
this.size = size;
this.data = new Order[size];
}
/* Put Order into buffer. If buffer is full, wait.*/
public void put(Order i){
sem.down();
while(counter == size) // Buffer is full
{
// Some sort of busy wait
sem.up();
sleep(100);
sem.down();
}
data[inPtr] = i;
inPtr = (inPtr+1)%size;
counter++;
sem.up();
}
/*
* Get order from the buffer and remove it.
* If buffer is empty and not finished, wait.
* Return null if buffer is empty and finished.
*/
public Order get(){
sem.down();
while(counter == 0 && !isFinished)
{
// Some sort of busy wait
sem.up();
sleep(100);
sem.down();
}
Order out;
if(counter) // Otherwise `isFinished` is set and null should be returned.
{
out = data[outPtr];
System.out.println(" Food: " + out.getFoodId() + " TillId: " +
out.getTillId());
outPtr = (outPtr+1)%size;
counter--;
}
sem.up();
return out;
}
/* Mark buffer as finished. */
void finish(void)
{
sem.down();
isFinished = true;
sem.up();
}
}
注意,等待的忙碌程度如何:信号量被释放,线程休眠一段时间,然后再次获取信号量。
接下来,最好将关于officer
的所有逻辑合并到单独的类中。它很简单,但会消耗semTills
全局信号量:
class Officer
{
private int totalOrders;
private int startOrder;
private Semaphore sem = new Semaphore(1);
public Officer(int totalOrders) {
this.totalOrders = totalOrders;
}
/* Return true if order is allowed, false otherwise. */
public bool getOrder(void) {
bool result;
sem.down();
if(startOrders != totalOrders) {
startOrders++;
result = true;
}
sem.up();
return result;
}
}
如您所见,检查startOrders
并对其进行修改应属于单个关键部分。
接下来,Tills
应该等待,同时处理由它生成的 order 。这种一次性等待可以使用最初锁定的Semaphore
:
public class Tills extends Thread {
private final Buffer buff;
private final int foodId;
private final int tillId;
private final Semaphore waiter = Semaphore(0); // Initially locked!
public Tills(int tillId, int foodId, Buffer buff) {
this.tillId = tillId;
this.foodId = foodId;
this.buff = buff;
}
@Override
public void run(){
while(FastFood.officer.getOrder()){
Order v = new Order(foodId, tillId);
System.out.println(v.toString());
Random n = new Random();
int time = n.nextInt(100) + 1;
sleep(time);
buff.put(v);
//Wait order to be processed
waiter.down();
}
}
/* Tell that order, added to the buffer, is processed. */
public markOrderProcessed(void) {
waiter.up();
}
}
请注意,实现变得更加简单。此类也发布方法markOrderProcessed
以供Worker
调用。
因为worker只从缓冲区获取 order ,所以此对象(类型为Order
)应该包含对Tills
的引用,并创建它。此外,现在可以同时创建Order
个对象。因此,其静态count
字段应受Semaphore
保护。
public class Order {
...
private Tills till; // New member
static private Semaphore sem = Semaphore(1); // Protect *count* field
public Order(..., Tills till) { // New parameter to the constructor
...
this.till = till;
// `count` should be incremented under protection.
sem.down();
this.orderCount = count++;
sem.up();
}
/* New method: mark order as processed. */
public markProcessed(void)
{
till.markOrderProcessed();
}
}
现在已经阅读了实施Worker
运行方法的所有内容。请注意,现在这种方法没有直接使用任何同步,一切都在低级别的类中完成:
public void run(){
Order order;
while((order = buff.get()) != null) {
System.out.println("Worker: " + workerId + " " + order);
Random n = new Random();
int time = n.nextInt(100) + 1;
sleep(time);
order.markProcessed(); // Mark order as processed, so tills can continue.
}
}
主要课程。请注意,只有在所有Tills
完成(加入)之后,缓冲区才会标记为已完成:
public class FastFood {
static Buffer buff = new Buffer(2);
static Officer officer = new Officer(10);
public static void main(String[] args) {
// TODO code application logic here
int numberOfWorkers = 2;
int numberOfTills = 3;
int numberOfFoodChoices = 4;
Random rand = new Random();
Tills[] tills = new Tills[numberOfTills];
Worker[] workers = new Worker[numberOfWorkers];
//int tillId, int foodId, Buffer buff
for (int i = 0; i < tills.length; i++) {
int foodId = rand.nextInt(numberOfFoodChoices) + 1;
tills[i] = new Tills(i, foodId, buff);
tills[i].start();
}
//int workerId, Buffer buff
for (int i = 0; i < workers.length; i++) {
workers[i] = new Worker(i, buff);
workers[i].start();
}
for (Tills till : tills) {
try {
till.join();
}catch (InterruptedException ex) {
System.out.println(ex);
}
}
/*
* Mark buffer as finished.
*
* Workers, which found the buffer empty, may safetly stop now.
*/
buff.finish();
for (Worker worker : workers) {
try {
worker.join();
}catch (InterruptedException ex) {
System.out.println(ex);
}
}
}
}