我使用两个线程将两个矩阵相乘(但是,程序也是为了扩展而编写的,因此我可能会使用三个,四个等线程)。每个线程计算/完成最终矩阵的一行(或列)的工作。如果一个线程在一行上工作,另一个线程不应该在该行上工作。它/它们应该移动到下一个可用行。
首先,我不确定我实施问题的方式是否正确。如果你能看到更好的方法,请告诉我。
其次,我每次测试它的方式(使用不同大小的矩阵 - 甚至是大的矩阵),只有一个线程完成工作。也就是说,每次,同一个线程都可以访问run()方法的synchronized块。其他线程正在进入run()方法,但为什么只有一个线程总是获得锁定并完成所有工作?
这是我的运行方法:
public void run() {
System.out.println(Thread.currentThread().getName());
while (i < number of columns in final matrix) {
synchronized (this) {
if (i < number of columns in final matrix) {
for (int j = 0; j < Main.B[0].length; j++) {
for (int k = 0; k < Main.A[0].length; k++) {
Main.C[i][j] += Main.A[i][k] * Main.B[k][j];
}
}
i++;
}
}
}
}
这是我的驱动程序类中创建线程并启动程序的代码:
MyRunnable r = new MyRunnable();
Thread thread1 = new Thread(r);
Thread thread2 = new Thread(r);
thread1.start();
thread2.start();
try {
thread1.join();
thread2.join();
} catch (InterruptedException ie) {
System.out.println("\nThe following error occurred: " + ie);
}
}
我想我的问题是双重的 - 我的方法是否适合手头的问题?如果是这样,(如果没有),为什么一个线程总是抓住锁并完成所有工作?我已经在20x20矩阵上检查了最多6个线程的程序,并且总是只有一个线程正在完成工作。
答案 0 :(得分:5)
正如一些评论所暗示的那样,问题在于锁定(即synchronized(this)
部分)。同步是在this
上完成的,在你的情况下是MyRunnable
的单个实例,所以当一个线程在synchronized
块内完成工作时,所有其他线程将等到工作完了。如此有效,一次只有一个线程正在做实际的工作。
以下是解决问题的方法。由于您需要线程并行处理不同的行,因此这项工作必须不通过锁同步(因为锁定意味着相反:一次只能有一个线程可以完成工作)。你做需要同步的是每个线程决定它将在哪一行上工作的部分。
这是一个伪代码示例:
public void run(){
int workRow;
synchronized(this){
workRow = findNextUnprosessedRow();
}
for(int i=0; i<matrix[workRow].length; i++){
//do the work
}
}
请注意,由于上述原因,实际工作有意未同步。
你使用线程的方式是正确的,所以没有问题,但是,我建议你看一下Java的并发API:Thread Pools。以下是如何在您的上下文中使用它的示例:
//Creates a pool of 5 concurrent thread workers
ExecutorService es = Executores.newFixedThreadPool(5);
//List of results for each row computation task
List<Future<Void>> results = new ArrayList<Future<Void>>();
try{
for(int row=0; row<matrix.length; row++){
final int workRow = row;
//The main part. You can submit Callable or Runnable
// tasks to the ExecutorService, and it will run them
// for you in the number of threads you have allocated.
// If you put more than 5 tasks, they will just patiently
// wait for a task to finish and release a thread, then run.
Future<Void> task = es.submit(new Callable<Void>(){
@Override
public Void call(){
for(int col=0; col<matrix[workRow].length; col++){
//do something for each column of workRow
}
return null;
}
});
//Store the work task in the list.
results.add(task);
}
}finally{
//Make sure thread-pool is shutdown and all worker
//threads are released.
es.shutdown();
}
for(Future<Void> task : results){
try{
//This will wait for threads to finish.
// i.e. same as Thread.join()
task.get();
}catch(ExecutionException e){
//One of the tasks threw an exception!
throw new RuntimeException(e);
}
}
这种方法更加清洁,因为工作分配是主要的 线程(外部for循环),因此不需要同步它。
使用线程池时,你也获得了很少的奖金:
很好地处理每个计算过程中的任何异常 的线程。使用裸线时,就像在你的方法中一样,这很容易 “失去”例外。
汇集线程。也就是说,它们会自动重用,因此您无需担心产生新线程的成本。这在你的情况下特别有用,因为你需要在矩阵中每行产生一个线程,这可能相当大,我怀疑。
提交给ExecutorService
的任务包含在一个有用的Future<Result>
对象中,这在每个计算任务实际返回某种结果时最有用。在您的情况下,如果您需要总结矩阵中的所有值,则每个计算任务都可以返回该行的总和。然后你只需要总结一下。
有点长,但希望它清除一些东西。
答案 1 :(得分:4)
您的问题是您将整个区域与synchronized(this)
同步。这意味着一次只允许一个线程进入循环进行计算。当然,这可能意味着多个线程可以计算不同的部分,但不能同时计算多个线程。这也意味着你的“并行”解决方案并不比一个线程更快。
如果您想并行计算,请查看应涵盖主题的Parallel Matrix Multiplication in Java 6和Fork Join Matrix Multiplication in Java
答案 2 :(得分:2)
线程调度取决于特定的VM实现。在一些实现中,线程将继续运行,直到它以某种方式阻塞或被更高优先级的线程抢占。在您的情况下,所有线程都具有相同的优先级,因此进入synchronized
块的第一个线程永远不会阻塞,它不会被抢占。一些调度程序实现了优先级老化,这样一个饥饿的线程最终会优先级增加,但是你可能运行时间不够长就不会产生影响。
在Thread.yield()
块结束后添加synchronized
来电。这告诉调度程序选择要运行的新线程(可能是同一个,但可能是另一个)。
答案 3 :(得分:1)
你的run函数有第一个获取锁定的线程在仍然拥有锁的情况下完成所有工作。对于下一行,也许另一个线程将获得锁定,但它将阻止所有其他线程,直到完成。
我要做的是拥有与行数相同的布尔数组,并使用这些布尔值来处理处理每一行的任务。它将类似于以下伪代码:
//before creating the threads, pre-fill BoolList with trues
function run()
{
while (true)
{
lock(BoolList)
{
//find first true value and set it to false
//if no true found, return
}
//do the actual math of multiplying the row we claimed above
}
}
另外请记住,创建新线程的开销足以使多线程化程序只适用于大型矩阵。
答案 4 :(得分:1)
正如 mru 在他的评论中已经说明的那样,问题是所有行计算都在“synchronized(this)”块内执行。因此,所有线程将在下一个开始之前等待处理一行,同一个线程总是获取锁定可能是优化的结果,因为你几乎使计算单线程。您可以考虑仅决定在synchronized块中处理哪一行:
int rowToProcess;
synchronized (this) {
if (i < number of columns in final matrix){
rowToProcess = i;
i++;
}
else
return;
}