[英]Java Multithreading: threads shall access list
我正在使用五个线程运行,并且我有一个对象列表(我独立于线程进行初始化)。 列表中的对象使用 boolean 作为标志,所以我知道它们是否已经由另一个线程处理。 此外,我的线程的“ID”有一个 Integer(所以你知道哪个线程当前正在工作)。
问题:参与 for 循环的第一个线程将处理列表中的所有对象,但我希望线程交替进行。 我究竟做错了什么?
run() 方法与此类似:
void run() {
for (int i = 0; i < list.size(); i++) {
ListObject currentObject = list.get(i);
synchronized (currentObject) {
if (currentObject.getHandled == false) {
currentObject.setHandled(true);
System.out.println("Object is handled by " + this.getID());
} else {
continue;
}
}
}
}
TL;DR在线程之间显式或隐式地划分列表; 如果确实需要,还可以同步;
问题:参与 for 循环的第一个线程将处理列表中的所有对象,但我希望线程交替。 我究竟做错了什么?
这整个代码块是可以预料的
for (int i = 0; i < list.size(); i++) {
ListObject currentObject = list.get(i);
synchronized (currentObject) {
....
}
}
基本上是按顺序执行的,因为每个线程在每次迭代中都使用 Object currentObject
隐式锁进行同步。 所有五个线程都进入run
方法,但是其中一个首先进入synchronized (currentObject)
中,所有其他线程将依次等待第一个线程释放currentObject
隐式锁定。 当线程完成时,将继续进行下一次迭代,而其余线程仍在前一次迭代中。 因此,进入synchronized (currentObject)
的第一个线程将有一个领先的开始,并且将是先前线程的步骤头,并且可能会计算所有剩余的迭代。 最后:
第一个处理 for 循环的线程将处理列表中的所有对象,
实际上,您最好在性能方面和可读性方面按顺序执行代码。
假设
我假设
我建议不是每个线程都遍历整个列表并在每次迭代中同步——这非常不执行并且实际上破坏了并行性的点——每个线程都将计算列表的不同块(例如,划分迭代线程之间的for循环)。 例如:
方法一:使用并行 Stream
如果您不必显式并行化代码,请考虑使用 ParallelStream:
list.parallelStream().forEach(this::setHandled);
private void setHandled(ListObject currentObject) {
if (!currentObject.getHandled) {
currentObject.setHandled(true);
System.out.println("Object is handled by " + this.getID());
}
}
方法 2:如果您必须使用执行器显式并行化代码
我正在运行五个线程,
(最初由 ernest_k 说明)
ExecutorService ex = Executors.newFixedThreadPool(5);
for (ListObject l : list)
ex.submit(() -> setHandled(l));
...
private void setHandled(ListObject currentObject) {
if (!currentObject.getHandled) {
currentObject.setHandled(true);
System.out.println("Object is handled by " + this.getID());
}
}
方法 3:如果您必须显式使用线程
void run() {
for (int i = threadID; i < list.size(); i += total_threads) {
ListObject currentObject = list.get(i);
if (currentObject.getHandled == false) {
currentObject.setHandled(true);
System.out.println("Object is handled by " + this.getID());
}
}
}
在这种方法中,我以循环方式在线程之间拆分for循环的迭代,假设total_threads
是计算run
方法的线程数,并且每个线程都有一个唯一的threadID
,范围从0
到total_threads - 1
。 在线程之间分配迭代的其他方法也将如此可见,例如在线程之间动态分配迭代:
void run() {
for (int i = task.getAndIncrement(); i < list.size(); i = task.getAndIncrement();) {
ListObject currentObject = list.get(i);
if (currentObject.getHandled == false) {
currentObject.setHandled(true);
System.out.println("Object is handled by " + this.getID());
}
}
}
其中task
将是原子 integer(即AtomicInteger task = new AtomicInteger();
)。
在所有方法中,想法都是相同的,将列表的不同块分配给线程,以便这些线程可以彼此独立地执行这些块。
如果假设 1. 和 2. 无法做出,那么您仍然可以应用上述在线程之间拆分迭代的逻辑,但您需要添加同步,在我的示例中添加到以下代码块中:
private void setHandled(ListObject currentObject) {
if (!currentObject.getHandled) {
currentObject.setHandled(true);
System.out.println("Object is handled by " + this.getID());
}
}
照原样,您可以将currentObject
字段转换为AtomicBoolean
,如下所示:
private void setHandled(ListObject currentObject) {
if (currentObject.getHandled.compareAndSet(false, true)) {
System.out.println("Object is handled by " + this.getID());
}
}
否则使用同步子句:
private void setHandled(ListObject currentObject) {
synchronized (currentObject) {
if (!currentObject.getHandled) {
currentObject.setHandled(true);
System.out.println("Object is handled by " + this.getID());
}
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.