线程的常见使用方法
Java中的线程并不是真正意义的线程,我们使用的是Thread类来表示线程,而这个类是 JVM 用来管理线程的一个类,也就是说,每个线程都有一个唯一的 Thread对象 与之关联
每一个执行流都需要有一个对象来进行描述,那么一个Thread对象就是用来表述一个线程执行流的,JVM会将这些对象统一起来管理,方便线程调度
常见构造方法
方法 | 说明 |
Thread() | 直接创建线程对象 |
Thread(Runnable target) | 使用Runnable对象只创建线程 |
Thread(String name) | 创建线程对象,同时命名 |
Thread(Runnable target,String name) | 使用Runnable对象创建线程,同时命名 |
Thread(()->{代码块}) | 使用lambda表达式创建线程 |
Thread(()->{代码块},String name) | 使用lambda表达式创建线程,同时命名 |
Thread(ThreadGroup group,Runnable target) 仅了解 | 线程可以被用来进行分组管理,分好的组就是线程组 |
//创建线程对象
Thread t = new Thread();
Thread t = new Thread("自定义线程");
//使用Runnable对象
Thread t = new Thread(new Runnable());
Thread t = new Thread(new Runnable("自定义线程"));
//使用lambda表达式
Thread t = new Thread(()->{
//代码块
},"自定义线程");
常见属性
属性 | 说明 | 获取方法 |
ID | 线程的唯一标识,不同线程不会重复 | getId() |
名称 | 调用线程的工具的名称 | getName() |
状态 | 线程当前所处的情况 | getState() |
优先级 | 越优先的线程越容易被调用到 | getPriority() |
是否后台线程 | JVM只有在一个进程的所有非后台线程结束后,才会结束运行(只管非后台) | isDaemon() |
是否存活 | 线程是否结束了 | isAlive() |
是否被中断 | isInterrupted() |
public static void main(String[] args) {
Thread t = new Thread(()->{
for (int i = 0; i < 5; i++) {
System.out.println("still alive!");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
System.out.println("I'm going to die");
});
System.out.println(t.getName()+" ID: "+t.getId());
System.out.println(t.getName()+" 状态: "+t.getState());
System.out.println(t.getName()+" 优先级: "+t.getPriority());
System.out.println(t.getName()+" 后台: "+t.isDaemon());
System.out.println(t.getName()+" 存活: "+t.isAlive());
System.out.println(t.getName()+" 中断: "+t.isInterrupted());
t.start();
System.out.println("die? --> 100%");
System.out.println(t.getName()+" 状态: "+t.getState());
}
在这个代码中,因为没有设置,t线程就是前台线程
线程启动
使用 start() 方法来启动线程
当我们覆写 run 方法来创建对象时,并不是说明线程开始运行了,这只能说明线程已经创建好了,至于让他动起来,就需要在加其他的方法
例如:
我们去医院进行挂号,当你在挂号处拿到号时,并不代表你可以去医生的诊室就诊了,你只是有了可以就诊的条件
只有当医院播报到你的名字,你才可以去到诊室进行就诊
那么我们覆写run方法创建对象就相当于挂号,而调用start方法就是患者被叫号了
需要注意的一点是,同一进程内,对于同一线程是不可以调用两次start方法的,就像同一患者不会在同种状态下被叫两次(复诊不算-->状态改变了)
public static void main(String[] args) {
Thread t = new Thread(()->{
for (int i = 0; i < 5; i++) {
System.out.println(i);
}
});
t.start();
System.out.println("================");
t.start();
}
休眠当前线程
线程的调度是不可以控制的,就像上一张图片,是先打印分割符才打印的数字(理想是先打印数字,再打印分割符)所以会用一些方法来保证实际休眠的时间大于等于参数设置的休眠时间
方法 | 说明 |
Public static void sleep(long mills) throws InterruptedException | 休眠当前线程mills毫秒 |
Public static void sleep(long mills,int nanos) throws InterruptedException | 可以更高精度的休眠 |
public static void main(String[] args) throws InterruptedException {
System.out.println(System.currentTimeMillis());
Thread.sleep(2000);
System.out.println(System.currentTimeMillis());
}
获取当前运行线程
方法 | 说明 |
Public static Thread currentThread() | 返回当前线程对象的引用 |
public static void main(String[] args) {
Thread t = Thread.currentThread();
System.out.println(t.getName());
}
中断线程
当线程开始运行的时候,在线程完成之前是不会结束的!但是我们也可以增加一些机制,用来结束线程,例如你在付款时,突然不想要那个东西了,就会退出付款界面,取消订单,那么此时你的购买操作就被中断了
目前有两种实现中断的方式:
-
通过共享的标记来进行沟通
-
调用interrupt()方法来通知
方法一
使用自定义的变量来作为标志位,需要给标志位加上volatile关键字
public static Boolean isRunning=true;
public static void main(String[] args){
Thread t = new Thread(()->{
//这里是t线程的条件
while (isRunning){//内部类访问外部类的成员
System.out.println("thread");
try{
Thread.sleep(1000);
}catch (InterruptedException e){
e.printStackTrace();
}
}
System.out.println("t线程结束!");
});
//t的内部条件在执行时,外部的代码也在一起运行
t.start();
try {
Thread.sleep(3000);//-->过了3s之后 线程修改?
}catch (InterruptedException e){
e.printStackTrace();
}
//3秒之后,主线程修改isRunning的值,从而通知 t 结束-->所以会打印3s的thread
System.out.println("控制t线程结束!");
isRunning=false;
}
方法二
使用 Thread.interrupt() 或者 Thread.currentThread().isInterrupted() 代替自定义标志位
方法 | 说明 |
public void interrupt() | 中断与对象关联的线程。如果线程正在阻塞,则以异常方式通知;否则,设置中断标志位 |
public static boolean interrupted() | 判断当前线程的中断标志位是否被设置,并在调用后清除标志位 |
public boolean isInterrupted() | 判断与对象关联的线程的中断标志位是否被设置,调用后不清除标志位 |
public static void main(String[] args) {
Thread t =new Thread(()->{
while(!Thread.currentThread().isInterrupted()){//Thread.currentThread().isInterrupted() 默认是false
//括号内 true是线程要终止了 false线程要继续执行
System.out.println("thread");
try{
Thread.sleep(1000);
}catch (InterruptedException e){
//e.printStackTrace();
throw new RuntimeException(e);//e.printStackTrace();-->会继续执行
}
}
});
t.start();
try{
Thread.sleep(3000);
}catch (InterruptedException e){
throw new RuntimeException(e);
}
t.interrupt();//通过这个方法,相当于设置Boolean值为true-->还能唤醒sleep等阻塞的方法
//抛出异常,不会等待
}
Thread 收到的方法有两种:
-
当线程调用 wait/join/sleep 等方法阻塞,使用 InterruptedException 异常的形式通知,清除中断标志
-
此时要不要结束线程取决于catch中的写法,可以报错,可以直接退出等
-
-
设置中断标识
-
Thread.isInterrupted() 判断当前线程的中断标志被设置,清除中断标志
-
Thread.currentThread().isInterrupted() 判断当前线程的中断标志被设置,不清除中断标志
-
等待线程
当我们需要等待一个线程完成之后,其他线程才能继续工作,这个时候我们就要让其他线程等待那个线程,比如当你选好商品打算付款时,发现钱包里面钱不够,你就只能等工资到账之后才能付款,那么此时就是你的付款操作等待工资到账操作
调用 join 方法的线程会让其他线程等待自己完成
public static void main(String[] args) throws InterruptedException {
Runnable init = ()->{
for (int i = 0; i <5 ; i++) {
System.out.println(Thread.currentThread().getName()+"say: I'm running!");
}
System.out.println("I'm over!");
};
Thread t1 = new Thread(init,"工资");
Thread t2 = new Thread(init,"结账");
t1.start();
t1.join();
System.out.println("工资到账啦!可以买东西了!");
t2.start();
t2.join();
System.out.println("买到喜欢的东西了!");
}