Java Create Two Threads but Only One Running

2019-06-14 17:04发布

问题:

I might have asked earlier here, and there is lack of understanding and complex, Now I rewrite the program in a much easier way to understand.

The Problem:

When I run the 2 threads, only 1 thread do the job.

Suspicion Helper

I suspect that the thread lock itself, so that another thread cannot access it.

Code

Initialization Main

    Shop shop = new Shop();
    CarGenerator carGenerator = new CarGenerator(shop);

    Mechanics mechanics1 = new Mechanics(shop, "Mechanics 1");
    Mechanics mechanics2 = new Mechanics(shop, "Mechanics 2");

    //Threads
    Thread CarThread = new Thread(carGenerator);
    Thread Mechanics1Thread = new Thread(mechanics1);
    Thread Mechanics2Thread = new Thread(mechanics2);


    CarThread.start();
    Mechanics1Thread.start();
    Mechanics2Thread.start();

Here we can see 2 threads are creates and work as expected.

Now what does the mechanic's function do:

@Override
public void run() {
    while (true) {
        System.out.println("Working Thread: " + Thread.currentThread().getName());
        shop.CarFix(MechanicsName);
    }
}

Clue:

Here I print out how many threads are there and the result is 2:

Working Thread: Thread-1
Working Thread: Thread-2

Great now both thread running as expected, and now I will work at the shop for infinite. I will not cut to show whole process and understand better:

public void CarFix(String MechanicsName) {
    Car car;
    synchronized (ListCarEntry) {
    while (ListCarEntry.size() == 0) {
        try {
            ListCarEntry.wait();
        }   catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    try {
        Thread.sleep(2000);
    } catch (InterruptedException ex) {
        Logger.getLogger(Shop.class.getName()).log(Level.SEVERE, null, ex);
    }
    System.out.println("Working Thread: " + Thread.currentThread().getName());
    //Done 2 Sec Fixing
    //Release Now

    car = (Car) ((LinkedList<?>) ListCarEntry).poll();
    ((LinkedList<Car>) ListCarFix).offer(car);
    System.out.println("Car FIX: " + car.getCarName() + " being fixed by " + MechanicsName);

}

Here the Thread only one that is running instead of earlier 2 threads, and the result is:

Working Thread: Thread-1
Car FIX: Car 1 being fixed by Mechanics 1

Working Thread: Thread-1
Car FIX: Car 2 being fixed by Mechanics 1

Working Thread: Thread-1
Car FIX: Car 3 being fixed by Mechanics 1

Right now only Mechanics 1 fixing, while Mechanics 2 missing.

Clue 2:

When I try to REMOVE THE SLEEP FOR 2 SEC, the result is what I really want like this:

Working Thread: Thread-1
Car FIX: Car 1 being fixed by Mechanics 1

Working Thread: Thread-2
Car FIX: Car 2 being fixed by Mechanics 2

Working Thread: Thread-1
Car FIX: Car 3 being fixed by Mechanics 1

Which is what I really wanted to see.

The Question:

How to make sure thread working as expected with sleep functionality for it?

For the one who reads till the end:

See I have no idea remove sleep might solve the problem, quiet weird.. Anyway any idea guys.. and sorry for the long problem.

EDIT

This is my CarGenerator Class:

public class CarGenerator implements Runnable{
    Shop shop;

    public CarGenerator(Shop shop) {
        this.shop = shop;
    }

    @Override
    public void run() {

    int i = 0;
    while (true) {
        i++;

        Car car = new Car(shop, "Car " + i);

        try {
            Thread.sleep(1000);
        } catch (InterruptedException ex) {
            Logger.getLogger(CarGenerator.class.getName()).log(Level.SEVERE, null, ex);
        }

        Thread thCar = new Thread(car);
        thCar.start();

    }
    }

}

Which call this car class to move the car into the shop:

public class Car implements Runnable{


    private String CarName;
    private Shop shop;


    public Car(Shop shop, String CarName) {
        this.shop = shop;
        this.CarName = CarName;               
    }

    public String getCarName() {
        return CarName;
    }

    @Override
    public void run() {
        //DO
        synchronized(this) {
        shop.CarEntrance(this);
        }


    }

}

This is where the car comes into the shop

public void CarEntrance(Car car) {
    System.out.println("Entry: " + car.getCarName());

    if (ListCarEntry.size() == 10){
        System.out.println("Exit: " + car.getCarName() + "Exit the store, cause FULL");
        return;
    }

    ((LinkedList<Car>) ListCarEntry).offer(car);
    if (ListCarEntry.size() == 1) {
        synchronized(ListCarEntry) {
            ListCarEntry.notify();
        }

    }
}

As seen here I call notify Synchronized notify that being called by the mechanics wait earlier to wait for car entrance.

回答1:

It's a very weird setup to begin with. Your mechanics are the threads being synchronized, but the synchronization is done in the Shop; you'd usually synchronize in the threads themselves. What's more, you don't really need explicit synchronization if you use a thread-safe queue object to manage the cars to be fixed. That makes it a typical producer/consumer problem, with the shop being the producer and the mechanics being the consumers.

class Shop {
    private static BlockingQueue<Integer> cars = new ArrayBlockingQueue<>();
    private static int carNumber;
    private static Random RANDOM = new Random();

    public static void main(String[] args) {

        // you can add more mechanics here
        int mechanicsCount = 2;
        for (int i = 0; i < mechanicsCount; i++) {
            new Thread(createMechanic(String.format("Mechanic %d", i))).start();
        }

        ScheduledExecutorService producingScheduler = Executors.newSingleThreadScheduledExecutor();
        // add a new car every 300 ms
        producingScheduler.scheduleAtFixedRate(() -> {
            try {
                cars.put(carNumber++);
            } catch (InterruptedException e) { }
        }, 0, 300, TimeUnit.MILLISECONDS);
    }

    // the concurrently running code
    private Runnable createMechanic(String name) {
        return () -> {
            try {
                while (true) {
                    // synchronization happens here: queue is thread-safe
                    Integer car = cars.take();
                    System.out.printf("repairing car %s in thread %s\n", car, Thread.currentThread().getName());
                    // take some time repairing
                    int timeToRepair = RANDOM.nextInt(500);
                    Thread.sleep(timeToRepair);
                    System.out.printf("car %s is repaired, took %s ms\n", car, timeToRepair);
                }
            } catch (InterruptedException e) { }
        };

    }
}

This way, the mechanics will be waiting on the queue for the next car to appear.



回答2:

For the solution, I just moved the Thread.sleep from the CarFix Function:

public void CarFix(String MechanicsName) {
    Car car;
    synchronized (ListCarEntry) {
    while (ListCarEntry.size() == 0) {
        try {
            ListCarEntry.wait();
        }   catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    //There is No Sleep

    car = (Car) ((LinkedList<?>) ListCarEntry).poll();
    ((LinkedList<Car>) ListCarFix).offer(car);
    System.out.println("Car FIX: " + car.getCarName() + " being fixed by " + MechanicsName);

}

And moved it into Mechanics Class:

@Override
public void run() {
    while (true) {

    try {
        Thread.sleep(3000);
    } catch (InterruptedException ex) {
        Logger.getLogger(Shop.class.getName()).log(Level.SEVERE, null, ex);
    }
        shop.CarFix(MechanicsName);
    }
}

And the result:

Entry: Car 1
Entry: Car 2

Working Thread: Thread-1
Working Thread: Thread-2
Car FIX: Car 1 being fixed by Mechanics 2
Car FIX: Car 2 being fixed by Mechanics 1

Entry: Car 3
Entry: Car 4
Entry: Car 5

Working Thread: Thread-1
Working Thread: Thread-2
Car FIX: Car 3 being fixed by Mechanics 1
Car FIX: Car 4 being fixed by Mechanics 2

And I thought the car is working 2 by 2 car, which I not really want, I want an independent Mechanics, So I tried to set the Cargenerator to 10 second for debugging test, and the result:

Entry: Car 1
Working Thread: Thread-2
Car FIX: Car 1 being fixed by Mechanics 2

Entry: Car 2
Working Thread: Thread-1
Car FIX: Car 2 being fixed by Mechanics 1

Entry: Car 3
Working Thread: Thread-2
Car FIX: Car 3 being fixed by Mechanics 2

Entry: Car 4
Working Thread: Thread-1
Car FIX: Car 4 being fixed by Mechanics 1

Now It is much fair, and works much better than expected.

Thanks for all of the contribution, and case closed.