得到了一个具有不同变体的经典问题的任务。 我们在北方和南方之间有一座桥梁,n个试图从北方穿过的物体,以及许多试图从南方穿过的物体。(每个物体都在自己的线程上运行)。在这种情况下,对象是农民。所有线程同时开始,因此输出应根据谁首先到达信号量而变化。
我知道有很多这方面的问题已被提出,但我似乎找不到与我能理解的问题关系太密切的问题。
我已经在我的网桥上使用单个 java.util.concurrent.Semaphore 实现了一个功能完善的“一次一个”,但我正在努力升级它以满足下一个的新标准题。
问题是它们现在必须成对交叉,并且两对必须来自桥的同一侧,因此2可以从北方一起穿过,或者从南方穿过2,而不是在如果那边只剩下1个(需要无休止地试图越过)。
我不希望你为我回答我的作业问题,但任何使用java信号量指向正确方向的帮助都会很棒。我的理解是我必须让我的信号量最多为2(足够容易)并且我必须锁定桥,直到某一侧的两个物体准备好交叉(不那么容易)。
在N = 3 S = 4
时交叉的示例输出 Question 2.
N_Farmer1: Waiting for bridge. Going towards South
N_Farmer2: Waiting for bridge. Going towards South
N_Farmer3: Waiting for bridge. Going towards South
S_Farmer1: Waiting for bridge. Going towards North
S_Farmer2: Waiting for bridge. Going towards North
S_Farmer3: Waiting for bridge. Going towards North
S_Farmer4: Waiting for bridge. Going towards North
N_Farmer1: Crossing bridge Step 5.
N_Farmer1: Crossing bridge Step 10.
N_Farmer1: Crossing bridge Step 15.
N_Farmer1: Across the Bridge.
NEON = 1
N_Farmer2: Crossing bridge Step 5.
N_Farmer2: Crossing bridge Step 10.
N_Farmer2: Crossing bridge Step 15.
N_Farmer2: Across the Bridge.
NEON = 2
N_Farmer3: Crossing bridge Step 5.
N_Farmer3: Crossing bridge Step 10.
N_Farmer3: Crossing bridge Step 15.
N_Farmer3: Across the Bridge.
NEON = 3
S_Farmer2: Crossing bridge Step 5.
S_Farmer2: Crossing bridge Step 10.
S_Farmer2: Crossing bridge Step 15.
S_Farmer2: Across the Bridge.
NEON = 4
S_Farmer1: Crossing bridge Step 5.
S_Farmer1: Crossing bridge Step 10.
S_Farmer1: Crossing bridge Step 15.
S_Farmer1: Across the Bridge.
NEON = 5
S_Farmer3: Crossing bridge Step 5.
S_Farmer3: Crossing bridge Step 10.
S_Farmer3: Crossing bridge Step 15.
S_Farmer3: Across the Bridge.
NEON = 6
S_Farmer4: Crossing bridge Step 5.
S_Farmer4: Crossing bridge Step 10.
S_Farmer4: Crossing bridge Step 15.
S_Farmer4: Across the Bridge.
NEON = 7
我的代码Bridge.java
public class Bridge {
private int crossed; //Count the number of crossings
private Semaphore bridgeSem; //semaphore to only allow 1 crossing at a time
//Constructor
public Bridge() {
crossed=0;
bridgeSem = new Semaphore(1); //one bridge resource, mutual exclusivity
}
//Getters
public int getCrossed() {
return crossed;
}
//Methods
public void cross() {
//Semaphore acquire
try {
bridgeSem.acquire();
crossed++; //increment NEON counter
}
catch (InterruptedException e) {}
}
public void exit() {
//Semaphore release
bridgeSem.release();
}
}
Farmer.java:
public class Farmer extends Thread{
private String location; //current location
private String destination; //Opposite location, destination, set in the constructor
private String id; //name
private Bridge bridge; //bridge being used
//constructor
public Farmer(String id, String location, Bridge bridge) {
this.id=id;
this.location=location;
if (location=="North") destination="South"; //Island objects are not necessary for this particular implementation, as our options are merely North or South
else destination="North";
this.bridge = bridge;
System.out.println(id+": Waiting for bridge. Going towards "+destination); //print initial waiting for bridge
}
//getters
public String getLocation() {
return location;
}
public String getID() {
return id;
}
//Do not need setters, none of the instance variables need to change
@Override //initiatied when the thread.start() method is called
public void run() {
//***initiate critical section requiring semaphore***
bridge.cross();
System.out.println(id+": Crossing bridge Step 5.");
System.out.println(id+": Crossing bridge Step 10.");
System.out.println(id+": Crossing bridge Step 15.");
//Sleep for 200 units ,improves readability (else output is too fast)
try {
Thread.sleep(200);
} catch (InterruptedException e) {} //No interrupts implemented, so thread shouldn't be interrupted?
System.out.println(id+": Across the Bridge.");
System.out.println("NEON = "+bridge.getCrossed());
bridge.exit();
//***end critical section***
//Sleep for 20 units, prevents hogging of semaphore(starvation)
try {
Thread.sleep(20);
} catch (InterruptedException e) {}
}//end run
} //结束课
主:
public static void main(String[] args) {
System.out.println("Question 2.");
int N=3,S=4; //DEBUG, add file reading later
Bridge bridge = new Bridge(); //create our bridge
Farmer[] f = new Farmer[N+S]; //array of Farmers
//create North farmers
for (int i=0; i<N; i++) {
f[i] = new Farmer("N_Farmer"+(i+1),"North",bridge);
}
//create South farmers
for (int i=N; i<S+N; i++) {
f[i]= new Farmer("S_Farmer"+(i-N+1),"South",bridge);
}
//start all farmers
for (int i=0;i<S+N;i++) {
f[i].start(); //start Farmer Threads. Farmers can run start, as Farmer extends thread
}
}
答案 0 :(得分:0)
我使用的解决方案: 将交叉相关的东西移动到Bridge文件中 大量的相关逻辑在Farmer.java run()命令中,并且在Bridge.java中有一点同步功能(保留相关的计数器)。 主要文件主要是文件阅读和启动农民。 Farmer Threads基本上检查过那个桥还没有计算2个北方或2个南方农民,如果他们还没有计算当前的那个同步桥功能upThis(Farmer f)。 Bridge记录了北方或南方农民的准备情况。 在Farmer run()中,如果North或South命中2,我们给信号量访问适当的一侧(信号量有2个资源),然后我们等待直到两个都完成(使用同步的bridge.getExited()== 2)然后退出两个他们,并重置所有计数器。现在计数器已经重置,Farmer Thread while循环可以再试一次。
可能不是最好的解决方案,Farmer线程在无限循环中运行,不断重新检查条件,可能不理想。但是就我所说的而言,它的确有效,所以我认为我会把它扔给有类似问题的人。
将此标记为已解决且正确的解决方案,直到某人做出更好的回应。
Bridge.java:
import java.util.concurrent.Semaphore;
public class Bridge {
private int crossed; //Count the number of crossings
private static Semaphore bridgeSem; //semaphore to only allow 1 crossing at a time
private int northWaiting, southWaiting;
private int exited;
//Constructor
public Bridge() {
crossed=0;
bridgeSem = new Semaphore(2); //one bridge resource, mutual exclusivity
northWaiting = southWaiting = 0;
exited = 0;
}
//Getters
public int getCrossed() {
return crossed;
}
//Methods
public synchronized void upCross() {
crossed++;
System.out.println("NEON = "+getCrossed());
}
public synchronized void upThis(Farmer f) {
if (f.getID().startsWith("N")) northWaiting++;
else southWaiting++;
f.counted();
//System.out.println(f.getID()+" is queued to cross"); //DEBUG
}
public synchronized void upExited() {
exited++;
}
public synchronized int getNorth() {
return northWaiting;
}
public synchronized int getSouth() {
return southWaiting;
}
public synchronized int getExited() {
return exited;
}
public synchronized void resetExited() {
exited=0;
}
public synchronized void resetNorth() {
northWaiting=0;
}
public synchronized void resetSouth() {
southWaiting=0;
}
public void cross(Farmer f) {
//Semaphore acquire
try {
bridgeSem.acquire();
System.out.println(f.getID()+": Crossing bridge Step 5.");
System.out.println(f.getID()+": Crossing bridge Step 10.");
System.out.println(f.getID()+": Crossing bridge Step 15.");
//Sleep for 200 units ,improves readability (else output is too fast)
try {
Thread.sleep(200);
} catch (InterruptedException e) {} //No interrupts implemented, so thread shouldn't be interrupted?
System.out.println(f.getID()+": Across the Bridge.");
upCross(); //increment NEON counter, synchronized to avoid print conflicts
//Sleep for 200 units ,improves readability (else output is too fast)
try {
Thread.sleep(200);
} catch (InterruptedException e) {} //No interrupts implemented, so thread shouldn't be interrupted?
}
catch (InterruptedException e) {}
}
public void exit() {
//Semaphore release
upExited();
bridgeSem.release();
}
}
Farmer.java:
public class Farmer extends Thread{
private String location; //current location
private String destination; //Opposite location, destination, set in the constructor
private String id; //name
private Bridge bridge; //bridge being used
private boolean finished=false;
private boolean counted = false;
//constructor
public Farmer(String id, String location, Bridge bridge) {
this.id=id;
this.location=location;
if (location=="North") destination="South"; //Island objects are not necessary for this particular implementation, as our options are merely North or South
else destination="North";
this.bridge = bridge;
System.out.println(id+": Waiting for bridge. Going towards "+destination); //print initial waiting for bridge
}
//getters
public String getLocation() {
return location;
}
public String getID() {
return id;
}
public boolean isCounted() {
return counted;
}
//setter
public void setFinished(boolean finished) {
this.finished=finished;
}
public void counted() {
counted=true;
}
//Overrides the Thread toString() method. Called with Thread.getCurrent().toString()
@Override
public String toString() {
return id;
}
@Override //initiatied when the Farmer Thread .start() method is called
public void run() {
//if ready to cross
while (!finished) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {}
if (bridge.getNorth() != 2 && bridge.getSouth() != 2 && !counted) { //if neither equal 2 yet and we havent added this to the list
bridge.upThis(this); //increments the appropriate north/south counter in a thread safe method, also marks this thread as counted=true
}
if (counted && bridge.getNorth()==2 && id.startsWith("N")) { //if this has been counted, and is a northern farmer and there are 2 northern farmers ready
bridge.cross(this);
bridge.exit();
finished=true;
if (bridge.getExited()==2) { //if both successfully crossed reset counts
bridge.resetExited();
bridge.resetNorth();
//System.out.println("Reset exited and North"); //DEBUG
//System.out.println("Exit: "+bridge.getExited()+", North: "+bridge.getNorth()+", South: "+bridge.getSouth()); //DEBUG
}
}
else if (counted && bridge.getSouth()==2 && id.startsWith("S")) { //else if this has been counted, and is a southern farmer and there are 2 southern farmers ready
bridge.cross(this);
bridge.exit();
finished=true;
if (bridge.getExited()==2) { //if both successfully crossed reset counts
bridge.resetExited();
bridge.resetSouth();
//System.out.println("Reset exited and South"); //DEBUG
//System.out.println("Exit: "+bridge.getExited()+", North: "+bridge.getNorth()+", South: "+bridge.getSouth()); //DEBUG
}
}
}
}//end run
}//end class
Main.java:
import java.io.File;
import java.io.FileNotFoundException;
import java.util.NoSuchElementException;
import java.util.Scanner;
import packagename.Bridge;
import packagename.Farmer;
public class MainP2 {
public static void main(String[] args) {
System.out.println("Question 2.");
//File reading
boolean success = false; //looping file input
int N=0,S=0;
String[] input;
Scanner in = new Scanner(System.in);
System.out.println("Enter file name eg input.txt: ");
while (!success) { //loop until a valid file is given
try {
String f = in.nextLine();
Scanner file = new Scanner(new File(f)); //Throws file not found exception
try {
//split by space
input = file.nextLine().split("\\s+");
//set number of north and south farmers
N = Integer.parseInt(input[0].replaceAll("[^0-9]+",""));
S = Integer.parseInt(input[1].replaceAll("[^0-9]+",""));
success = true; //no exception thrown, all went well, break loop
} catch (NoSuchElementException e) {System.out.println("File was empty or invalid! Please enter a valid file.");}
file.close();
} catch (FileNotFoundException e) { System.out.println("File not found! Please enter a valid file.");}
}
in.close();
//end file reading
Bridge bridge = new Bridge(); //create our bridge
Farmer[] f = new Farmer[N+S]; //array of Farmers
//create North farmers
for (int i=0; i<N; i++) {
f[i] = new Farmer("N_Farmer"+(i+1),"North",bridge);
}
//create South farmers
for (int i=N; i<S+N; i++) {
f[i]= new Farmer("S_Farmer"+(i-N+1),"South",bridge);
}
//start all farmers
for (int i=0;i<S+N;i++) {
f[i].start(); //start Farmer Threads. Farmers can run start, as Farmer extends thread
}
}
}