2017-04-05 104 views
1

我想了解同步线程,但我不明白我遇到的问题。为什么我的线程不同步?

有人可以帮助我诊断这个,或者甚至更好的解释我如何诊断这个自己?

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(); 
    } 
    } 
} 

有一个主要方法(上面)接受我希望作为命令行参数的线程数。而且还有一个工作流程(如下图)这我的目标有增量在二维阵列中的所有元素,并打印数组的下一个线程都有机会做同样的面前:

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(); 
    } 
} 
} 

想象的大小该阵列是2×2,预期产量会是这个样子:

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

严重:这是一个很好的问题。你放下所有的代码,但它不是“太多”的代码;你描述了预期的和实际的产出。如果现在接受,你只是做了一个奇妙的工作;-) – GhostCat

回答

4

没有令人惊讶的结果。您创建n线程。你告诉所有线程开始。每个线程运行()开始于:

long threadId = Thread.currentThread().getId(); 
System.out.println(threadId + " Starting"); 
...changeArray(); 

要去改变共享数组。 写入数组后,您尝试同步(在该屏障上)。那太晚了!

重点是:你有10 不同 ThreadableClass实例。每个人都在拥有!​​关键词...在这里根本不提供任何保护!

因为:同步防止不同线程调用相同物体上的相同的方法。但是当你有多个对象,并且你的线程正在调用那些不同的对象上的那个方法时,比起有没有锁!你的代码做下来归结到:

threadA to call changeArray() .. on itself 
threadB to call changeArray() .. on itself 
threadC to call changeArray() .. on itself 

...

换句话说:你给ň线程访问该共享的阵列。但是,你允许那些线程同时输入changeArray()。

一个简单的修复;改变

private synchronized void changeArray() { 

换句话说:确保n个线程必须锁定相同监视器上;在那种情况下共享数组。

或者:而不是使changeArray()一个方法在该ThreadableClass ...创建一个类

ArrayUpdater { 
    int arr[] to update 

    synchronized changeArray() ... 

然后创建该类的一个实例;并为每个线程分配相同的实例。现在,sync'ed方法将阻止多个线程进入!

+0

令人惊叹。非常感谢。我现在明白了。通过您的解决方案阅读,我决定在Contoller类中创建一个ThreadableClass实例,然后传递相同实例的每个线程。 (只是试图为下一个人提供一些额外的上下文来想知道同样的事情)。再次感谢GhostCat!你真的帮了很多。 – SupposedlySleeping

+0

由于你似乎知道你在做这些部件的工作,我是否应该对此做出任何风格改进?任何我应该遵循的习俗我不是? (生日快乐!!!) – SupposedlySleeping

+0

只是少数;因为我很快就要上床了;-) A)即使使用静态主要;尽量在那里做尽可能少的事情。像:编写一个方法,该方法使用该数字“n”并创建一个线程列表;另一种方法继续对它们进行处理。而你主要只是读取数字,然后调用这些方法。 B)尽量让你的领域**最终**所以编译器可以帮助你确保它们被分配一个值......正好一次! – GhostCat

0

由于您使用new ThreadableClass(barrier, arr)每个theard提供new情况下,基本上所有theadrs使用不同ThreadableClass对象,所以你的代码同步方法平行运行,所以你需要使用一个ThreadableClass对象,如下图所示:

ThreadableClass threadableClass= new ThreadableClass(barrier, arr); 
for (int i = 0; i < numThreads; i++) { 
    Thread newThread = new Thread(threadableClass); 
    threads.add(newThread); 
} 

重要的一点是同步是所有关于在时间提供访问(即,键),以用于一个单一的线程中的对象。如果您为每个线程使用不同的对象,则线程不会等待该密钥,因为每个线程都有自己的密钥(如您的示例中所示)。