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

时间:2018-12-14 15:23:00

标签: java

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

例如,如果我创建了一个交通信号灯类和一个汽车类。我想要一个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();
        }
    }

 }

2 个答案:

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