守护线程:
Java中有两种线程,一种是用户线程,另一种是守护线程。
用户线程是指用户自定义创建的线程(相当于主线程创建的),主线程停止,用户线程不会停止
守护线程当进程不存在或主线程停止,守护线程也会被停止。(例如gc线程)
使用setDaemon(true)方法设置为守护线程
The Java Virtual Machine exits when the only threads running are all daemon threads.
当运行的唯一线程都是守护进程线程时,Java虚拟机退出。
package com.jinglitong.shop.controller;
import java.util.concurrent.TimeUnit;
public class DaemonThread {
public static void main(String[] args) {
Runtime.getRuntime().addShutdownHook(new Thread(()->System.out.println("JVM 退出了")));
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
while (true) {
try {
Thread.sleep(100);
} catch (Exception e) {
// TODO: handle exception
}
System.out.println("我是子线程...");
}
}
});
thread.setDaemon(true);
thread.start();
for (int i = 0; i < 5; i++) {
try {
//TimeUnit.SECONDS.sleep(100);
Thread.sleep(100);
} catch (Exception e) {
}
System.out.println("我是主线程"+i);
}
System.out.println("主线程执行完毕!");
}
}
由于是守护线程,所以主线程执行完毕后,只剩下守护线程,jvm退出。
非守护线程,则线程会一直执行,不会退出。
//thread.setDaemon(true);
守护线程使用场景:
守护线程拥有自动结束自己生命周期的特性,而非守护线程不具备这个特点。
JVM 中的垃圾回收线程就是典型的守护线程,如果说不具备该特性,会发生什么呢?
当 JVM 要退出时,由于垃圾回收线程还在运行着,导致程序无法退出,这就很尴尬了!!!由此可见,守护线程的重要性了。
通常来说,守护线程经常被用来执行一些后台任务,但是呢,你又希望在程序退出时,或者说 JVM 退出时,线程能够自动关闭,此时,守护线程是你的首选。
线程的状态:
线程从创建、运行到结束总是处于下面五个状态之一:新建状态、就绪状态、运行状态、阻塞状态及死亡状态。
新建状态
当用new操作符创建一个线程时, 例如new Thread(r),线程还没有开始运行,此时线程处在新建状态。 当一个线程处于新生状态时,程序还没有开始运行线程中的代码
就绪状态
一个新创建的线程并不自动开始运行,要执行线程,必须调用线程的start()方法。当线程对象调用start()方法即启动了线程,start()方法创建线程运行的系统资源,并调度线程运行run()方法。当start()方法返回后,线程就处于就绪状态。
处于就绪状态的线程并不一定立即运行run()方法,线程还必须同其他线程竞争CPU时间,只有获得CPU时间才可以运行线程。因为在单CPU的计算机系统中,不可能同时运行多个线程,一个时刻仅有一个线程处于运行状态。因此此时可能有多个线程处于就绪状态。对多个处于就绪状态的线程是由Java运行时系统的线程调度程序(thread scheduler)来调度的。
运行状态
当线程获得CPU时间后,它才进入运行状态,真正开始执行run()方法.
阻塞状态
线程运行过程中,可能由于各种原因进入阻塞状态:
A、线程执行了Thread.sleep(int millsecond);方法,当前线程放弃CPU,睡眠一段时间,然后再恢复到就绪状态
B、线程执行一段同步代码,但是尚且无法获得相关的同步锁,只能进入阻塞状态,等到获取了同步锁,才能回复执行。
C、线程执行了一个对象的wait()方法,直接进入阻塞状态,等待其他线程执行notify()或者notifyAll()方法。
D、线程执行某些IO操作,因为等待相关的资源而进入了阻塞状态。比如说监听system.in,但是尚且没有收到键盘的输入,则进入阻塞状态。
死亡状态
有两个原因会导致线程死亡:
1) run方法正常退出而自然死亡,
2) 一个未捕获的异常终止了run方法而使线程猝死。
为了确定线程在当前是否存活着(就是要么是可运行的,要么是被阻塞了),需要使用isAlive方法。如果是可运行或被阻塞,这个方法返回true; 如果线程仍旧是new状态且不是可运行的, 或者线程死亡了,则返回false.
join()方法作用
join作用是当前线程执行完毕之后才会执行其他线程(主线程或者子线程)。
需求:
创建一个线程,子线程执行完毕后,主线程才能执行。
class JoinThread implements Runnable {
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getName() + "---i:" + i);
}
}
}
public class JoinThreadDemo {
public static void main(String[] args) {
JoinThread joinThread = new JoinThread();
Thread t1 = new Thread(joinThread);
Thread t2 = new Thread(joinThread);
t1.start();
t2.start();
try {
//其他线程变为等待状态,等t1线程执行完成之后才能执行join方法。
t1.join();
} catch (Exception e) {
}
for (int i = 0; i < 100; i++) {
System.out.println("main ---i:" + i);
}
}
}
优先级
现代操作系统基本采用时分的形式调度运行的线程,线程分配得到的时间片的多少决定了线程使用处理器资源的多少,也对应了线程优先级这个概念。在JAVA线程中,通过一个int priority来控制优先级,范围为1-10,其中10最高,默认值为5。下面是源码(基于1.8)中关于priority的一些量和方法。
class PrioritytThread implements Runnable {
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().toString() + "---i:" + i);
}
}
}
public class ThreadDemo4 {
public static void main(String[] args) {
PrioritytThread prioritytThread = new PrioritytThread();
Thread t1 = new Thread(prioritytThread);
Thread t2 = new Thread(prioritytThread);
t1.start();
// 注意设置了优先级, 不代表每次都一定会被执行。 只是CPU调度会有限分配
t1.setPriority(10);
t2.start();
}
}
Yield方法
Thread.yield()方法的作用:暂停当前正在执行的线程,并执行其他线程。(可能没有效果)
yield()让当前正在运行的线程回到可运行状态,以允许具有相同优先级的其他线程获得运行的机会。因此,使用yield()的目的是让具有相同优先级的线程之间能够适当的轮换执行。但是,实际中无法保证yield()达到让步的目的,因为,让步的线程可能被线程调度程序再次选中。
结论:大多数情况下,yield()将导致线程从运行状态转到可运行状态,但有可能没有效果。
Thread.yield( ) 当前线程让出线程,当前线程仍有机会
join :当前线程执行完,再执行其他线程。
Thread.sleep()方法使当前线程进入停滞状态,所以执行sleep()的线程在指定的时间内肯定不会执行, 同时sleep函数不会释放锁资源,这两个的区别在于yield只能是同级,或者高级优先执行,而sleep可以低级,同级都可以有优先执行机会。
void interrupt() 中断线程。
wait(),notify()方法
从Object类继承来的方法 void notify() void wait()
顺便说下Object 类的wait()方法和notify()方法,注意这个不是Thread类的方法;还有这两个方法主要是操作锁标志的,所以只能在synchronized方法或者synchronized block中
使用;wait()方法在睡眠的时候会放开锁,给其他的线程使用。wait()有两种方式获得锁:1.wait(longtimeout),通过设定时间来获得锁,值得注意的是,timeout这个时间到了以后,它不会立即醒来,而是要看那个正在使用这把锁的线程是否结束。2.通过notify()的方法,通知需要这把锁的wait(),使之唤醒,还有wait 方法也要和sleep方法区别,上面说到sleep方法是不释放锁资源的。