我正在尝试创建一个具有单个任务生产者(老板)和多个任务使用者(雇员)的应用程序。老板会不时添加更多任务,并且由员工来清除任务队列。问题如下:只有一个消费者正在完成所有工作,一旦完成,其他消费者就开始工作。
我尝试使用ReentrantLock,但似乎没有任何改变。
package threads;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.ReentrantLock;
public class Store {
public static ReentrantLock lock = new ReentrantLock();
public static void main(String[] args) {
AtomicInteger tasksList = new AtomicInteger(7);
Runnable boss = () -> {
System.out.println("Boss: Good morning.");
List<Runnable> employeeList = generateEmployeesList(5, tasksList);
for (Runnable employee : employeeList) {
employee.run();
}
while(tasksList.get() > 0) {
try {
lock.lock();
System.out.println("Work more!");
tasksList.incrementAndGet();
} finally {
lock.unlock();
try {
Thread.sleep(1500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
System.out.println("Boss: Time to go home.");
};
System.out.println("= Starting =");
boss.run();
}
public static List<Runnable> generateEmployeesList(int amountOfEmployees, AtomicInteger tasksList){
List<Runnable> employeeList = new ArrayList<Runnable>();
for (int i = 0; i < amountOfEmployees; i++) {
employeeList.add(createEmployee(i+1, tasksList));
}
return employeeList;
}
public static Runnable createEmployee(int employeeNumber, AtomicInteger tasksList) {
return () -> {
System.out.println("Thread #" + (employeeNumber) + " Started. Missing tasks: " + tasksList.get());
while(tasksList.get() > 0) {
try {
lock.lock();
System.out.println("Thread #" + (employeeNumber) + ". Task completed: " + tasksList.decrementAndGet() +" tasks left.");
} finally {
lock.unlock();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
System.out.println("Thread #" + (employeeNumber) +": Finished.");
};
}
}
结果(错误):
= Starting =
Boss: Good morning.
Thread #1 Started. Missing tasks: 7
Thread #1. Task completed: 6 tasks left.
Thread #1. Task completed: 5 tasks left.
Thread #1. Task completed: 4 tasks left.
Thread #1. Task completed: 3 tasks left.
Thread #1. Task completed: 2 tasks left.
Thread #1. Task completed: 1 tasks left.
Thread #1. Task completed: 0 tasks left.
Thread #1: Finished.
Thread #2 Started. Missing tasks: 0
Thread #2: Finished.
Thread #3 Started. Missing tasks: 0
Thread #3: Finished.
Thread #4 Started. Missing tasks: 0
Thread #4: Finished.
Thread #5 Started. Missing tasks: 0
Thread #5: Finished.
Boss: Time to go home.
预期结果(随机消费者减少任务变量):
= Starting =
Boss: Good morning.
Thread #4 Started. Missing tasks: 7
Thread #4. Task completed: 6 tasks left.
Thread #2 Started. Missing tasks: 6
Thread #2. Task completed: 5 tasks left.
Thread #4. Task completed: 4 tasks left.
Thread #1 Started. Missing tasks: 4
...
Thread #1: Finished.
Thread #2: Finished.
Thread #3: Finished.
...
Boss: Time to go home.
关于我在做什么错的任何想法?我该怎么解决我的问题?
我修改了应用程序以使用线程而不是Runnables。但是,我仍然遇到同样的问题。
class Lock {
private static final ReentrantLock lock = new ReentrantLock();
public static final ReentrantLock getLock() {
return lock;
}
}
public class Store {
public static void main(String[] args) {
Boss b = new Boss(5, 7);
b.start();
}
public static List<Thread> generateEmployeesList(int amountOfEmployees, AtomicInteger tasksList){
List<Thread> employeeList = new ArrayList<Thread>();
for (int i = 0; i < amountOfEmployees; i++) {
employeeList.add(employeeFactory(i+1, tasksList));
}
return employeeList;
}
public static Thread employeeFactory(int employeeNumber, AtomicInteger tasksList) {
return new Employee(employeeNumber, tasksList);
}
}
class Employee extends Thread {
int number = 0;
AtomicInteger tasksList;
public Employee(int number, AtomicInteger tasksList) {
this.number = number;
this.tasksList = tasksList;
}
@Override
public void start() {
System.out.println("Thread #" + number + " Started. Missing tasks: " + tasksList.get());
while(tasksList.get() > 0) {
try {
Lock.getLock().lock();
System.out.println("Thread #" + number + ". Task completed: " + tasksList.decrementAndGet() +" tasks left.");
} finally {
Lock.getLock().unlock();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
System.out.println("Thread #" + number +": Finished.");
}
}
class Boss extends Thread {
int employees = 0;
AtomicInteger tasks;
public Boss(int employees, int numberOfTasks) {
this.employees = employees;
this.tasks = new AtomicInteger(numberOfTasks);
}
public void start() {
List<Thread> employeeList = Store.generateEmployeesList(employees, tasks);
System.out.println("Boss: Good Morning!");
for (Thread employee : employeeList) {
employee.start();
}
while(tasks.get() > 0 ) {
Lock.getLock().lock();
try {
System.out.println("Keep working!");
tasks.incrementAndGet();
} finally {
Lock.getLock().unlock();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
答案 0 :(得分:0)
此代码存在多个问题。您误会了如何使用Runnable。 Runnable#run()方法仅运行在当前线程中创建的代码。如果需要某种并行性,则必须创建一个单独的线程来运行它们。
// Change this
employee.start();
// to this:
Thread t = new Thread(employee);
t.start();
第二,如评论员所述,您没有正确锁定对原子的访问。因此,即使它在单个操作中是线程安全的,但在多个调用之间仍然不一致,尤其是如果您的行为取决于先前的结果(您这样做)。这就是为什么如果您在上方进行了单个更改,则会看到类似以下的输出:
= Starting =
Boss: Good morning.
Work more!
Thread #2 Started. Missing tasks: 7
Thread #1 Started. Missing tasks: 8
Thread #4 Started. Missing tasks: 7
Thread #3 Started. Missing tasks: 7
Thread #1. Task completed: 7 tasks left.
Thread #5 Started. Missing tasks: 7
Thread #5. Task completed: 6 tasks left.
Thread #2. Task completed: 5 tasks left.
Thread #4. Task completed: 4 tasks left.
Thread #3. Task completed: 3 tasks left.
Thread #5. Task completed: 2 tasks left.
Thread #1. Task completed: 1 tasks left.
Thread #2. Task completed: 0 tasks left.
Thread #4. Task completed: -1 tasks left.
Thread #3. Task completed: -2 tasks left.
Boss: Time to go home.
Thread #5: Finished.
Thread #1: Finished.
Thread #4: Finished.
Thread #2: Finished.
Thread #3: Finished.
在观察到还有任务之间
tasksList.get() > 0
“完成”一项任务
tasksList.incrementAndGet();
另一个线程可能承担了一项任务。这就是您看到的负数计数。这些操作必须都存在于循环中,或者必须使用某些其他同步方法,例如内置的对象同步方法:
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.ReentrantLock;
public class Store {
public static Object sync = new Object();
public static void main(String[] args) {
AtomicInteger tasksList = new AtomicInteger(7);
Runnable boss = () -> {
System.out.println("Boss: Good morning.");
List<Runnable> employeeList = generateEmployeesList(5, tasksList);
for (Runnable employee : employeeList) {
Thread t = new Thread(employee);
t.start();
}
synchronized (sync) {
while(tasksList.get() > 0) {
try {
System.out.println("Work more!");
tasksList.incrementAndGet();
} finally {
try {
sync.wait(1500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
System.out.println("Boss: Time to go home.");
};
System.out.println("= Starting =");
boss.run();
}
public static List<Runnable> generateEmployeesList(int amountOfEmployees, AtomicInteger tasksList){
List<Runnable> employeeList = new ArrayList<Runnable>();
for (int i = 0; i < amountOfEmployees; i++) {
employeeList.add(createEmployee(i+1, tasksList));
}
return employeeList;
}
public static Runnable createEmployee(int employeeNumber, AtomicInteger tasksList) {
return () -> {
System.out.println("Thread #" + (employeeNumber) + " Started. Missing tasks: " + tasksList.get());
synchronized (sync) {
while (tasksList.get() > 0) {
try {
System.out.println("Thread #" + (employeeNumber) + ". Task completed: "
+ tasksList.decrementAndGet() + " tasks left.");
} finally {
try {
sync.wait(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
System.out.println("Thread #" + (employeeNumber) +": Finished.");
};
}
}
屈服:
= Starting =
Boss: Good morning.
Thread #2 Started. Missing tasks: 7
Thread #3 Started. Missing tasks: 7
Work more!
Thread #4 Started. Missing tasks: 7
Thread #1 Started. Missing tasks: 7
Thread #3. Task completed: 7 tasks left.
Thread #5 Started. Missing tasks: 8
Thread #2. Task completed: 6 tasks left.
Thread #5. Task completed: 5 tasks left.
Thread #1. Task completed: 4 tasks left.
Thread #4. Task completed: 3 tasks left.
Thread #2. Task completed: 2 tasks left.
Thread #1. Task completed: 1 tasks left.
Thread #3. Task completed: 0 tasks left.
Thread #4: Finished.
Thread #5: Finished.
Boss: Time to go home.
Thread #3: Finished.
Thread #2: Finished.
Thread #1: Finished.