为什么我的线程没有同步?

时间:2017-04-05 18:52:00

标签: java multithreading synchronization

我正在努力掌握同步线程,但我不了解我遇到的问题。

有人可以帮我诊断一下,或者更好地解释一下我如何为自己诊断这个问题?

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)

相反,所有线程都会增加数组,并开始相互打印。

2 个答案:

答案 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);
}

重要的一点是同步就是一次为单个线程提供对象的访问权限(即密钥)。如果您为每个线程使用不同的对象,则线程不会等待密钥,因为每个线程都有自己的密钥(如示例所示)。