1. Java中线程的两种创建方式
1.1 继承Thread类
当调用start()方法后,该线程就进入到线程队列当中,这时候在等待CPU的服务,一旦获取到了CPU的时间片,这个时候就会转到run()方法执行相对应的代码。当run()方法的逻辑执行完了,那么线程就会自然而然的消完了。
1.2 实现Runnable接口
他们之间的共同点: 都需要创建线程,然后再调用start()方法。
区别:1. Runnable方式可以避免Thread方式由于Java单继承特性带来的缺陷。
2. Runnable的代码可以被多个线程(Thread实例)共享,适合于多个线程处理同一资源的情况。
2. 线程的生命周期
1. 创建: 新建一个线程对象,如Thread thd = new Thread();
2. 就绪状态: 创建了线程对象后,调用了线程的start()方法(Ps:此时线程只是进入到了线程队列,等待CPU的服务,具备了运行的条件,但并不一定已经开始运行了)。
3. 运行状态: 处于就绪状态的线程,一旦获取了CPU资源,便进入到了运行状态,开始执行run()方法里面的逻辑。
4. 终止: 线程的run()方法执行完毕,或者线程调用了stop()方法,线程便进入终止状态。但是stop()方法目前已经被淘汰掉了。
5. 阻塞状态: 一个正在执行的线程在某些情况下,由于某种原因而暂时让出了CPU资源,暂停了自己的执行,便进入到了阻塞状态,如调用了sleep()方法:
BLOCK:一个线程正在等待监听器的锁,例如被synchronized
WATING:一个线程无限期的等待另一个线程执行任务,例如调用join()
TIMED_WATING: 也是在等待其他线程的任务,只不过等待时间有限制,例如调用sleep()
5.1 当线程调用了例如sleep方法或者其他方法,它会休眠自己,从而进入到阻塞状态:
5.2:这时,线程1会把CPU的资源让出来,从而让其他线程执行
5.3 : 一旦sleep所指定的时间到了之后,那么线程就“苏醒”过来了,阻塞状态就被解除了。
5.4 :然后线程就会重新回到就绪状态,等待获取CPU
5.5 : 一旦获取到CPU,就又重新进入到运行状态,也有可能被终止掉:
整个流程如下:
首先是当我们new出来一个线程对象之后,线程就进入到了创建状态,之后调用start()方法,让线程进入到就绪状态,接着线程就会进入到线程队列中,等待获取CPU的服务,一旦获取到CPU资源,那么线程就会进入到运行状态,开始执行run()方法里面的逻辑,如果run()方法里面的逻辑执行完毕,这之后线程就会进入到终止状态。
但是如果run()方法在执行过程中,遇到了一些阻塞事件,例如:调用了 wait(),sleep(),join()。那么这时候线程就会进入到阻塞状态,一旦阻塞状态被解除之后,就会重新回到就绪状态,重新开始一个新的轮回。
3. 守护线程
Java线程有两类:
1. 用户线程:运行在前台,执行具体的任务
例如:程序的主线程,连接网络的子线程等都是用户线程。所有执行具体任务的线程,都可以叫做用户线程。
这类工作 ”看的清,但摸不到“。
2. 守护线程 : 运行在后台,为其他前台线程服务。
特点:一旦所有的用户线程都结束运行,守护线程会随着JVM一起结束工作。
应用:数据库连接池中的监测线程:例如会有线程来监测连接的个数,以及连接超时的时间等等
JVM虚拟机启动后的监测线程:例如会有线程来监测虚拟机内存的使用情况。
最常见的守护线程:垃圾回收线程。
如何设置守护线程:
可以通过调用Thread类的setDaemon(true)
来设置当前的线程为守护线程。
注意事项:
1. setDaemon(true)方法必须在start()方法之前调用,否则会抛出IllegalThreadStateException异常。
也就是说,守护线程必须放在线程启动之前。
2. 在守护线程中产生的新线程也是守护线程。
3. 不是所有的守护线程都可以分配给守护线程来执行,比如说读写操作,或者逻辑计算:
一旦所有的用户线程都退出运行了:
守护线程就会觉得自己没有存在的必要了,因为没有所守护的对象了。所以这个时候会随着JVM结束掉工作。
但是当我们在守护线程里面做了一些读写操作:
但是当做到一半的时候,用户线程退了出来:
那这个时候,守护线程就没有存在的必要了,它就会结束掉自己:
但是这个时候读写操作还没进行完毕,守护线程就退出来了,程序就会崩溃了~
学习于慕课网: