4

我有一个简单的程序来生成 CRC32 的输入,它输出一个以 123456 结尾的数字(十六进制)。它在工作线程中进行暴力破解(下一步将是创建多个工作线程)。工作线程将结果发送到队列q

import java.util.*;
import java.util.concurrent.*;
import java.util.zip.*;

class A {
    static final BlockingQueue<List<Byte>> q =
        new LinkedBlockingQueue<List<Byte>>();

    static {
        new Thread() {
            public void run() {
                System.out.println("worker thread starting");
                int len = 1;
                byte[] a = new byte[32];
                for (int i =0; i < 32; i++) a[i]=-128;
                while (true) {
                    CRC32 crc = new CRC32();
                    crc.update(a, 0, len);
                    long x = crc.getValue();
                    if ((x & 0xffffff) == 0x123456) {
                        System.out.println("HA " + Arrays.toString(a) + "; " + x);
                        List<Byte> l = new LinkedList<Byte>();
                        for (int i = 0; i < a.length; i++) l.add(a[i]);
                        System.out.println("A");
                        q.add(l);
                        System.out.println("B"); // never reaches here
                    }
                    // generate next guess
                    boolean c = true;
                    for (int i = 0; i < len && c; i++) {
                        c = ++a[i] == -128;
                        if (c) if (i + 2 > len) len = i + 2;
                    }
                }
            }
        }.start();
    }

    // for stats: amount of inputs found per interval
    static long amount = 3;
    static long interval;
    static {
        // record an initial value for interval, so any code using
        // it doesn't have to have a special case to check whether
        // it's initialized
        long start = System.currentTimeMillis();
        while (q.size() != amount);
        interval = System.currentTimeMillis() - start;
    }

    public static void main(String[] args) {
        System.out.println("main");
    }
}

由于某种原因,它卡住了:

$ javac A.java && java A
worker thread starting
HA [-49, 72, 73, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128]; 1376924758
A

工作线程在发送到队列(q.add(l))时明显被阻止,否则,“B”将被打印到控制台。如果我从队列 ( ) 中注释掉读取的行while (q.size() != amount),它就不再卡住了。我认为这种行为对于无界队列是不可能的(假设其中还没有 2147483647 个元素)。这不是SynchronousQueue,因此无论是否有任何线程从它接收,发送到队列都应该工作。实际上,这种行为似乎与SynchronousQueue仅在没有接收者的情况下发送才有效

为什么工作线程在尝试发送到队列时被阻塞?

4

1 回答 1

4

它在synchronized(A.class)非队列中被阻止。

当主线程启动时,它会加载类 A。为了做到这一点,它会在类本身上同步以有效地加载类。当您启动一个新线程并尝试访问队列时,它将尝试加载该类(因为由于第二个静态块中的忙自旋,该类尚未完成加载)。现在第一个线程拥有类的同步锁,第二个线程将坐在类监视器上,直到第一个线程存在第二个静态(忙自旋)。

运行程序并kill 3查看线程转储。您会注意到监视器在课堂上而不是队列上。

我将尝试说明这一点。

Thread-1
 Load Class A
  synchronized(A.class){
    static: create new Thread (call it Thread-2)
    static: busy spin while (q.size() != amount); 

  }

Thread-2 (after first static)
  run(){
     A.q 
      Load Class A
      synchronized(A.class){ //block until Thread-1 releases the lock
           A.q.add(..);
      }
  } 
于 2013-07-01T15:55:42.360 回答