-
每个线程都有优先级,高优先级的线程可能会比优先级低的优先执行。
-
父线程创建子线程后,优先级、是否是守护线程等属性父子线程是一致的。
-
JVM 启动时,通常都启动 MAIN 非守护线程,以下情况,线程会停止
-
退出方法被调用,并且安全机制允许这么做(比如调用 Thread.interrupt 方法);
-
所有非守护线程都消亡,或者从运行的方法正常返回,或者运行的方法抛出了异常;
-
-
每个线程都有名字,多个线程可能具有相同的名字,Thread 有的构造器如果没有指定名字,会自动生成一个名字。
-
继承Thread,实现run方法
public class Main { class Task extends Thread { @Override public void run() { System.out.println(Thread.currentThread().getName()); } } public void test() { new Task().start(); } public static void main(String[] args) { new Main().test(); } }
输出结果为Thread-0,而主线程的名字是main,由此可见,开了一个子线程来执行打印的操作。
-
实现Runnable接口,作为Thread入场
public static void main(String[] args) { Thread thread = new Thread(new Runnable() { @Override public void run() { System.out.println(Thread.currentThread().getName()); } }); // 开一个子线程去执行 thread.start(); // 不会新起线程,是在当前主线程上继续运行 thread.run(); }
要注意区分thread.start()和thread.run(),前者输出Thread0,是开一个子线程去执行。后者输出main,不会新起线程,是在当前主线程上继续运行。
-
start源码
public synchronized void start() { // 如果没有初始化,抛异常 if (threadStatus != 0) throw new IllegalThreadStateException(); //把线程添加到线程组,并通知线程组线程准备启动,线程组里面的未开始的线程数量减1 group.add(this); // started 是个标识符,线程启动之前标识符是 false,发生完成之后变成 true boolean started = false; try { // 创建一个新的线程,执行完成之后,新的线程已经在运行了,既 target 的内容已经在运行了 start0(); // 这里执行的还是主线程 started = true;, } finally { try { // 如果失败,把线程从线程组中删除,nUnstartedThreads++(启动失败线程数加1) if (!started) { group.threadStartFailed(this); } } // Throwable 可以捕捉一些 Exception 捕捉不到的异常,比如说子线程抛出的异常 catch (Throwable ignore) { /* do nothing. If start0 threw a Throwable then it will be passed up the call stack */ } } } // 开启新线程使用的是 native 方法 private native void start0();
-
run源码
// 简单的运行,不创建新的线程,target 是 Runnable public void run() { if (target != null) { target.run(); } }
- NEW 表示线程创建成功,但没有运行,在 new Thread 之后,没有 start 之前,线程的状态都是 NEW;
- 当我们运行 strat 方法,子线程被创建成功之后,子线程的状态变成 RUNNABLE,RUNNABLE 表示线程正在运行中;
- 子线程运行完成、被打断、被中止,状态都会从 RUNNABLE 变成 TERMINATED,TERMINATED 表示线程已经运行结束了;
- 如果线程正好在等待获得 monitor lock 锁,比如在等待进入 synchronized 修饰的代码块或方法时,会从 RUNNABLE 变成 BLOCKED(阻塞状态);
- WAITING 和 TIMED_WAITING 类似,都表示在遇到 Object#wait、Thread#join、LockSupport#park 这些方法时,线程就会等待另一个线程执行完特定的动作之后,才能结束等待,只不过 TIMED_WAITING 是带有等待时间的。
我们默认创建的线程都是非守护线程。创建守护线程时,需要将 Thread 的 daemon 属性设置成 true,守护线程的优先级很低,当 JVM 退出时,是不关心有无守护线程的,即使还有很多守护线程,JVM 仍然会退出,我们在工作中,可能会写一些工具做一些监控的工作,这时我们都是用守护子线程去做,这样即使监控抛出异常,但因为是子线程,所以也不会影响到业务主线程,因为是守护线程,所以 JVM 也无需关注监控是否正在运行,该退出就退出,所以对业务不会产生任何影响。
// 无参构造器,线程名字自动生成
public Thread() {
init(null, null, "Thread-" + nextThreadNum(), 0);
}
//参数为Runnable的构造方法,当线程启动的时候,执行Runnable里面的run方法
public Thread(Runnable target) {
init(null, target, "Thread-" + nextThreadNum(), 0);
}
//这个不是公有构造方法,创建一个线程包含AccessControlContext对象
Thread(Runnable target, AccessControlContext acc) {
init(null, target, "Thread-" + nextThreadNum(), 0, acc, false);
}
// g 代表线程组,线程组可以对组内的线程进行批量的操作,比如批量的打断 interrupt
// target 是我们要运行的对象
// name 我们可以自己传,如果不传默认是 "Thread-" + nextThreadNum(),nextThreadNum 方法返回的是自增的数字
// stackSize 可以设置堆栈的大小
public Thread(ThreadGroup group, Runnable target, String name,
long stackSize) {
init(group, target, name, stackSize);
}
private void init(ThreadGroup g, Runnable target, String name,
long stackSize) {
init(g, target, name, stackSize, null, true);
}
private void init(ThreadGroup g, Runnable target, String name,
long stackSize, AccessControlContext acc,
boolean inheritThreadLocals) {
if (name == null) {
throw new NullPointerException("name cannot be null");
}
this.name = name;
// 当前线程作为父线程
Thread parent = currentThread();
SecurityManager security = System.getSecurityManager();
if (g == null) {
/* Determine if it's an applet or not */
/* If there is a security manager, ask the security manager
what to do. */
if (security != null) {
g = security.getThreadGroup();
}
/* If the security doesn't have a strong opinion of the matter
use the parent thread group. */
if (g == null) {
g = parent.getThreadGroup();
}
}
/* checkAccess regardless of whether or not threadgroup is
explicitly passed in. */
g.checkAccess();
/*
* Do we have the required permissions?
*/
if (security != null) {
if (isCCLOverridden(getClass())) {
security.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION);
}
}
g.addUnstarted();
this.group = g;
// 子线程会继承父线程的守护属性
this.daemon = parent.isDaemon();
// 子线程继承父线程的优先级属性
this.priority = parent.getPriority();
if (security == null || isCCLOverridden(parent.getClass()))
this.contextClassLoader = parent.getContextClassLoader();
else
this.contextClassLoader = parent.contextClassLoader;
this.inheritedAccessControlContext =
acc != null ? acc : AccessController.getContext();
this.target = target;
setPriority(priority);
// 当父线程的 inheritableThreadLocals 的属性值不为空时
// 会把 inheritableThreadLocals 里面的值全部传递给子线程
if (inheritThreadLocals && parent.inheritableThreadLocals != null)
this.inheritableThreadLocals =
ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
/* Stash the specified stack size in case the VM cares */
this.stackSize = stackSize;
/* Set thread ID */
//线程 id 自增
tid = nextThreadID();
}
join 的意思就是当前线程等待另一个线程执行完成之后,才能继续操作。
public static void main(String[] args) throws InterruptedException {
Thread main = Thread.currentThread();
System.out.println(main + " is run");
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + " is run");
try {
System.out.println(Thread.currentThread().getName() + " is sleep");
Thread.sleep(3000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+" is end");
}
});
// 开一个子线程去执行
thread.start();
// 当前主线程等待子线程执行完成之后再执行
thread.join();
System.out.println(main + " is end");
}
/**
*输出
*Thread[main,5,main] is run
*Thread-0 is run
*Thread-0 is sleep
*Thread-0 is end
*Thread[main,5,main] is end
/
执行的结果,就是主线程在执行 thread.join (); 代码后会停住,会等待子线程沉睡 30 秒后再执行,这里的 join 的作用就是让主线程等待子线程执行完成后,再去执行主线程的。
yield 是个 native 方法,底层代码如下
public static native void yield();
意思是当前线程做出让步,放弃当前 cpu,让 cpu 重新选择线程,避免线程过度使用 cpu,我们在写 while 死循环的时候,预计短时间内 while 死循环可以结束的话,可以在循环里面使用 yield 方法,防止 cpu 一直被 while 死循环霸占。
注意,让步不是绝不执行,重新竞争时,cpu 也有可能重新选中自己。
public class Main {
public static void main(String[] args) throws InterruptedException {
Thread t1 = new MyThread("thread1");
Thread t2 = new MyThread("thread2");
t1.start();
t2.start();
}
}
class MyThread extends Thread {
MyThread(String s) {
super(s);
}
public void run() {
for(int i = 1; i <= 20; i++) {
System.out.println(Thread.currentThread().getName() + ":is run" );
if(i % 5 == 0) {//每次被5整除后让出cpu
System.out.println(Thread.currentThread().getName() +" is yield");
Thread.yield();
}
}
}
}
/** 运行结果
thread1:is run
thread2:is run
thread2:is run
thread2:is run
thread2:is run
thread2:is run
thread1:is run
thread2is yield thread2让出cpu,但是cpu重新选择了thread2
thread2:is run
thread2:is run
thread2:is run
thread1:is run
thread1:is run
thread2:is run
thread1:is run
thread1is yield thread1让出cpu,cpu重新选择了thread2
thread2:is run
thread1:is run
thread2is yield thread2让出cpu,cpu重新选择了thread1
thread1:is run
thread2:is run
thread1:is run
thread1:is run
thread1:is run
thread1is yield thread1让出cpu,cpu重新选择了thread2
thread2:is run
thread1:is run
thread1:is run
thread1:is run
thread1:is run
thread1:is run
thread1is yield thread1让出cpu,cpu重新选择了thread2
thread2:is run
thread1:is run
thread1:is run
thread1:is run
thread1:is run
thread2:is run
thread2:is run
thread2is yield thread2让出cpu,但是cpu重新选择了thread2
thread2:is run
thread2:is run
thread2:is run
thread2:is run
thread2:is run
thread2is yield thread2让出cpu,cpu重新选择了thread1
thread1:is run
thread1is yield
/
sleep 也是 native 方法,可以接受毫秒的一个入参,也可以接受毫秒和纳秒的两个入参,意思是当前线程会沉睡多久,沉睡时不会释放锁资源,所以沉睡时,其它线程是无法得到锁的。
接受毫秒和纳秒两个入参时,如果给定纳秒大于等于 0.5 毫秒,算一个毫秒,否则不算。
public static void main(String[] args) {
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + " is sleep 3s");
try {
Thread.sleep(3000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
thread.start();
}
可以打断中止正在运行的线程,如
- Object#wait ()、Thread#join ()、Thread#sleep (long) 这些方法运行后,线程的状态是 WAITING 或 TIMED_WAITING,这时候打断这些线程,就会抛出 InterruptedException 异常,使线程的状态直接到 TERMINATED;
- 如果 I/O 操作被阻塞了,我们主动打断当前线程,连接会被关闭,并抛出 ClosedByInterruptException 异常;
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + " is run");
try {
System.out.println(Thread.currentThread().getName() + " is sleep 30s");
Thread.sleep(30000L);
} catch (InterruptedException e) {
System.out.println(Thread.currentThread().getName() + " is Interrupt");
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " is end");
}
});
thread.start();
Thread.sleep(1000L);
System.out.println("主线程" + Thread.currentThread().getName() + "运行1秒后,打断子线程");
thread.interrupt();
}
/** 输出结果
Thread-0 is run
Thread-0 is sleep 30s
主线程main运行1秒后,打断子线程
Thread-0 is Interrupt
Thread-0 is end
java.lang.InterruptedException: sleep interrupted
at java.lang.Thread.sleep(Native Method)
at thread.Main$1.run(Main.java:17)
at java.lang.Thread.run(Thread.java:748)
/