一,构造方法
方法 | 说明 |
Thread() | 创建线程对象 |
Thread(Runnable target) | 使用 Runnable 对象创建线程对象 |
Thread(String name) | 创建线程对象,并命名 |
Thread(Runnable target, String name) | 使用 Runnable 对象创建线程对象,并命名 |
Thread(ThreadGroup group, Runnable target) | 线程可以用来被分组管理,分好的组即线程组 |
举例:Thread(Runnable target, String name)
public class ThreadDemo5 {
public static void main(String[] args) {
Thread t = new Thread(() ->{
while(true){
//想要执行的代码
}
},"线程名");
t.start();
}
}
执行代码,使用 JConsole 工具查看线程,发现线程名就是进程所设置的名字。
当我们没有手动设置线程名时,系统会自动为我们设置一个,默认是按照Thread-0,1,2....
线程名是可以重复的。
二,常见属性
属性 | 获取方法 |
ID | getld() |
名称 | getName() |
状态 | getState() |
优先级 | getPriority() |
是否后台线程 | isDaemon() |
是否存活 | isAlive() |
是否被中断 | isInterrupted() |
1. getld()
获取线程 ID。ID 是 JVM 生成的身份标识,具有唯一性。
2. getName()
获取线程名。一个进程的不同线程的线程名可能是一样的。
3. getState()
获取线程状态。Java中对线程状态又进行进一步的区分,比系统原生状态更丰富。
4. getPriority()
获取线程的优先级,在 Java 中设置优先级,会对内核调度器产生一些影响,但是由于系统的随即调度,所以这个影响并不是很明显。
5. isDaemon()
是否为守护线程,也可以叫后台线程。相应的,也有前台线程。
前台线程的运行会阻止进程结束,但是守护线程(后台线程)不会。
一般我们写入的代码默认为前台线程。
演示代码:
public class ThreadDemo5 {
public static void main(String[] args) {
Thread t = new Thread(() ->{
while(true){
System.out.println("t 线程");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
t.start();
}
}
运行结果:一直打印 " t 线程 "
上述代码一共有两个线程:main 线程和 t 线程。两个线程均为前台线程,所以当 main 线程执行结束时,t 线程阻止进程结束,还会继续执行。
public class ThreadDemo5 {
public static void main(String[] args) {
Thread t = new Thread(() ->{
while(true){
System.out.println("t 线程");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
/*setdaemon必须设置在start之前,
true为后台,不会阻止进程结束
非true为前台,会阻止进程结束*/
t.setDaemon(true);
t.start();
}
}
运行结果:
上述代码我们将 t 线程设置为后台进程,所以当 main 线程执行结束时,t 线程不会阻止进程结束,进程就此结束。
6. isAlive()
判断内核中的线程是否还存活。
public class ThreadDemo6 {
public static void main(String[] args) throws InterruptedException {
Thread t = new Thread( () -> {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
System.out.println(t.isAlive());
t.start();
System.out.println(t.isAlive());
//时间参数要大于上面的,确保线程执行完毕
Thread.sleep(2000);
System.out.println(t.isAlive());
}
}
运行结果:
原因解释:
Java 代码中定义的线程对象实例的生命周期和内核中的 PCB 生命周期完全不一样。
代码运行到 Thread t = new Thread() 时,此时只有一个 t 对象,但是内核 PCB 还没有这个线程,所以此时运行结果为 “false”;当代码运行了 t.start() 之后,才在内核中创建出这个 PCB ,所以此时运行结果为 “true”;最后线程执行完 run() ,内核中的线程也就结束了,PCB 释放,所以此时运行结果为 “false”。
7. isInterrupted()
获取线程的中断标志。
public class Threaddemo7 {
public static void main(String[] args) throws InterruptedException {
Thread t = new Thread( () -> {
while (!Thread.currentThread().isInterrupted()) {
System.out.println("Thread is running");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
//屏蔽是防止控制台显示异常信息
//e.printStackTrace();
//因为 t.interrupt() 会提前打断sleep的休眠期,导致清除 interrupt 的标志位
break;
}
}
System.out.println("Thread is stopped");
});
t.start();
Thread.sleep(3000);
System.out.println("调用interrupt()前:"+t.isInterrupted());
System.out.println("线程中断");
t.interrupt();
System.out.println("调用interrupt()后:"+t.isInterrupted());
}
}
运行结果:
三,常用方法
1. start()
作用:线程对象只有调用了 start() 方法才会真正创建一个线程(内核中创建 PBC )。
未调用 start() :
public class Demo1 {
public static void main(String[] args) {
Thread t = new Thread(() -> {
System.out.println("这是 t 线程");
});
System.out.println("这是 main 线程");
}
}
// 运行结果:这是 main 线程
调用 start() :
public class Demo1 {
public static void main(String[] args) {
Thread t = new Thread(() -> {
System.out.println("这是 t 线程");
});
t.start();
System.out.println("这是 main 线程");
}
}
//运行结果:
//这是 main 线程
//这是 t 线程
调用两次 start() :
public class Demo1 {
public static void main(String[] args) {
Thread t = new Thread(() -> {
System.out.println("这是 t 线程");
});
t.start();
t.start();
}
}
运行结果:
代码报错:非法的线程状态。
总结:
1). 线程对象只有调用了 start() 方法后才会创建出真正的线程。
2). 一个线程对象只能调用一次 start() 方法。
2. interrupt()
作用:interrupt() 中断线程,中断线程操作实质上是修改了一下中断标示位为 true ,核心是让 run() 方法执行结束(具体实践由代码决定)。
引入标志位中断代码:
public class Demo1 {
//引入一个标志位
private static boolean isQuit = true;
public static void main(String[] args) {
Thread t = new Thread(() -> {
//当 isQuit 为 flase 时,while 循环结束
while (isQuit) {
System.out.println("Thread is running");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("Thread is over");
});
t.start();
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("让 t 线程结束");
isQuit = false;
}
}
运行结果:
注意:main 线程想要让 t 线程结束的前提是代码逻辑对此有所支持,并不是任何代码都可以实现的。
但是有时候再去自定义一个标志位有一点麻烦,此时我们就可以用 interrupt() 方法,使用 Thread 实例内部自带的标志位代替自定义的标志位。
如何使用一个 interrupt() 方法实现:
public class Threaddemo7 {
public static void main(String[] args) throws InterruptedException {
Thread t = new Thread( () -> {
while (!Thread.currentThread().isInterrupted()) {
System.out.println("Thread is running");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
//屏蔽是防止控制台显示异常信息
//e.printStackTrace();
//因为 t.interrupt() 会提前打断sleep的休眠期,导致清除 interrupt 的标志位
break;
}
}
System.out.println("Thread is stopped");
});
t.start();
Thread.sleep(3000);
System.out.println("线程中断");
t.interrupt();
}
}
运行结果:
3. join()
作用:影响线程的先后顺序(固定条件的等待),比如现在有 t1 和 t2 两个线程,如果 t2 线程调用 t1.join 方法,则必学先让 t1 线程执行完毕,才能执行 t2 线程, join() 是可能阻塞 t2 线程,让其暂时不参与 CPU 调度。确定的不是执行顺序,而是结束顺序。
4. sleep()
令当前线程至少休眠 n 毫秒(固定时间的等待,防止无休止的休眠),其中 1000ms = 1s
5. currentThread()
获取当前线程状态。