首页 > 解决方案 > 如何同步两种方法以防止它们同时被不同的线程访问,但只允许一个方法的多个实例

问题描述

如何同步两种方法以防止它们同时被不同的线程访问,但只允许一个方法的多个实例?

例如,如果我创建了一个交通灯类和一个汽车类。我想要一个trafficlights.goleft();trafficlights.goright();

如何使汽车的许多线程可以同时运行 goleft 但右被锁定。

目前我已经在方法前面添加了同步关键字,但这完全锁定了它们。

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();
        }
    }

 }

标签: java

解决方案


我修改了我之前的答案,因为这不是一个很好的例子。

以前的解决方案应该是一个指南,因此您可以自己实现。这可能不是帮助你,而是让你感到困惑,所以我做了一个不同的例子来说明可以解决你的问题的东西。

请注意,我已经测试了这个解决方案并且它有效。

//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();
        }
    }
}

推荐阅读