目录
Thread类的基本用法
方法1:创建子类,继承Thread
方法2:创建一个类,实现Runnable接口,再创建Runnable实例传递给Thread实例
方法3:使用匿名内部类创建Thread子类对象
方法4:使用匿名内部类创建Runnable子类对象
方法5:使用lambda表达式创建Runnable子类对象
多线程的优势
Thread类及其常见的用法
Thread常见的构造方法
Thread的几个常见的属性
ID
名称
状态
优先级
是否后台线程
是否存活
是否中断
启动线程——start()
中断线程
1.手动设置特殊的标志位,来控制线程是否要执行结束
2.通过 Thread.currentThread().isInterrupted()和Thread.Interrupted()来判定
等待线程
获取当前线程的引用
线程的休眠
当我们用Java进行进行多线程编程的时候,需要创建多个线程,那么怎么创建多个线程呢?在Java标准库中,就提供了一个Thread类,来表示操作线程,Thread类也可以视为是Java标准库提供的API。
在Java中创建好的Thread实例其实和操作系统中的线程是一一对应的关系,操作系统提供了一种关于线程的API(C语言风格),在Java中,对这API进一步包装,就成了Thread类。
Thread类的基本用法
通过Thread类创建线程写法有很多种,其中,最简单的方法就是创建子类,重写run()方法
方法1:创建子类,继承Thread
class MyThread extends Thread{
//run方法里描述了线程内部要执行哪些代码
//是在新创建出来的线程中被执行的
@Override
public void run() {
System.out.println("hello,thread");
}
}
public class Demo1 {
public static void main(String[] args) {
Thread t = new MyThread();
//通过start执行线程,才是真正在系统中创建了线程,执行上面的run操作
//调用start之前,系统中是没有创建出线程的。
t.start();
}
}
class MyThread extends Thread{
//run方法里描述了线程内部要执行哪些代码
//是在新创建出来的线程中被执行的
@Override
public void run() {
System.out.println("hello,thread");
}
}
public class Demo1 {
public static void main(String[] args) {
Thread t = new MyThread();
//通过start执行线程,才是真正在系统中创建了线程,执行上面的run操作
//调用start之前,系统中是没有创建出线程的。
t.start();
}
}
其中,我们要注意的是,线程之间是并发执行的,其中,在Java进程中,至少含有一个调用main方法的线程,接下来,我们来举个例子来说明下
首先我们先创建一个MyThread继承Thread,并且重写run() 方法,里面一直每隔一秒循环打印“hello,Thread”,然后我们再在main线程中同理循环打印“hello,main”
接下来,让我们来看下结果,
这里我们会发现,并不是每次都是一下“hello main” 一下“hello Thread”,我们会发现先执行那个线程是具有随机性的,也就是我们常说的线程的抢占式执行。而这个随机执行也会给我们线程编程带来很大的麻烦。
方法2:创建一个类,实现Runnable接口,再创建Runnable实例传递给Thread实例
通过Runnable类描述任务的内容,再将描述好的任务交个Thread实例。
方法3:使用匿名内部类创建Thread子类对象
方法4:使用匿名内部类创建Runnable子类对象
方法3和方法4都是使用匿名内部类来创建线程,我们通常认为Runnable() 这个方法会好点,这样可以让线程和线程执行的任务更好的解耦(我们一般希望写的代码高内聚,低耦合) Runnable单纯的只是描述了一个任务,至于这个任务怎么执行,他并不关心。
方法5:使用lambda表达式创建Runnable子类对象
这个方法相当于第四种方法的延伸,使用了lambda表达式代替了Runnable而已。
多线程的优势
多线程的优势是啥呢,自然而然的是提高了代码运行的效率,以前只有一个main线程,现在几个线程,下面我们来用代码展现怎么个提高效率法
让两个数自增十亿次,分别使用一个线程和两个线程
接下来我们来运行下,看看需要花费多长的时间。
花了10798ms,那么接下来我们来看看两个线程会花费多少时间呢
我们发现双线程比单线程快了 很多其中join()是啥作用我们后面将会提到
Thread类及其常见的用法
Thread常见的构造方法
我们前面讲的五种创建线程的方法就是。
Thread的几个常见的属性
ID
线程的唯一标识,不同线程不会重复
名称
是调试工具所要用到的
状态
表示线程所处情况
优先级
调度的优先等级
是否后台线程
这里我们只需要记住一点,JVM在一个进程的所有非后台线程结束后,才会结束运行
是否存活
简单的理解就是run方法时候运行结束了
是否中断
这个问题我们会在后面慢慢提到
启动线程——start()
start()决定了系统是不是真的创建了线程,run()方法只是描述了线程会干什么,而start() 则是一个特殊的方法,内部会在系统中创建线程。
中断线程
如何让一个线程停下来呢?线程停下来的关键,是要让线程对应的run方法执行完(main线程特殊,得main方法执行完)
为了让run方法执行完,我们有以下方法
1.手动设置特殊的标志位,来控制线程是否要执行结束
这里我们设置了一个标志位,main线程修改isQuit当执行时间过了5000ms就停止线程
这种方法并不严谨,我们还有更好的方法
2.通过 Thread.currentThread().isInterrupted()和Thread.Interrupted()来判定
Thread.Interrupted()方法是一个静态方法
Thread.currentThread().isInterrupted()是一个实例方法,其中currentThread()可以获取到当前的线程的实例
具体用法大家可以看看图片中的讲述
Thread.Interrupted()这个方法判定的标志位是Thread中的静态成员(一个程序中只有一个标志位)而Thread.currentThread().isInterrupted()方法判定的是Thread中的普通成员,每一个示例都能有自己的标志位,所以通常使用这个。
等待线程
这里我们就是来讲解上文用到的join(),首先,调用这个方法的线程是main线程,针对t这个线程使用,就是让main线程等待t线程,当调用join方法后。main线程就会进入阻塞状态,不在继续进行下去,那么怎么才能继续执行下去呢,只有当t线程执行晚了(也就是run方法执行的内容跑完了)通过线程等待,让t线程先结束,main线程在结束,一定程度上,干预了线程的执行次序
但是join()方法属于死等,这并不是很合理,所以,就提供了另一个版本就是我们可以再( )中输入最多等待时间。
获取当前线程的引用
看到标题是不是有点熟悉,对了,这就是前面提到的currentThread(),调用这个方法就可以获得到线程的实例。
线程的休眠
关于线程的休眠,我们前面的例子有使用过,Thread.sleep();当一个线程调用了sleep方法就会进入阻塞队列,而操作系统在调度线程执行的时候,只会从就绪队列中选择合适的PCB到CPU上运行,阻塞队列的就只能干等着,当sleep时间过了,就会重新进入就绪队列。
以上这些就是多线程的基本用法,谢谢大家



