我正在努力掌握同步线程,但我不了解我遇到的问题。
有人可以帮我诊断一下,或者更好地解释一下我如何为自己诊断这个问题?
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CyclicBarrier;
public class Controller {
public static void main(String[] args) {
int numThreads = 0;
List<Thread> threads = new ArrayList<>();
if (args.length > 0) {
numThreads = Integer.parseInt(args[0]);
}
else {
System.out.println("No arguments");
System.exit(1);
}
CyclicBarrier barrier = new CyclicBarrier(numThreads);
int arr[][] = new int[10][10];
for (int i = 0; i < numThreads; i++) {
Thread newThread = new Thread(new ThreadableClass(barrier, arr));
threads.add(newThread);
}
for (Thread thread : threads) {
thread.start();
}
}
}
有一个main方法(上面)接受我想要的线程数作为命令行参数。还有一个工作流程(下面),我的目标是增加2D数组中的所有元素,并在下一个线程有机会做同样的事情之前打印数组:
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
public class ThreadableClass implements Runnable {
private CyclicBarrier barrier;
private int arr[][];
public ThreadableClass(CyclicBarrier barrier, int[][] arr) {
this.barrier = barrier;
this.arr = arr;
}
@Override
public void run() {
long threadId = Thread.currentThread().getId();
System.out.println(threadId + " Starting");
for (int i = 0; i < 10; i++) {
changeArray();
try {
barrier.await();
} catch (InterruptedException | BrokenBarrierException e) {
e.printStackTrace();
}
}
}
private synchronized void changeArray() {
for (int i = 0; i < arr.length; i++) {
for (int j = 0; j < arr.length; j++) {
arr[i][j]++;
}
}
printArray();
}
private synchronized void printArray() {
System.out.println(Thread.currentThread().getId() + " is printing: ");
for (int i = 0; i < arr.length; i++) {
for (int j = 0; j < arr.length; j++) {
System.out.print(arr[i][j] + " ");
}
System.out.println();
}
}
}
想象数组的大小是2x2,预期输出看起来像这样:
1 1
1 1
2 2
2 2
3 3
3 3
4 4
4 4
...
...
(10 * numThreads)-1 (10 * numThreads)-1
(10 * numThreads)-1 (10 * numThreads)-1
(10 * numThreads) (10 * numThreads)
(10 * numThreads) (10 * numThreads)
相反,所有线程都会增加数组,并开始相互打印。
答案 0 :(得分:4)
没有对结果感到惊讶。您创建 n 个线程。你告诉所有线程开始。每个线程run()都以:
开头long threadId = Thread.currentThread().getId();
System.out.println(threadId + " Starting");
...changeArray();
要改变那个共享数组。 写入数组后,您尝试同步(在该障碍上)。太晚了!
关键是:你有10个不同的 ThreadableClass实例。每个人都在拥有上运作! synchronized
关键字......在这里根本没有提供任何保护!
因为:synchronized会阻止两个不同的线程在相同的对象上调用相同的方法。但是当你有多个对象,并且你的线程在那些不同的对象上调用该方法时,比没有锁定!您的代码可以归结为:
threadA to call changeArray() .. on itself
threadB to call changeArray() .. on itself
threadC to call changeArray() .. on itself
...
换句话说:您为 n 线程提供对该共享阵列的访问权限。但是你允许那些 n 线程同时输入changeArray()。
一个简单的解决方法;改变
private synchronized void changeArray() {
到
private void changeArray() {
synchronized(arr) {
换句话说:确保n个线程必须在相同监视器上锁定;在这种情况下是共享数组。
或者:不要在该ThreadableClass中创建changeArray()
方法...而是创建一个类
ArrayUpdater {
int arr[] to update
synchronized changeArray() ...
然后创建该类的一个实例;并为每个线程提供相同的实例。现在,同步方法将阻止多个线程进入!
答案 1 :(得分:0)
因为您使用new
为每个theard提供了new ThreadableClass(barrier, arr)
个实例,所以所有theadrs都使用不同的ThreadableClass
个对象,因此您的代码同步方法并行运行,因此您需要使用单个ThreadableClass
对象,如下所示:
ThreadableClass threadableClass= new ThreadableClass(barrier, arr);
for (int i = 0; i < numThreads; i++) {
Thread newThread = new Thread(threadableClass);
threads.add(newThread);
}
重要的一点是同步就是一次为单个线程提供对象的访问权限(即密钥)。如果您为每个线程使用不同的对象,则线程不会等待密钥,因为每个线程都有自己的密钥(如示例所示)。