ReentrantLock - lock.unlock() 不释放锁定



我正在尝试创建一个具有单个任务生产者(老板)和多个任务使用者(员工)的应用程序。老板会不时添加更多任务,由员工清除任务队列。问题如下:只有一个使用者在做所有的工作,一旦完成,其他人就开始工作。

我尝试使用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();
}
}
}
}
}

此代码存在多个问题。你误解了如何使用Runnables。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.

最新更新