学习目标
-
线程是什么?线程与进程的区别与联系
-
并行与并发的区别和联系
-
线程的创建方式有哪几种,以及它们各自的特点
-
线程的生命周期有哪些
-
线程怎么启动停止的
第1章 线程简介
1.1 何为线程
1、线程的定义:程序中的一个顺序控制流程,也是CPU中的最小调度单位。
从定义来看,可能很多同学觉得有点晦涩难懂,OK,我这里举个例子就明白了,其实在我们最开始学习java的时候就写过Hello World程序对吧,在一个main方法里面定义一条打印语句,然后执行main方法就能完成打印,那同学们有没有想过,到底是由什么玩意儿来执行的main方法呢?
其实就是有我们的线程来执行的,在我们java中,任何代码的执行都是由线程来完成的,那线程追溯到计算机底层其实又是由CPU来完成的。
OK,到这里就明确了,计算机执行任何程序都是由CPU来完成的,而CPU真正执行的是一个个的线程,然后由线程去完成相应代码的执行。
好,从计算机中反应到现实中,每个线程就好比每个不同的人,每个人都自己的独立完成工作的能力,线程也可以这么理解,以后在我的课堂中,人就是线程,线程就是人。
2、进程的定义:运行中的应用程序。(没有运行的应用程序只是一堆静态代码,这里可以通过查看计算机进程验证)
3、进程和线程的关系:一个进程由多个线程构成。
1.2 线程的关系户
1、线程与CPU核心数的关系。如果线程数小于或者等于CPU核心数的时候,则CPU能同时运行,如果,大于的时候,CPU只能通过上下文切换的方式轮转交替执行线程。
2、守护线程和本地线程
-
Java中的线程分为两种:守护线程(Daemon)和用户线程(User)。任何线程都可以设置为守护线程和用户线程,通过方法Thread.setDaemon(boolean);true则把该线程设置为守护线程,反之则为用户线程。Thread.setDaemon()必须在Thread.start()之前调用,否则运行时会抛出异常。
-
两者的区别: 唯一的区别是判断虚拟机(JVM)何时离开,Daemon是为其他线程提供服务,如果全部的User Thread已经撤离,Daemon 没有可服务的线程,JVM撤离。也可以理解为守护线程是JVM自动创建的线程(但不一定),用户线程是程序创建的线程;比如JVM的垃圾回收线程是一个守护线程,当所有线程已经撤离,不再产生垃圾,守护线程自然就没事可干了,当垃圾回收线程是Java虚拟机上仅剩的线程时,Java虚拟机会自动离开。
1.3 线程的作用
-
说的官方一点就是线程的使用能够提高CPU的效率,说的直白一点就是人多了,干一件事就快了,干多件事也快了,反正就是人多干活快。
-
举个例子,如果现在你要往数据库里面插入1千万条数据(暂时先不考虑数据库的性能问题)然后你一个线程一条条插可能要10分钟,但是如果你是10个线程同时执行,可能就只要少于1分钟就能搞定。
-
虽然线程很好用,但是并不是线程越多越好,就看人一样的,虽然人多力量大,但是人太多了,地球妈妈也扛不住啊,所以线程太多了,CPU扛不住,为什么扛不住呢?看上面讲的核心数。
1.4 线程的使用
OK,知道线程是啥了,也知道他很牛*了,接下来我们都开始用它了。怎么用呢?这也是一道简单的面试题,当面试官问你jdk提供几种线程创建方式的时候,其实有三种回答。
普通回答:jdk提供四种创建线程的方式,下面介绍这四种
1、通过继承Thread类
这里可以带着同学们进到底层start0()方法,然后解释一下start方法是怎么调用的。
-
自定义Girl类,然后继承Thread类
-
重写run方法(这个方法里面的逻辑是具体要干的活,比如这个女孩要生小孩)
-
创建Girl类的对象,Girl girl= new Girl();
-
调用对象的start方法,girl.start();
2、通过实现Runnable接口
Runnable实际上不能理解成线程,应该理解成任务更加准确,既然是任务,它就不能自己去执行,必须得通过线程(人)去执行
-
自定义MyTask类,然后实现Runnable接口
-
重写run方法(这里也是具体要干的活)
-
创建MyTask类的对象,MyTask task = new MyTask();
-
注意,我们现在创建的只是一个任务,不能指望任务自己去完成自己,所以还得通过下一步的操作
-
-
创建一个基本的线程,Thread ren = new Thread(task);
-
调用线程的start方法,ren.start()
3、通过实现Callable接口
-
自定义MyCallable类,然后实现Callable接口
-
重写call方法(这里也是具体要干的活)
-
创建MyCallable类的对象,MyCallable calla= new MyCallable();
-
这里也是一个任务,也只能通过线程去执行,但是它只能通过线程池去做
-
-
创建一个线程池,ExecutorService executorService = Executors.newFixedThreadPool(3);executorService.submit(calla);
它与Runnable的区别在于有无返回值,和能否抛异常
4、线程池
高手回答:jdk只有一种创建线程的方式,因为不论是上面四种中的哪一种方式,其实最终都是通过Thread去创建
顶手回答:jdk没有创建线程的能力,因为最终走到底层会发现,其实JVM是调用了底层操作系统的方法,由操作系统去真正创建线程了
1.5 线程的生命周期
刚刚已经给大家演示了怎么创建一个线程,然后怎么使用它,OK,那现在我们来总结一下,线程从出生到死亡,它走过了哪些阶段:
1、new,初始化状态;(创建类的这个过程还是能算是前期准备哈,线程这个人还没出生,这个时候不能算,类的创建阶段还只能算备孕阶段,只有通过new关键字创建了对象,才能算开始)
2、runnable,运行状态
-
start,就绪状态,这个状态还没有真正执行,只是告诉CPU,我准备好了,你可以来让我干活了。
-
running,运行中状态,拿到CPU给的工作牌,开始干活
3、timed_waiting,限期等待
sleep(10)/wait(10)/parkNanos(10)
4、waiting,无限期等待状态
wait()/park()
5、blocked,阻塞状态
在用锁的情况下(synchronized),没有获取到锁的线程进入blocked状态。限期和无限期等待也可以称为阻塞状态。
6、terminated:终止状态,执行完run方法之后,自动被回收。
1.6 线程启动和停止
1、start方法启动
2、终止:
-
run方法执行完了之后自动终止
-
Thread.stop(不建议使用,对程序不友好,如果线程任务正在执行的话,调用stop会强制是的线程中断)
-
Thread.interrupt(),这个是友好的中断。
-
interrupt()作用:更改线程的中断标记,唤醒处于阻塞状态下的线程。
会抛出InterruptedException,线程自己决定要不要结束,异常会触发复位
-
interrupted():获取线程的中断标记,并且执行复位操作
-
isInterrupted():获取线程的中断标记,但是不会进行复位操作
-
代码举例
public class InterruptDemo02 implements Runnable{
@Override
public void run() {
while(!Thread.currentThread().isInterrupted()){ //false
try {
TimeUnit.SECONDS.sleep(20);
System.out.println("1231");
} catch (InterruptedException e) {
e.printStackTrace();
System.out.println(Thread.currentThread().isInterrupted());
//可以不做处理,
//继续中断 ->
Thread.currentThread().interrupt(); //再次中断
//抛出异常。。
}
// Thread.currentThread().interrupted(); //再次复位
System.out.println(Thread.currentThread().isInterrupted());
}
System.out.println("processor End");
}
public static void main(String[] args) throws InterruptedException {
Thread t1=new Thread(new InterruptDemo02());
t1.start();
TimeUnit.SECONDS.sleep(5);
// Thread.sleep(1000);
t1.interrupt(); //有作用 true
}
}
下文预告
-
synchronized使用方式
-
synchronized锁状态及升级
-
volatile的作用
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/76683.html