执行处于非同步部分时线程进入阻塞状态的原因

时间:2016-04-27 06:15:44

标签: java multithreading

我有两个线程在运行:主线程和子线程。他们所做的活动是:

  1. 子线程计算1到100的总和。
  2. 主线程在孩子计算完毕后打印总和(所以我使用了waitnotify)。它还输出子线程状态。
  3. 以下是代码部分。

    class MainThread {
        public static void main(String[] args) throws InterruptedException {
            ThreadB b = new ThreadB();
            b.setName("Child thread: ");
            b.start();
    
            synchronized(b) {
                System.out.print("\nwaiting for b to complete\n");
    
                b.wait();
    
                System.out.print("\nno more wait .........................\n");
                for (int i = 0; i < 1000; i++)
                    System.out.print("\n" + b.getName() + "'s state:" + b.getState());
            }
            Thread.sleep(1000);
    
            System.out.print("\n" + b.getName() + "state:" + b.getState());
            System.out.print("\ntotal in " + Thread.currentThread().getName() + "is :" + b.total);
        }
    }
    
    class ThreadB extends Thread {
    
        int total;
    
        public  void run() {
            synchronized(this) {
                for (int i = 0; i < 100; i++)
                    total += i;
                System.out.println(this.getName() + "finished sum, lets notify other objects");
    
                notify();
                System.out.println(this.getName() + " done with notify and sync block\n");
            }
    
            System.out.println(this.getName() + " : entered non critical section"); 
            for(int j = 0; j < 1000; j++) {
                System.out.print("Hi ");
            }
        }
    }
    
    1. Child完成了其synchronized块并通知了主线程。甚至它已经启动了非同步的部分,只是在循环中打印"Hi "
    2. 现在通知主线程后,恢复工作并打印孩子的状态。
    3. 但是我的观察结果是:在执行"Hi "(对于非同步块中的循环)时,有时孩子的状态为RUNNABLE,而且大部分时间都是BLOCKED。例如:

      Child thread: 's state:RUNNABLE Hi Hi Hi 
      
      Child thread: 's state:BLOCKED Hi Hi Hi Hi    
      

      在执行非同步块时,导致子线程进入BLOCKED状态的原因是什么?

4 个答案:

答案 0 :(得分:4)

两个线程无法同时打印到同一设备。如果他们尝试,其中一个将被阻止。

答案 1 :(得分:0)

这两个线程在ThreadB上没有相互同步,而是在System.out Printstream的write方法上同步,该方法有一个synchronized块,因此一次只有一个线程可以写入流。

public void write(int b) {
    try {
        synchronized (this) {
            ensureOpen();
            out.write(b);
            if ((b == '\n') && autoFlush)
                out.flush();
        }
    }

答案 2 :(得分:0)

我不确定我完全理解你的问题。你是说儿童线程挂起? (即,你是说它永远被阻止?)如果是这样,那可能是丢失的通知

您的程序包含数据竞争。在主线程调用b.start()之后,两个线程竞争在新ThreadB对象上进行同步。如果ThreadB线程赢得比赛,它将进入同步块,最多计数100,调用this.notify()然后退出。

只要ThreadB线程退出synchronized块,MainThread线程就可以进入其synchronized块,并呼叫b.wait()b.wait()调用永远不会返回,因为没有其他线程可以调用b.notify()ThreadB线程已经完成并且已经消失了。

如果没有其他线程已经在等待,则对b.notify()的调用根本不会执行任何内容。对象b不记得已通知。

wait()notify()方法是低级基元,旨在以非常特定的方式使用。有关详细信息,请参阅Oracle Guarded Blocks Tutorial

答案 3 :(得分:-1)

有时ThreadB的运行速度比主线程快,并首先获得同步(此)。在这种情况下,它应该适用于您的方案。但有时主线程在同步(b)中运行得更快。然后ThreadB被主线程阻止,而主线程又等待来自b的通知。 尝试从主线程中删除同步块,仅在b.wait()上进行中继。