日期:2014-05-16  浏览次数:20587 次

在Linux下多线程的问题
最近做的程序出现了一个多线程的问题,肯定是同步引起的,但是我个人还是不太理解,因为同样一段代码在Windows下运行正常,在Linux下就处理一堆问题了。

程序思想是这样子的,有多个任务需要处理,每次主线程处理一个任务,并把这个任务分成多个子任务让子线程去处理。在主线程中启动多个子线程去做事情,并注册自己。主线程就不断的检查是所有线程已经注销了,子线程做完事情后,会向主线程销自己,然后结束。

代码如下:

public class TaskDispatchController
{
    Set threadSet = new HashSet();
    /**
    *  启动子线线程做事情
    */
    private void startThread()
    {
        //重用线程对象
        ImportThread[] thread = importTool.getImportThread();
        threadList = new ArrayList(thread.length);
        for (int i = 0; i < thread.length; i++)
        {
            ImportThread impThread = thread[i];
            //子线程注册自己
            threadSet.add(impThread);
            impThread.setController(this);
            new Thread(impThread).start();
        }
        checkQuite();
    }
    
    /**
    *  注销自己,并唤醒主线程检查是否可以退出
    */
    public synchronized void unregister(ImportThread thread)
    {
       threadSet.remove(thread);
       notifyAll();
    }

    /**
    *
    */
    private synchronized void checkQuite()
    {
        try
        {
            while (!threadSet.isEmpty())
            {
                wait();
            }
            backThread();
        }
        catch (InterruptedException e)
        {
            throw new ThreadOperationException("main thread exception", e);
        }
        finally
        {
            close();
        }
    }
}

public class ImportThread
{
    TaskDispatchController  taskDispatchController = null;
    public void setController(TaskDispatchController  taskDispatchController )
    {
         this.taskDispatchController = taskDispatchController ;
    }
     public void run()
     {
	if (taskDispatchController == null)
	{
	    return;
	}
         //做某事情
         [b]taskDispatchController.doXXX();[/b]	
         while (true)
	{
	    // do something
         }
         //执行完事情后结束
	release();
     }
     
     
     private void release()
     {
	if (taskDispatchController != null)
	{
	    taskDispatchController .unregister(this);
	}
         [b]taskDispatchController == null;[/b]    
     }
}





这段代码在Windows下,双核CPU的环境中做事不会出现错误,且可以启动多个子线程。但在Linux环境下,就启动一个子线程就会出问题,会出现死锁,并且是销在TaskDispatchController的startThread方法,感到很奇怪。

解决方法:随后,我将startThread线程,并标记为同步,并且要所有子线程启动后,子线程才能注销自己。以避免子线程跑得太快了,就结束自己了(这是领导认为的问题所在,但我个人不这么认为,看代码就明白了)。

修改了之后仍然出现死锁。原因是:在子线程run中调用taskDispatchController.doXXX()的时候报NullPointerException,导致子线程没有注销自己,主线程处于一直等待状态。

解决方法:因为我在线程结束的时候将taskDispatchController = null了(也不知道当时是怎么考虑的,其实没有必要设置为null),所以我将release方法中这一行删去。
并修改TaskDispatchController.unregister方法
  public synchronized void unregister(ImportThread thread)
  {
       threadSet.remove(thread);
       thread.setController(null);
       notifyAll();
  }


现在程序在Linux下能正常运行,但其中的原因还是不得而知,希望哪位指点指点
1 楼 dennis_zane 2008-09-11  
第一次遇到的问题并不是什么死锁,是主线程wait没有被唤醒,问题在于你的HashSet在添加元素的时候没有加锁,HashSet不是线程安全的,你remove和判断isEmpty都记的synchronized,怎么add的时候却忘记呢,导致isEmpty返回的值不是真实值,主线程一直没有被唤醒,特别是在线程多并发大的情况下。你可以试试在windows上开5000个线程试试,在我机器上马上复现了你的现象。因此你的领导说startThread加锁同步是没错的,就是解释错了。

同步后出现的空指针问题,想了下,我的理解还是错误,也没有复现你的场景。你说“并且要所有子线程启动后,子线程才能注销自己”,不知道怎么修改的,我想看看,可能是你修改的部分出问题了。
2 楼 dennis_zane 2008-09-11  
而且我觉的你的代码完全可以用CountDownLatch+ExecutorService改写,更漂亮,更简洁。
3 楼 卒子99 2008-09-12  
dennis_zane 写道
同步后出现的空指针问题,想了下,我的理解还是错误,也没有复现你的场景。你说“并且要所有子线程启动后,子线程才能注销自己”,不知道怎么修改的,我想看看,可能是你修改的部分出问题了。

我是加了一个变量 来控制的

voliate boolea staredAll =