一个线程两次调用start()方法会出现什么情况? - 《java核心技术》笔记

java的线程不允许启动两次,第二次调用会抛出IllegalThreadStateException,是一种运行时异常。

线程间状态流转:
3169b7ca899afeb0359f132fb77c29dc

  • 就绪:该线程已经在JVM,可能正在运行,也可能正在等待系统分配给它CPU时间片,在就绪队列中排队;
  • 阻塞:等待monitor lock;
  • 等待:正在等待其他线程采取某些操作,比如消费者模式。

需要注意的一些点:

  1. 守护线程必须在线程启动之前设置
Thread daemonThread = new Thread();
daemonThread.setDaemon(true);
daemonThread.start();

2.supurious wakeup问题,线程等待可能在没有任何线程广播或者发出信号的情况下被唤醒,所以建议采用以下模式书写:

// 推荐
while ( isCondition()) {
waitForAConfition(...);
}

// 不推荐,可能引入 bug
if ( isCondition()) {
waitForAConfition(...);
}

3.慎用ThreadLocal,因为在其整个线程生命周期内有效,所以可以方便的在一个线程关联的不同业务模块之间传递信息,比如事务ID、Cookie等上下文相关信息。
其内部条目使用弱引用:

static class ThreadLocalMap {
	static class Entry extends WeakReference<ThreadLocal<?>> {
    	/** The value associated with this ThreadLocal. */
    	Object value;
    	Entry(ThreadLocal<?> k, Object v) {
        	super(k);
    	value = v;
    	}
      }
   // …
}

set的例子:

private void set(ThreadLocal<?> key, Object value) {
	Entry[] tab = table;
	int len = tab.length;
	int i = key.threadLocalHashCode & (len-1);

	for (Entry e = tab[i];; …) {
    	//…
    	if (k == null) {
// 替换废弃条目
        	replaceStaleEntry(key, value, i);
        	return;
    	}
       }

	tab[i] = new Entry(key, value);
	int sz = ++size;
//  扫描并清理发现的废弃条目,并检查容量是否超限
	if (!cleanSomeSlots(i, sz) && sz >= threshold)
    	rehash();// 清理废弃条目,如果仍然超限,则扩容(加倍)
}

通常弱引用都会和引用队列配合清理机制使用,但是ThreadLocal没有这么做,废弃项目的回收需要依赖于显示地触发,否则就要等待线程结束,进而回收相应ThreadLocalMap,所以这就是很多OOM的来源。

comments powered by Disqus