目录
什么是线程
线程和进程、协程(虚拟线程)的区别
线程和进程的区别
线程和协程的区别
线程的六种状态
1.NEW
2.RUNNABLE
3.BLOCKED
4.WAITING
5.TIME_WAITING
6.TERMINATED
创建线程
方法一:通过继承Thread类
方法二:通过实现Runnable接口
两种方法的区别
线程常用方法
start 与 run 线程执行
sleep 线程休眠
wait 线程等待
join 线程合并
interrupt 线程中断
yield 线程让步
setPriority 线程优先级
setDaemon 设置守护线程
什么是线程
线程是操作系统中的基本执行单元(能够直接执行的最小代码块),它是进程中的⼀个实体,是CPU调度和分派的基本单位。
线程和进程、协程(虚拟线程)的区别
线程和进程的区别
-
在操作系统中,进程是基本的资源分配单位,操作系统通过进程来管理计算机的资源,如CPU、内存、磁盘等。⼀个进程可以包含多个线程,每个线程都可以独立执行不同的任务,但它们共享进程的资源。
线程和协程的区别
-
调度方式上,线程由操作系统调度,切换线程时会涉及上下文切换和内核态的开销;协程由程序调度,在用户态切换,没有上下文切换的开销,性能更高。
-
阻塞与非阻塞方面,线程通常采用阻塞模型;协程是非阻塞的。
-
资源占用方面,线程每个线程需要分配栈空间,且栈大小固定,导致线程资源消耗较大;协程的栈空间可以动态增长,内存开销远小于线程。
总结一下:先有进程,然后进程可以创建线程,线程是依附在进程里面的, 线程里面可以包含多个协程;进程之间不共享全局变量,线程之间共享全局变量,但是要注意资源竞争的问题。
线程的六种状态
1.NEW
初始状态,线程被构建,但是还没有调用start()方法。
2.RUNNABLE
运行状态,Java线程将操作系统中的就绪和运行两种状态统称为"运行中"。
3.BLOCKED
阻塞状态,表示线程阻塞于锁。
4.WAITING
等待状态,表示线程进入等待状态,进入该状态表示当前线程需要其他线程通知(notify或者notifyAll)。
5.TIME_WAITING
超时等待状态,可以指定等待时间自己返回。
6.TERMINATED
终止状态,表示当前线程已经执行完毕。
创建线程
方法一:通过继承Thread类
-
创建一个类继承
Thread
类。 -
重写该类的
run()
方法,将线程要执行的代码放在run()
方法中。 -
创建该类的实例,并调用其
start()
方法来启动线程。
java">class MyThread extends Thread {@Overridepublic void run() {System.out.println("线程开始运行");}
}public class Main {public static void main(String[] args) {MyThread myThread = new MyThread();myThread.start(); }
}
方法二:通过实现Runnable接口
-
创建一个类实现
Runnable
接口。 -
实现
Runnable
接口的run()
方法,将线程要执行的代码放在run()
方法中。 -
创建该类的实例,并将其作为参数传递给
Thread
类的构造函数,创建线程对象。 -
调用线程对象的
start()
方法来启动线程。
java">class MyRunnable implements Runnable {@Overridepublic void run() {System.out.println("线程开始运行");}
}public class Main {public static void main(String[] args) {MyRunnable myRunnable = new MyRunnable();Thread thread = new Thread(myRunnable);thread.start(); }
}
或者更简单的写为:
java"> public static void main(String args[]){Thread thread = new Thread(new Runnable() {@Overridepublic void run() {System.out.println("线程开始运行");}});thread.start();}
两种方法的区别
继承Thread类:简单直接,但Java不支持多继承,如果类已经继承了其他类,则无法再继承Thread类。
实现Runnable接口:更灵活,推荐使用。可以避免Java单继承的限制,适合多个线程共享同一个资源的情况。
线程常用方法
start 与 run 线程执行
start()
和run()
方法都与线程的执行有关,但它们之间有着本质的区别:
-
执行run方法它不会产生新线程,而执行start方法会产生新线程。
-
run方法可以被执行无数次,而star方法只能被执行⼀次,原因就在于线程不能被重复启动。
总结一下:run方法的作用是存放任务代码,而start的方法呢是启动线程。
sleep 线程休眠
sleep()
方法是Thread
类的一个静态方法,用于让当前正在执行的线程暂停执行指定的时间,以便其他线程可以运行。
休眠线程例如:
java">public class SleepExample {public static void main(String[] args) {Thread thread = new Thread(() -> {try {System.out.println("线程开始执行");Thread.sleep(2000); // 睡2秒System.out.println("线程执行完毕");} catch (InterruptedException e) {System.out.println("线程被中断");}});thread.start();}
}
sleep 特点
不释放锁、对中断敏感、释放 CPU。
wait 线程等待
wait()
方法是Object
类的一个方法,用于让当前线程等待,直到其他线程调用同一对象的notify()
或notifyAll()
方法。
wait()
方法通常用于实现线程间的协调和同步,常用于生产者-消费者模型,例如:
java">public class WaitNotifyExample {private static final Object lock = new Object();public static void main(String[] args) {Thread producer = new Thread(() -> {synchronized (lock) {System.out.println("生产者开始生产");// 生产产品...System.out.println("生产者生产完毕");lock.notify(); // 通知消费者}});Thread consumer = new Thread(() -> {synchronized (lock) {try {System.out.println("消费者开始等待");lock.wait(); // 等待生产者生产完毕System.out.println("消费者开始消费");// 消费产品...} catch (InterruptedException e) {System.out.println("消费者被中断");}}});consumer.start();producer.start();}
}
wait 特点
释放锁、对中断敏感、释放 CPU。
注意:
wait()
方法必须在同步块(synchronized block)或同步方法(synchronized method)中调用,否则会抛出IllegalMonitorStateException
异常。因为wait()
方法需要释放对象的锁,而只有在同步块中才能获取对象的锁。
join 线程合并
join()
方法是Thread
类的一个方法,用于等待一个线程终止。当你调用一个线程对象的join()
方法时,当前线程会暂停执行,直到被调用join()
方法的线程执行完毕。
常用于确保执行顺序例如:
java">public class JoinExample {public static void main(String[] args) {Thread thread1 = new Thread(() -> {System.out.println("线程1开始执行");// 模拟耗时操作try {Thread.sleep(2000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("线程1执行完毕");});Thread thread2 = new Thread(() -> {System.out.println("线程2开始执行");try {thread1.join(); // 等待线程1执行完毕} catch (InterruptedException e) {e.printStackTrace();}System.out.println("线程2执行完毕");});thread1.start();thread2.start();}
}
interrupt 线程中断
interrupt()
方法是Thread
类的一个方法,用于中断一个线程。中断是一种协作机制,它不会立即停止线程,而是设置线程的中断状态,线程需要检查这个状态并作出相应的响应。
举个简单的例子:
java">public class InterruptExample {public static void main(String[] args) {Thread thread = new Thread(() -> {while (!Thread.currentThread().isInterrupted()) {// 执行任务...System.out.println("线程正在运行");try {Thread.sleep(1000); // 模拟耗时操作} catch (InterruptedException e) {Thread.currentThread().interrupt(); // 重新设置中断状态break; // 退出循环}}System.out.println("线程被中断,退出执行");});thread.start();try {Thread.sleep(3000); // 让主线程等待一段时间} catch (InterruptedException e) {e.printStackTrace();}thread.interrupt(); // 中断线程}
}
注意:线程通过方法isInterrupted()来进行判断是否被中断,也可以调用静态方法Thread.interrupted()对当前线程的中断标识位进行复位。
yield 线程让步
yield()
方法是Thread
类的一个静态方法,用于提示线程调度器当前线程愿意让出CPU的执行权,以便其他线程可以执行。
举个简单的例子:
java">public class YieldExample {public static void main(String[] args) {Thread thread1 = new Thread(() -> {for (int i = 0; i < 5; i++) {System.out.println("线程1执行:" + i);if (i == 2) {Thread.yield(); // 在i等于2时让出CPU执行权}}});Thread thread2 = new Thread(() -> {for (int i = 0; i < 5; i++) {System.out.println("线程2执行:" + i);}});thread1.start();thread2.start();}
}
注意:yield()方法并不能保证线程⼀定会让出CPU资源,它只是⼀个提示,告诉调度器当前线程愿意让出CPU资源。具体是否让出CPU资源,还是由调度器决定。
setPriority 线程优先级
在Java线程中,通过一个整型成员变量priority来控制优先级,优先级的范围从1~10,在线程构建的时候可以通过setPriority(int)方法来修改优先级,默认优先级是5,优先级高的线程分配CPU时间片的数量要多于优先级低的线程。
修改线程优先级例如:
java">Thread thread = new Thread(new Runnable() {@Overridepublic void run() {System.out.println("线程正在运行");}
});thread.setPriority(Thread.MAX_PRIORITY); // 设置线程优先级为最高
thread.start();
注意:优先级高的线程分配CPU时间片的数量要多于优先级低的线程,并不是优先级高的线程先比优先级低的执行。
setDaemon 设置守护线程
setDaemon
方法是Thread
类的一个方法,用于将线程设置为守护线程(daemon thread)。
什么是守护线程
守护线程是一种特殊的线程,它不用于程序的正常运行。当程序中所有的非守护线程(用户线程)都结束时,程序会自动退出,即使还有守护线程在运行。守护线程通常用于为用户线程提供服务或者执行后台任务,如垃圾回收器线程。