问题
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.
来源:https://stackoverflow.com/questions/49899944/java-create-two-threads-but-only-one-running