文章内容
一、何为线程?
1、线程的定义
线程的定义:程序中的一个顺序控制流程,也是CPU中的最小调度单位。
从定义来看,可能很多同学觉得有点晦涩难懂,OK,我这里举个例子就明白了,其实在我们最开始学习java的时候就写过Hello World程序对吧,在一个main方法里面定义一条打印语句,然后执行main方法就能完成打印,那同学们有没有想过,到底是由什么玩意儿来执行的main方法呢?其实就是有我们的线程来执行的,在我们java中,任何代码的执行都是由线程来完成的,那线程追溯到计算机底层其实又是由CPU来完成的。OK,到这里就明确了,计算机执行任何程序都是由CPU来完成的,而CPU真正执行的是一个个的线程,然后由线程去完成相应代码的执行。
从计算机中反应到现实中,每个线程就好比每个不同的人,每个人都自己的独立完成工作的能力,线程也可以这么理解,以后在我的课堂中,人就是线程,线程就是人。
2、进程的定义
进程的定义:运行中的应用程序。
没有运行的应用程序只是一堆静态代码,这里可以通过查看计算机进程验证
3、进程和线程的关系
进程和线程的关系:一个进程由多个线程构成。
二、线程的关系户
1、线程与CPU核心数的关系
如果线程数小于或者等于CPU核心数的时候,则CPU能同时运行,如果,大于的时候,CPU只能通过上下文切换的方式轮转交替执行线程。
2、守护线程和本地线程
1)两种Java中的线程
守护线程(Daemon)和用户线程(User)。任何线程都可以设置为守护线程和用户线程,通过方法Thread.setDaemon(boolean);true则把该线程设置为守护线程,反之则为用户线程。Thread.setDaemon()必须在Thread.start()之前调用,否则运行时会抛出异常。
2)守护线程和用户线程的区别
唯一的区别是判断虚拟机(JVM)何时离开,Daemon是为其他线程提供服务,如果全部的User Thread已经撤离,Daemon 没有可服务的线程,JVM撤离。也可以理解为守护线程是JVM自动创建的线程(但不一定),用户线程是程序创建的线程;比如JVM的垃圾回收线程是一个守护线程,当所有线程已经撤离,不再产生垃圾,守护线程自然就没事可干了,当垃圾回收线程是Java虚拟机上仅剩的线程时,Java虚拟机会自动离开。
三、线程的作用
说的官方一点就是线程的使用能够提高CPU的效率,说的直白一点就是人多了,干一件事就快了,干多件事也快了,反正就是人多干活快。
举个例子,如果现在你要往数据库里面插入1千万条数据(暂时先不考虑数据库的性能问题)然后你一个线程一条条插可能要10分钟,但是如果你是10个线程同时执行,可能就只要少于1分钟就能搞定。
虽然线程很好用,但是并不是线程越多越好,就看人一样的,虽然人多力量大,但是人太多了,地球妈妈也扛不住啊,所以线程太多了,CPU扛不住,为什么扛不住呢?看上面讲的核心数。
四、线程的使用
普通回答: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、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、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
}
}