线程(英语:thread)是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务
Java中有两类线程:User Thread(用户线程)、Daemon Thread(守护线程)
用户线程即运行在前台的线程,而守护线程是运行在后台的线程。 守护线程作用是为其他前台线程的运行提供便利服务,而且仅在普通、非守护线程仍然运行时才需要,比如垃圾回收线程就是一个守护线程。当VM检测仅剩一个守护线程,而用户线程都已经退出运行时,VM就会退出,因为如果没有了守护者,也就没有继续运行程序的必要了。如果有非守护线程仍然活着,VM就不会退出。
守护线程并非只有虚拟机内部提供,用户在编写程序时也可以自己设置守护线程。用户可以用Thread的setDaemon(true)方法设置当前线程为守护线程。
虽然守护线程可能非常有用,但必须小心确保其它所有非守护线程消亡时,不会由于它的终止而产生任何危害。因为你不可能知道在所有的用户线程退出运行前,守护线程是否已经完成了预期的服务任务。一旦所有的用户线程退出了,虚拟机也就退出运行了。因此,不要再守护线程中执行业务逻辑操作(比如对数据的读写等)。
setDaemon(true)必须在调用线程的start()方法之前设置,否则会出IllegalThreadStateException异常。
在守护线程中产生的新线程也是守护线程
不要认为所有的应用都可以分配给守护线程来进行服务,比如读写操作或者计算逻辑。
线程的状态
- New:新建状态,new出来,还没有调用start
- Runnable:可运行状态,调用start进入可运行状态,可能运行也可能没有运行,取决于操作系统的调度
- Blocked:阻塞状态,被锁阻塞,暂时不活动,阻塞状态是线程阻塞在进入synchronized关键字修饰的方法或代码块(获取锁)时的状态。
- Waiting:等待状态,不活动,不运行任何代码,等待线程调度器调度,wait sleep
- Timed Waiting:超时等待,在指定时间自行返回
- Terminated:终止状态,包括正常终止和异常终止
前边我们提到了线程的六种状态,New Runnable Blocked Waiting Timed Waiting Terminated,那么在这六种状态下调用线程中断的代码会怎样呢?
- New和Terminated状态下,线程不会理会线程中断的请求,既不会设置标记位
- 在Runnable和Blocked状态下调用interrupt会将标志位设置位true
- 在Waiting和Timed Waiting状态下会发生InterruptedException异常,
线程的创建
- 继承Thread重写run方法
- 实现Runnable重写run方法
- 实现Callable重写call方法
实现Callable和实现Runnable类似,但是功能更强大,具体表现在
- 可以在任务结束后提供一个返回值,Runnable不行
- call方法可以抛出异常,Runnable的run方法不行
- 可以通过运行Callable得到的Fulture对象监听目标线程调用call方法的结果,得到返回值,(fulture.get(),调用后会阻塞,直到获取到返回值)
什么导致线程阻塞?
线程执行了Thread.sleep(int millsecond)方法,放弃CPU,睡眠一段时间,一段时间过后恢复执行;
线程执行一段同步代码,但无法获得相关的同步锁,只能进入阻塞状态,等到获取到同步锁,才能恢复执行;
线程执行了一个对象的wait()方法,直接进入阻塞态,等待其他线程执行notify()/notifyAll()操作;
线程执行某些IO操作,因为等待相关资源而进入了阻塞态,如System.in,但没有收到键盘的输入,则进入阻塞态。
线程礼让,Thread.yield()方法,暂停当前正在执行的线程对象,把执行机会让给相同或更高优先级的线程,但并不会使线程进入阻塞态,线程仍处于可执行态,随时可能再次分得CPU时间。线程自闭,join()方法,在当前线程调用另一个线程的join()方法,则当前线程进入阻塞态,直到另一个线程运行结束,当前线程再由阻塞转为就绪态。
线程执行suspend()使线程进入阻塞态,必须resume()方法被调用,才能使线程重新进入可执行状态。