Thread的基本用法
创始人
2024-02-29 08:02:57
0

目录

1.创建线程

2.1 继承Thread类,重写run方法

2.2 实现Runnable,重写run方法

2.3 使用匿名内部类继承Thread类,重写run方法

2.4 使用匿名内部类实现Runnable接口,重写run方法

2.5 使用lambda表达式

3.Thread类的构造方法

4.Thread类的常见属性的获取方法

5.线程方法的使用

5.1 前台线程与后台线程

5.2 线程的执行顺序

5.3 线程休眠

5.4 线程中断

5.5 线程等待

5.6 获取线程实例


1.创建线程

在我们没有使用多线程编程之前,我们代码的运行全部都由main线程(main线程也是主线程)独自运行,而使用多线程的编程之后,我们的代码可以在多个线程上"同步"运行,下面先介绍创建线程的5种方法

2.1 继承Thread类,重写run方法

class MyThread extends Thread{@Overridepublic void run() {while(true) {System.out.println("hello Thread");}}
}public class ThreadDemo1 {public static void main(String[] args){Thread t = new MyThread();t.start();while(true) {System.out.println("hello main");}}
}

首先创建一个类,由这个类去继承Thread类,重写其中的run方法

run方法: 创建出来的线程去执行的内容

start方法:  创建出一个新的线程,并让其去执行run方法里面的内容

执行上面的代码,会出现hello Thread 和 hello main交替打印的情况

他们的打印杂乱无章,可能是t线程(创建出来的线程)先打印,也可能是main线程先打印

这是由于: 当执行完t.start()之后,创建出来一个新的线程(简称t线程),t线程和main线程同属于在同一个进程上,他们在操作系统内核中的调度情况可能是并行,也可能是并发,是无法预知的,所以打印的前后顺序也是无法预知的.

sleep方法: sleep方法是Thread类中的一个静态方法,它可以让线程进入短暂休息的状态,比如sleep(1000) 就是休息1秒.(注意:sleep方法需要处理异常)

有了sleep方法,就可以更直观的去观察线程的输出情况,如下:

class MyThread extends Thread{@Overridepublic void run() {while(true) {System.out.println("hello Thread");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}}
}public class ThreadDemo1 {public static void main(String[] args){//创建一个Thread对象,new MyThread()Thread t = new MyThread();t.start();while(true) {System.out.println("hello main");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}}
}

2.2 实现Runnable,重写run方法

class MyRunnable implements Runnable {@Overridepublic void run() {System.out.println("hello Thread");}
}public class ThreadDemo2 {public static void main(String[] args) {Runnable runnable = new MyRunnable();Thread t = new Thread(runnable);t.start();}
}

首先创建一个类,实现Runnable接口,重写里面的run方法.

将Runnable对象传到Thread的构造方法中,然后t.start()线程就创建好了.

2.3 使用匿名内部类继承Thread类,重写run方法

public class ThreadDemo3 {public static void main(String[] args) {Thread t = new Thread() {@Overridepublic void run() {System.out.println("hello Thread");}};t.start();}
}

和2.1 类似,只不过使用了匿名内部类.

2.4 使用匿名内部类实现Runnable接口,重写run方法

public class ThreadDemo4 {public static void main(String[] args) {Thread t = new Thread(new Runnable() {@Overridepublic void run() {System.out.println("hello Thread");}});t.start();}
}

和 2.2 类似,将类实现替换成内部类实现

2.5 使用lambda表达式

public class ThreadDemo5 {public static void main(String[] args) {Thread t = new Thread(() -> {System.out.println("hello Thread");});t.start();}
}

仿照代码中去写就好了,大括号里面的内容就相当于重写run方法的内容

3.Thread类的构造方法

方法1:Thread()

创建线程对象

方法2:Thread(Runnable r)

使用Runnable对象创建线程对象

方法3:Thread(String name)

创建线程对象,并初始化线程的名字

方法4:Thread(Runnable r,String name)

使用Runnalbe对象创建线程,并初始化线程的名字

4.Thread类的常见属性的获取方法

(1) getId()

获取线程的ID

(2) getName()

获取线程的名字

(3) getState()

获取线程的状态

(4) getPriority()

获取线程的优先级

(5) isDaemon()

判断线程是否为后台线程(true/false)

(6) isAlive()

判断线程是否存活(true/false)

(7) isInterrupted()

判断线程是否被中断(true/false)

5.线程方法的使用

5.1 前台线程与后台线程

前台线程: 会阻止进程结束,如果前台线程没有执行完,进程是无法结束的.

后台进程: 不会阻止进程结束,即使后台线程没有执行完,进程也是可以结束的.

我们在main方法中创建的线程会默认为前台线程,但是我们可以使用setDaemon()方法去设置线程

如下面的代码:

当执行完t.setDaemon(true) 之后,t线程就被设置成了后台线程,此时当主线程(main线程)执行完之后,t线程也就结束了.

因为当main线程执行晚最后一条语句时,它的任务就完成了,main线程会自动销毁(t线程如果执行完run方法的内容后也会自动销毁),而此时这个进程中只有main一个前台线程,当main线程被销毁后,其他的后台线程也就结束了.

public class ThreadDemo6 {public static void main(String[] args) {Thread t = new Thread(new Runnable() {@Overridepublic void run() {while(true) {System.out.println("hello Thread");}}}, "MyThread");t.setDaemon(true);t.start();}
}

5.2 线程的执行顺序

线程的执行顺序是不确定的,如下面这个代码:

public class ThreadDemo7 {public static void main(String[] args) {Thread t = new Thread(()->{for(int i = 0;i < 3;i++) {System.out.println("hello Thread");try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}});t.start();for(int i = 0;i < 3;i++) {try {Thread.sleep(1000);System.out.println("hello main");} catch (InterruptedException e) {throw new RuntimeException(e);}}}
}

当执行完 t.start() 之后,t线程被创建,此时t线程执行run方法中的内容,同时main线程继续执行main方法中下面的内容,此时,main线程遇到sleep(),休眠1s,所以会先打印 "hello Thread" ,而之后会先打印谁,后打印谁是不确定的.

因为在微观上,操作系统内核在调度两个线程时是使用并行还是并发是不确定的,也就导致了打印的顺序是不确定的.

 

5.3 线程休眠

线程的休眠和前面提到的sleep方法有关

当线程处于sleep状态时,就是处于休眠状态

如下:

public class Test {public static void main(String[] args) {Thread t = new Thread(() -> {while(true) {try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}});t.start();}
}

当前这个t线程就是处于休眠状态.

5.4 线程中断

线程中断相当于线程终止(结束),这个操作完全取决于设计线程里面的代码,我们给出的中断信息,只是传递了一个让其中断的信息,至于是否会真的被中断、被什么时候中断都是不确定的.

而传递中断信息可以有两种方法:

第一种:使用自己设置的标志位去传递中断信息

public class ThreadDemo8 {private static boolean flag = true;public static void main(String[] args) throws InterruptedException {Thread t= new Thread(() -> {while(flag) {System.out.println("hello Thread");try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}});t.start();Thread.sleep(3000);flag = false;}
}

我们设置了一个boolean类型的静态成员变量 flag,让它成为我们的标志位,当我们将flag设置为false时,程序就会停止,至于会迅速停止的原因也是因为对run方法的设计.

第二种:使用Thread类自带的标志位

public class ThreadDemo9 {public static void main(String[] args) throws InterruptedException {Thread t = new Thread(() -> {while(!Thread.currentThread().isInterrupted()) {System.out.println("hello Thread");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();//等待0.5s在进行终止try {Thread.sleep(500);} catch (InterruptedException ex) {e.printStackTrace();}break;}}});t.start();Thread.sleep(3000);t.interrupt();}
}

Thread类中有一个方法是Thread.currentThread().isIntercurrpted(),它的返回值默认为false

当外部调用t.interrupt()时 它的返回值会传变为true.

但是如果调用t.interript()方法时,t进程处于休眠状态(sleep状态),会立即别唤醒,被唤醒之后sleep会报出异常,但是代码仍然会继续执行.

因为sleep会进行清除标志位的操作,也就是将修改为true的标志位,修改会false

那么,为什么sleep要进行清除标志位的操作呢?

因为当程序被唤醒后,它不清楚程序是否要进行中断操作,将这个问题抛给了程序员,此时线程是否中断,完全取决于程序员设计的代码.

比如上面的事例代码,我在里面加入了break操作,让循环终止,程序结束,那么线程也就随之销毁,是执行了中断操作;而我又在break前面加上了一个sleep,让其先休眠 0.5s 再进行中断操作,这也就对应上了我最后开始说的:线程是否被中断,取决于其中代码的实现.

5.5 线程等待

线程等待使用的操作是Thread中的join()方法,它用类的实例来调用.

当执行t.join()方法后,当前执行的线程就会进入等待(也成为线程阻塞),等到t线程执行完,才可以继续执行当前的线程.

如下代码:

public class ThreadDemo10 {public static void main(String[] args) throws InterruptedException {Thread t = new Thread(() -> {for(int i = 0;i < 3;i++) {System.out.println("hello Thread");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}});t.start();//如果在join之前t线程也就结束了,join就不会再阻塞,会立即返回//Thread.sleep(5000);System.out.println("join 之前");try {t.join();} catch (InterruptedException e) {e.printStackTrace();}System.out.println("join 之后");}
}

上面的代码的执行顺序是如下这样:

join 之前

...(t线程的三遍 hello Thread 打印)

join 之后

也就是main线程等待t线程,当t线程执行完毕,join()才让main线程停止等待

而如果在执行到 t.join() 之前join线程就执行完了,那么main线程就不会等待.

比如:如果将上面代码注释的sleep(5000)打开,那么执行的结果就会变成下面的情况:

...(t线程的三遍 hello Thread 打印)

join 之前

join 之后

5.6 获取线程实例

Thread.currentThread()方法

它是一个类方法,作用是获取当前线程的实例

也就是说: 你在哪个线程中去调用这个方法,那么它就会返回这个线程的实例对象.

public class ThreadDemo11 {public static void main(String[] args) {Thread t = new Thread(() -> {System.out.println(Thread.currentThread().getName());},"t线程");t.start();}
}

此时上面的胆码输出为 -- t线程

相关内容

热门资讯

监控摄像头接入GB28181平... 流程简介将监控摄像头的视频在网站和APP中直播,要解决的几个问题是:1&...
Windows10添加群晖磁盘... 在使用群晖NAS时,我们需要通过本地映射的方式把NAS映射成本地的一块磁盘使用。 通过...
protocol buffer... 目录 目录 什么是protocol buffer 1.protobuf 1.1安装  1.2使用...
Fluent中创建监测点 1 概述某些仿真问题,需要创建监测点,用于获取空间定点的数据࿰...
educoder数据结构与算法...                                                   ...
MySQL下载和安装(Wind... 前言:刚换了一台电脑,里面所有东西都需要重新配置,习惯了所...
MFC文件操作  MFC提供了一个文件操作的基类CFile,这个类提供了一个没有缓存的二进制格式的磁盘...
在Word、WPS中插入AxM... 引言 我最近需要写一些文章,在排版时发现AxMath插入的公式竟然会导致行间距异常&#...
有效的括号 一、题目 给定一个只包括 '(',')','{','}'...
【Ctfer训练计划】——(三... 作者名:Demo不是emo  主页面链接:主页传送门 创作初心ÿ...