文章目录
线程是为了解决并发编程引入的机制, 线程相比于进程来说,更轻量
进程是操作系统资源分配的基本单位 , 线程是操作系统调度执行的基本单位
1、创建线程的五种方法
1.1 继承 Thread类 ,重写 run方法
class MyThread extends Thread{
@Override
public void run() {
while (true){
System.out.println("hello thread");
//为了让这里的打印放慢点,方便观察, 加个sleep
try {
Thread.sleep(1000); //1秒
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
}
public static void main(String[] args) {
Thread thread=new MyThread(); //父类引用指向子类实例 子类的方法会被动态绑定的机制,被调用到
thread.start(); //线程中的特殊方法, 创建了一个新的线程 (启动一个线程), 新的线程负责执行thread.run()
}
1.2 实现 Runnable 接口, 重写 run方法
Runnable接口的作用,是描述一个 "要执行的任务" , run方法就是任务的执行细节
class MyRunnable implements Runnable{
@Override
public void run() {
System.out.println("hello world");
}
}
public static void main(String[] args) {
//这只是描述了个任务
Runnable runnable=new MyRunnable();
//把任务交给线程来执行
Thread thread=new Thread(runnable);
thread.start();
}
1.3 使用匿名内部类,创建 Thread子类对象
public static void main(String[] args) {
Thread thread=new Thread(){ //匿名内部类: 创建了一个Thread的子类,(该子类没有名字,所以叫“匿名”)
@Override
public void run() {
System.out.println("hello");;
}
};
thread.start();
}
1.4 使用匿名内部类, 创建 Runnable子类对象
public static void main(String[] args) {
Thread thread=new Thread(new Runnable() {
@Override
public void run() {
System.out.println("hello");
}
});
thread.start();
}
1.5 使用 lambda 表达式 ,创建 Runnable子类对象
lambda 表达式: ()->{函数体}
Lambda就是个匿名函数(没有名字的函数),用一次就销毁,
public static void main(String[] args) {
Thread thread=new Thread(()->{
System.out.println("hello thread");
});
thread.start();
}
2. Thread类 及 常见方法
Thread 类是 JVM 用来管理线程的一个类,换句话说,每个线程都有一个唯一的 Thread 对象与之关联。
Thread 类的对象就是用来描述一个线程执行流的,JVM 会将这些 Thread 对象组织起来,用于线程调度,线程管理。
2.1 Thread 的常见构造方法
方法 | 解释 |
---|---|
Thread() | 创建线程对象 |
Thread(Runnable target) | 使用 Runnable 对象创建线程对象 |
Thread(String name) | 创建线程对象,并命名 |
Thread(Runnable target,String name) | 使用 Runnable 对象创建线程对象,并命名 |
2.2 Thread 的几个常见属性
属性 | 获取方法 |
---|---|
ID | getId() |
名称 | getName() |
状态 | getState() |
优先级 | getPriority() |
是否后台线程 | isDaemon() |
是否存活 | isAlive() |
是否被中断 | isInterrupted() |
2.3 启动一个线程-start()
前面已经介绍了如何通过复写 run方法创建一个线程对象,但线程对象被创建出来并不意味着线程就开始运行了。
- 覆写 run 方法是提供给线程要做的事情的指令清单
- 线程对象可以认为是把 李四、王五叫过来了
- 而调用 start() 方法,就是喊一声:”行动起来!“,线程才真正独立去执行了。
调用 start()方法,才真的在操作系统的底层创建出一个线程。
2.4 中断一个线程
一个线程一旦进入到工作状态,他就会按照任务的步骤去进行工作,不完成时不会结束的,但有时我们需要增加一些机制,涉及到了停止线程的方式。
常见的有以下两种方式:
通过共享的标记来进行沟通
调用 interrupt()方法来通知
示例-1: 使用自定义的变量来作为标志位(需要给标志位上加 volatile 关键字)
public class ThreadDemo {
private static volatile boolean flag=true;
public static void main(String[] args) throws InterruptedException {
Thread thread=new Thread(()->{
while (flag){
System.out.println("hello thread");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
});
thread.start();
Thread.sleep(3000);
flag=false; //通过标志位 使线程中断结束
}
}
示例-2:使用 Thread.interrupted() 或者 Thread.currentThread().isInterrupted() 代替自定义标志位.
Thread 内部包含了一个 boolean 类型的变量作为线程是否被中断的标记.
方法 | 说明 |
---|---|
public void interrupt() | 中断对象关联的线程,如果线程正在阻塞,则以异常方式通知,否则设置标志位 |
public static boolean interrupted() | 判断当前线程的中断标志位是否设置,调用后清除标志位 |
public boolean isInterrupted() | 判断对象关联的线程的标志位是否设置,调用后不清除标志位 |
- 使用 thread 对象的 interrupted() 方法通知线程结束
//中断线程
public class ThreadDemo8 {
public static void main(String[] args) throws InterruptedException {
Thread thread=new Thread(()->{
while (!Thread.currentThread().isInterrupted()){
System.out.println("hello thread");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
// try {
// Thread.sleep(500);
// } catch (InterruptedException ex) {
// ex.printStackTrace();
// }
break;
}
}
});
thread.start();
Thread.sleep(3000);
thread.interrupt();
}
}
thread 收到通知的方式有两种:
-
如果线程因为调用 wait/join/sleep 等方法而阻塞挂起,则以 InterruptedException 异常的形式通知,清除中断标志
- 当出现 InterruptedException 的时候, 要不要结束线程取决于 catch 中代码的写法. 可以选择忽略这个异常, 也可以跳出循环结束线程.
-
否则,只是内部的一个中断标志被设置,thread 可以通过
- Thread.interrupted() 判断当前线程的中断标志被设置,清除中断标志
- Thread.currentThread().isInterrupted() 判断指定线程的中断标志被设置,不清除中断标志
这种方式通知收到的更及时,即使线程正在sleep也可以马上收到
2.5 等待一个线程-join()
方法 | 说明 |
---|---|
public void join() | 等待线程结束 |
public void join(long millis) | 等待线程结束,最多等 millis 毫秒 |
public void join(long millis) | 同理,但可以更高精度 |
public class ThreadDemo {
public static void main1(String[] args) {
Thread thread=new Thread(()->{
for (int i = 0; i <3 ; i++) {
System.out.println("hello thread");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
thread.start();
try {
thread.join(); //让main线程等待thread线程结束,(等待t的run执行完)
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
2.6 获取当前线程
方法 | 说明 |
---|---|
public static Thread currentThread(); | 返回当前线程对象的引用 |
public class ThreadDemo {
public static void main(String[] args) {
Thread thread = Thread.currentThread();
System.out.println(thread.getName());
}
}
2.7 休眠当前线程-sleep()
方法 | 说明 |
---|---|
public static void sleep(long millis) throws InterruptedException | 休眠当前线程 millis毫秒 |
public static void sleep(long millis, int nanos) throwsInterruptedException | 可以更高精度的休眠 |
public class ThreadDemo {
public static void main(String[] args) throws InterruptedException {
System.out.println(System.currentTimeMillis());
Thread.sleep(3 * 1000);
System.out.println(System.currentTimeMillis());
}
}