在Java中,每个应用程序最少有一个执行线程,运行程序时,JVM负责调用main()方法的执行线程。
当全部的非守护线程执行结束时,Java程序才算结束。从输出中也可以看到,主程序输出main thread end后,其他程序还是继续执行,直到执行结束。
需要注意的是,如果某个线程调用System.exit()指示终结程序,那么全部的线程都会结束执行。
线程中断、睡眠、设置优先级
下面的示例中,NumberGenerator中首先创建numberGenetorThread线程,并设置优先级,启动线程后,一直循环运行,打印出number的值,直到5毫秒后主线程调用interrupt()方法让其中断,numberGenetorThread线程其跳出while循环。首次调用方法isInterrupted()返回值为true,表示线程已中断。
需要注意的是,interrupt()方法测试当前线程是否已经中断,线程的中断状态也由该方法清除。换句话说,如果连续两次调用该方法,则第二次调用将返回 false。大家可以打开下面的注释去测试。
package com.molyeo.java.concurrent;
/**
* Created by zhangkh on 2018/8/23.
*/
public class ThreadTest2 {
public static void main(String[] args) throws InterruptedException {
Thread numberGenetorThread = new NumberGenerator(0);
numberGenetorThread.setPriority(Thread.MAX_PRIORITY);
numberGenetorThread.start();
Thread.sleep(5);
numberGenetorThread.interrupt();
System.out.println("first interrupt,isInterrupted=" + numberGenetorThread.isInterrupted());
//
Thread.sleep(5);
//
numberGenetorThread.interrupt();
//
System.out.println("second interrupt,isInterrupted=" + numberGenetorThread.isInterrupted());
}
}
class NumberGenerator extends Thread {
private int number;
public NumberGenerator(int number) {
this.number = number;
}
@Override
public void run() {
while (!isInterrupted()) {
System.out.println("number is " + number);
number++;
}
System.out.println("NumberGenerator thread,isInterrupted= " + this.isInterrupted());
}
}
程序部分输出如下:
number is 96
number is 97
NumberGenerator thread,isInterrupted= true
first interrupt,isInterrupted=true
ThreadLocal
定义和作用
ThreadLocal称线程本地变量,并不是为了解决共享对象的多线程访问的问题的,因为如果ThreadLocal.set()放进去的本来就是多线程共享的同一个对象的话,线程通过ThreadLocal.get()方法得到的还是共享对象本身,依旧存在并发访问的问题。其是每个线程所单独持有的,主要是提供了保持对象的方法和避免参数传递,以方便对象的访问。
每个线程中都有一个自己的ThreadLocalMap类对象,可以将线程自己的对象保持到其中,各管各的,线程可以正确的访问到自己的对象。
将一个共用的ThreadLocal静态实例作为key,将不同对象的引用保存到不同线程的ThreadLocalMap中,然后在线程执行的各处通过这个静态ThreadLocal实例的get()方法取得自己线程保存的那个对象,避免了将这个对象作为参数传递的麻烦。
程序运行时,每个线程都保持对其线程局部变量副本的隐式引用,只要线程是活动的并且 ThreadLocal 实例是可访问的;在线程消失之后,其线程局部实例的所有副本都会被垃圾回收(除非存在对这些副本的其他引用)。
使用示例
如下我们创建ThreadLocal的实例stringLocal,分别在主线程和子线程中设置其值为当前线程名字。查看输出的结果可以看到线程间彼此不干扰,各自输出自己设置的值。
package com.molyeo.java.concurrent;
/**
* Created by zhangkh on 2018/8/24.
*/
public class ThreadLocalDemo {
public static void main(String[] args) throws InterruptedException {
ThreadLocal<String> stringLocal = new ThreadLocal<String>();
stringLocal.set(Thread.currentThread().getName());
System.out.println(String.format("threadName=%10s,threadLocal valaue=%10s",Thread.currentThread().getName(),stringLocal.get()) );
Thread thread1 = new Thread() {
public void run() {
stringLocal.set(Thread.currentThread().getName());
System.out.println(String.format("threadName=%10s,threadLocal valaue=%10s",Thread.currentThread().getName(),stringLocal.get()) );
}
};
thread1.start();
thread1.join();
System.out.println(String.format("threadName=%10s,threadLocal valaue=%10s",Thread.currentThread().getName(),stringLocal.get()) );
}
}
程序输出如下:
threadName=
main,threadLocal valaue=
main
threadName= Thread-0,threadLocal valaue= Thread-0
threadName=
main,threadLocal valaue=
main
源码实现
ThreadLocal有3个成员变量
private final int threadLocalHashCode = nextHashCode();
private static AtomicInteger nextHashCode = new AtomicInteger();
private static final int HASH_INCREMENT = 0x61c88647;
private static int nextHashCode() {
return nextHashCode.getAndAdd(HASH_INCREMENT);
}