多线程(上)
本文系统介绍了Java线程的核心知识,包括线程概念、创建方式、常用方法和状态管理。主要内容有:1)线程是轻量级执行单元,共享进程资源,支持并发执行;2)创建线程的5种方式(继承Thread类/实现Runnable接口/匿名类/lambda表达式);3)Thread类的重要方法(start/interrupt/join/sleep等)和属性;4)线程6种状态(NEW/RUNNABLE/WAITING
目录
一、认识线程
1、概念
多线程是指在一个进程内,同时执行多个独立的代码片段(即线程),以提高程序的执行效率和资源利用率。
简单来说,它就像一个工厂(进程),里面有多个工人(线程),他们共享工厂的设备(内存等资源),同时做不同的工作,从而更快地完成整体任务。
2、核心特点
资源共享:同一进程下的所有线程共享该进程的内存空间和资源,无需额外的通信机制
轻量级:线程的创建、切换和销毁的开销远小于进程,能更高效地利用CPU
并发执行:在单核CPU上,线程通过快速切换实现 " 并发 " ; 在多核CPU上,可实现真正的 并行
主要应用场景:
- 提升响应速度:例如在下载文件(后台线程)的同时,允许用户继续操作界面 (UI线程)
- 提高CPU利用率:当一个线程因等待 I / O (如读取文件、网络请求)而阻塞时,其它线程可以继续使用CPU
- 处理批量任务:例如,同时处理多个网络请求、批量处理数据等
3、进程和线程的区别
- 进程包含线程,每个进程至少有一个线程存在,即主线程
- 每个进程都有自己独立的资源,进程和进程之间不能共享;同一个进程的线程之间,共享相同的资源
- 进程之间不会相互影响,一个进程挂了,其它进程不会有事;同一个进程线程挂了,可能把其他线程一起带走
- 进程之间通常(不绝对)不会有 “ 资源冲突 ” 情况;同一个进程的线程之间,特别容易出现资源访问冲突
- 进程是操作系统资源分配的基本单位,线程是操作系统调度执行的基本单位
二、创建线程
1、继承Thread类
继承Thread来创建一个线程类
class MyThread extends Thread{
@Override
public void run() {
System.out.println("hello MyThread");
}
}
public class Demo1 {
public static void main(String[] args) {
MyThread t=new MyThread();
t.start();//线程开始运行
}
}
2、实现Runnable接口
创建Thread类实例,调用Thread的构造方法时将Runnable对象作为target参数
class MyRunnable implements Runnable{
@Override
public void run() {
System.out.println("hello Runnable");
}
}
public class Demo2 {
public static void main(String[] args) {
MyRunnable myRunnable=new MyRunnable();
Thread t=new Thread(myRunnable);
t.start();
}
}
3、匿名内部类创建Thread子类对象
public class Demo3 {
public static void main(String[] args) {
Thread t=new Thread(){
@Override
public void run() {
System.out.println("使用匿名类创建 Thread 子类对象");
}
};
t.start();
}
}
4、匿名内部类创建Runnable子类对象
public class Demo4 {
public static void main(String[] args) {
Thread t=new Thread(new Runnable() {
@Override
public void run() {
System.out.println("使用匿名类创建 Runnable 子类对象");
}
});
t.start();
}
}
5、lambda表达式创建Runnable子类对象
public class Demo5 {
public static void main(String[] args) {
Thread t=new Thread(() ->{
System.out.println("hello lambda");
});
t.start();
}
}
三、Thread类及常见方法
1、Thread的常见构造方法
方法 | 说明 |
---|---|
Thread() | 创建线程对象 |
Thread(Runnable target) | 使用 Runnable 对象创建线程对象 |
Thread (String name) | 创建线程对象并命名 |
Thread(Runnable target,String name) | 使用 Runnable 对象创建线程对象,并命名 |
【了解】Thread(ThreadGroup group,Runnable target) | 线程可以被用来分组管理,方便对一组线程进行统一的操作 |
public class Demo6 {
public static void main(String[] args) {
Thread t1=new Thread();
Runnable runnable=new Runnable() {
@Override
public void run() {
System.out.println("MyRunnable");
}
};
Thread t2=new Thread(runnable);
Thread t3=new Thread("我的名字");
Thread t4=new Thread(runnable,"我的名字");
}
}
2、Thread的常见属性
属性 | 获取方法 |
---|---|
ID | getId() |
名称 | getName() |
状态 | getState() |
优先级 | getPriority() |
是否后台线程 | isDaemon() |
是否存活 | isAlive() |
是否被中断 | isInterrupted() |
ID是线程的唯一标识,不同线程会重复
- 名称是各种调试工具用到
- 状态表示线程当前所处的一个情况
- 优先级高的线程理论上来说更容易调度到
- 关于后台线程:JVM会在一个进程的所有非后台线程结束后才运行结束
- 是否存活,简单来说为 run 方法是否运行结束了
- 线程中断问题,下面进一步说明
public class Demo7 {
public static void main(String[] args) {
Thread t=new Thread(()->{
for (int i = 0; i < 10 ; i++) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
System.out.println(Thread.currentThread().getName()+"即将死亡");
});
System.out.println(Thread.currentThread().getName()+" ID: "+t.getId());
System.out.println(Thread.currentThread().getName()+" 名称: "+t.getName());
System.out.println(Thread.currentThread().getName()+" 状态: "+t.getState());
System.out.println(Thread.currentThread().getName()+" 优先级: "+t.getPriority());
System.out.println(Thread.currentThread().getName()+" 是否后台线程: "+t.isDaemon());
System.out.println(Thread.currentThread().getName()+" 是否存活: "+t.isAlive());
System.out.println(Thread.currentThread().getName()+" 是否被中断: "+t.isInterrupted());
t.start();
}
}
3、启动、中断、等待一个线程
启动一个线程 - start()
启动线程的核心是调用线程对象的 start ( ) 方法 (而非直接调用 run( ) 方法),该方法会通知操作系统为线程分配资源并调度执行,最终自动触发 run( ) 方法中的任务逻辑
public class Demo8 {
public static void main(String[] args) {
Thread t=new Thread(() ->{
System.out.println("start线程启动");
});
t.start();
System.out.println("main线程");
}
}
中断一个线程
有两种常见方式:
- 通过共享的标记来进行沟通
- 调用 interrupt( ) 方法来通知
通过标记:
public class Demo9 {
public static boolean flag=false;
public static void main(String[] args) throws InterruptedException {
Thread t=new Thread(() ->{
while(!flag){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("未中断");
}
System.out.println(Thread.currentThread().getName()+"已中断");
});
t.start();
Thread.sleep(10000);
flag=true;
}
}
通过已有的方法:
Thread内部包含了一个boolean类型的变量作为线程是否被中断的标记
方法 | 说明 |
---|---|
public void interrupt() |
中断对象关联的线程,如果线程正在阻塞,则以异常方式通知,否则设置标志位 |
public static boolean interrupted() |
判断当前线程的中断标志位是否设置,调用后清除标志位 |
public boolean isInterrupted() |
判断对象关联的线程标志位是否设置,调用后不清除标志位 |
public class Demo10 {
public static void main(String[] args) throws InterruptedException {
Thread t=new Thread(() ->{
Thread cur=Thread.currentThread();
while (!cur.isInterrupted()){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
//这里如果不break,那么sleep被唤醒后会重置标志位,会重新进入循环
break;
}
System.out.println("未中断");
}
System.out.println("被中断");
});
t.start();
Thread.sleep(1500);
t.interrupt();
}
}
等待一个线程 - join( )
方法 | 说明 |
---|---|
public void join() | 等待线程结束 |
public void join(long millis) | 等待线程结束,最多等millis毫秒 |
public void join(long millis,int nanos) | 同理但可以更高精度 |
当前线程(调用 join( ) 的线程)等待目标线程执行完毕,再执行自身后续的代码
public class Demo11 {
public static int count=0;
public static void main(String[] args) throws InterruptedException {
Thread t=new Thread(() ->{
for (int i = 0; i <=100 ; i++) {
count+=i;
}
});
t.start();
//这里的join方法是为了让main线程等待Thread0线程执行完毕后再执行main线程的逻辑
//如果没有join这个方法后续的count打印就是0
t.join();
System.out.println(count);
}
}
public class Demo12 {
public static int sum=0;
public static void main(String[] args) throws InterruptedException {
Thread mainThread=Thread.currentThread();
Thread t=new Thread(() ->{
try {
//t线程也可以等待main线程
mainThread.join();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println(sum);
});
t.start();
Thread.sleep(1000);
for (int i = 0; i <=100 ; i++) {
sum+=i;
}
}
}
4、获取当前线程引用
方法 | 说明 |
---|---|
public static Thread currentThread() | 返回当前线程对象的引用 |
public class Demo13 {
public static void main(String[] args) {
Thread t=Thread.currentThread();
System.out.println(t.getName());
}
}
5、休眠当前线程
方法 | 说明 |
---|---|
public static void sleep (long millis) throws InterruptedException |
休眠当前线程millis 毫秒 |
public static void sleep (long millis,int nanos) throws InterruptedException | 可以更高精度的休眠 |
public class Demo14 {
public static void main(String[] args) throws InterruptedException {
System.out.println(System.currentTimeMillis());
Thread.sleep(3000);
System.out.println(System.currentTimeMillis());
}
}
四、线程的状态
1、线程的所有状态
- NEW(新建状态):创建了 Thread 对象,未调用 start
- TERMINATED(终止状态):操作系统内部线程已销毁,但 Thread 对象还在,线程入口方法执行完毕
- RUNNABLE(就绪状态):正在工作中和即将开始工作
- WAITING(阻塞状态 - > 等待):死等进入阻塞
- TIMED_WAITING(超时等待):带有超时时间的阻塞等待
- BLOCKED(阻塞):锁竞争 / 锁冲突
2、线程状态和状态转移
NEW、RUNNABLE、TERMINATED状态的转换:
public class Demo15 {
public static void main(String[] args) throws InterruptedException {
Thread t=new Thread(() ->{
System.out.println("hello t");
while (true){
}
});
System.out.println(t.getState());//NEW
t.start();
Thread.sleep(1);
System.out.println(t.getState());//RUNNABLE
//如果没有while循环那么就是TERMINATED
//System.out.println(t.getState());//TERMINATED
}
}
WAITING、TIMED_WAITING:
public class Demo16 {
public static void main(String[] args) throws InterruptedException {
Thread mainThread=Thread.currentThread();
Thread t=new Thread(() ->{
while (true){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
//System.out.println(mainThread.getState());//WAITING
System.out.println(mainThread.getState());//TIMED_WAITING
}
});
t.start();
//t.join()
t.join(10000);
}
}
关于BLOCKED我们后续再根据锁来了解
更多推荐
所有评论(0)