如何同步两种方法,以防止它们同时被不同的线程访问,而只允许一个方法的多个实例?
例如,如果我创建了一个交通信号灯类和一个汽车类。我想要一个trafficlights.goleft();
和trafficlights.goright();
。
我如何做到使汽车的许多线程可以同时运行goleft但right已锁定。
目前,我已经在方法前面添加了synced关键字,但这完全锁定了它们。
public class Trafficlights{
public synchronized static void crossEast(int num) throws InterruptedException {
System.out.println("Car crossing Eastwards");
Thread.sleep(1000);
System.out.println("Car is now across");
}
public synchronized static void crossWest(int num) throws InterruptedException {
System.out.println("Car crossing westwards");
Thread.sleep(1000);
System.out.println("Car is now across");
}
public static void main(String[] args) {
for (int i =1; i < 20; i++) {
Car car = new Car(i);
car.run();
}
}
}
答案 0 :(得分:0)
对非静态方法使用synced关键字将在调用方法的对象实例上进行同步。因此,如果您有其他此类方法,它们将像您描述的那样被锁定。这个概念的关键是要知道什么是锁/信号量。
如果您不希望仅一部分方法在其操作上进行同步,则必须自己创建一个锁,并且仅那些方法子集应在此锁上进行同步。
在Java中,如果我还记得语法,则将方法主体放在同步块中:
goLeft() {
synchronized (leftRightLock) {
// Your goLeft code here
}
}
任何对象都可以是锁,但是由于其早期的创建属性,枚举曾经是首选。但是,在您处于实例级别的情况下,请在构造函数中创建一些东西,您就很好了。
免责声明:自1.7年前的许多年以来,我就不再使用Java进行编码,因此语法上要花些力气。我希望还有其他更好的方法可以实现这一目标,但核心概念仍然是Java或其他语言。
答案 1 :(得分:0)
我重新设计了以前的答案,因为这不是一个很好的例子。
以前的解决方案应该作为指南,因此您可以自己进行实施。这可能会让您感到困惑,而不是没有帮助您,所以我举了一个不同的例子来解决您的问题。
请注意,我已经测试了此解决方案,并且可以使用。
//Constants that represent left and right
private static final int LEFT = 1;
private static final int RIGHT = 2;
//permits set to 0 initially. No car is allowed to cross, either left or right
private static Semaphore goLeft = new Semaphore(0, true);
private static Semaphore goRight = new Semaphore(0, true);
//booleans to indicate whether cars will go left or right
public static boolean leftGreen = false, rightGreen = false;
public static void crossEast() throws InterruptedException {
goRight.acquire();
System.out.println("Car crossing Eastwards");
Thread.sleep(1000);
System.out.println("Car is now across");
goRight.release();
}
public static void crossWest() throws InterruptedException {
goLeft.acquire();
System.out.println("Car crossing westwards");
Thread.sleep(1000);
System.out.println("Car is now across");
goLeft.release();
}
//Method to be called by user, to turn a the right or left light to green
public static void turnOnLight(int light){
giveGreen(light);
try{
switchLight();
}
catch (InterruptedException ie){
ie.printStackTrace();
}
}
//Depending on the constant passed, leftGreen or rightGreen will be set to true
private static void giveGreen(int light){
if(light == LEFT){
leftGreen = true;
rightGreen = false;
}
else if(light == RIGHT){
leftGreen = false;
rightGreen = true;
}
else{
//you can throw a custom exception here
}
}
//This method will release a permit to the light that is green (true)
private static void switchLight() throws InterruptedException {
if(leftGreen){
if(goRight.availablePermits() > 0)
goRight.acquire();
goLeft.release();
//If thread tried to call goEast, it would be put to sleep
}
else if(rightGreen){
if(goLeft.availablePermits() > 0)
goLeft.acquire();
goRight.release();
//now allowed to go right
}
}
//Of course you shouldn't have throws and catch the exception at runtime instead
public static void main(String[] args) throws InterruptedException{
Thread[] cars = new Thread[4];
/*let's say we want to allow cars to only go left. If you don't
turn on any light, the cars won't move*/
turnOnLight(LEFT);
for(Car car : cars) {
car = new Thread(new Car());
System.out.print(car.getId()+": ");
car.run();
car.join();
}
}
我定义了一个简单的类Car
来演示其工作原理。当然可以更改。
public class Car implements Runnable {
private boolean leftLight, rightLight;
public Car(){
this.leftLight = TrafficLights.leftGreen;
this.rightLight = TrafficLights.rightGreen;
}
@Override
public void run() {
try {
if(leftLight)
TrafficLights.crossWest();
else if(rightLight)
TrafficLights.crossEast();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}