线程说明
跟进程比较,线程的特点:
- 线程本身不能单独运行,它必须在一个程序中运行.
- 线程是程序的内部控制流,一个进程在执行过程中,为了同时完成多个操作,可以创建多个线程,形成多条执行线索.每个线程有自己的堆栈、自己的程序设计器和自己的局部变量.
- 每个进程都有一段专用的内存区域.而同一个进程的各线程间可以共享相同的内存空间(代码空间和数据空间),并且利这些共享内存来实现数据交换、实施通信以及必要的同步工作.
线程是程序中的一个执行流,一个执行流是由CPU运行程序代码并操作程序的数据形成的.JAVA 中的线程模型就是一个CPU、程序代码和数据的封装体.线程在JAVA 中是由 java.lang.Thread 类来定义和描述的,程序中的线程都是 Thread 的实例.
线程的创建
JAVA 提供了一下两创建线程的方法:
-
继承类 Thread.定义一个类,作为类 Thread 的子类,在该类上重写方法
run()
. -
实现 Runnable 接口.定义一个类,实现接口 Runnable,在该类上重写方法
run()
.
继承 Thread 类
方法
run()
是线程在运行时所要执行的动作,但是在执行线程过程中,并不是直接调用该方法,而是在由该类创建实例对象后,通过方法
start()
来启动线程.
实现 Runnable 接口
接口 Runnable 只有一个抽象方法
run()
,在实现该接口的类中需要重写该方法.
在线程运行时,实现了接口 Runnable 的对象需要由类 Thread 封装为线程实例,类 Thread 的构造方法可以接受接口 Runnable 的实例,然后线程实例通过
start()
方法启动线程.
两种方法比较
两种创建线程的方法有所区别,各有利弊.
- 使用继承类 Thread 的方法相对简单,比较直观,但是由于 JAVA 语言是单继承机制,使得一个类继承了类 Thread 之后不能再继承其他类.
-
通过实现 Runnable 接口的方法来创建线程时,虽然在生成线程实例时需要再通过 Thread 实例对 Runnable 实例进行封装;但定义时该类可以再继承其他类.
Thread 类不支持线程重用.在处理多任务时,通常会创建多个线程,让一个线程执行一个任务.其实也可以创建几个线程,然后重用这些线程执行任务,相关可以学习线程池.
线程的属性
线程本身有一些特有的属性,例如:线程标识符、线程名以及线程间的优先级属性等.通过这些属性,可以用来识别一个线程、了解线程的状态、控制线程的优先权等.
线程标识符
该属性为每一个线程存储了唯一的一个标识符,通过线程标识符,可以对线程进行区分.使用方法getId().
thread:
this.getId()
Runnable:
Thread.currentThread().getId()
线程名
每个线程默认有一个名字,默认的名字采用 Thread-1,Thread-2…形式;也可以对创建的线程设置线程名.通过
getName()
可以获取线程名.
线程的状态
一个线程从创建、运行到终止称为一个生命周期.线程在其生命周期中要经历创建、就绪、运行、阻塞和终止5中状态.通过
getState()
获取运行状态.
创建 New
当一个 Thread 类或其子类使用 new 关键词声明一个对象实例时,此时线程处于创建的状态.处于创建状态的线程有自己的内存空间,但是线程还没有运行.
就绪 Runnable
处于创建状态的线程通过调用
start()
方法启动后,线程进入就绪状态.这时的线程就已经拥有了运行所需的所有条件,将进入线程队列等待CPU调度.由于还没有获取到CPU的时间片,所以不会立即执行.
运行 Running
当处于就绪状态的线程被调度,并获得CPU资源时,便进入运行状态.
-
运行状态的线程执行
Run()
方法中的操作. - 运行状态线程不是一直占用CPU的,而是根据CPU的调度策略有关.CPU时间片用完后,就会转为就绪状态.
- 操作系统的调度策略,会考虑线程的优先级.高优先级的线程获得CPU调度几率要大.
- 多于处理器内核数目的线程数,并不能提供执行效率.
阻塞 Blocked
在某些情况下,一个正在运行的线程会让出正在使用的CPU资源,进入阻塞状态.
- 某些共享资源(打印机或文件资源)被占用.
- 等待I/O操作.
- 调用了wait、sleep或者 suspend 等方法.
- 尝试获得锁,而该锁正在被其他线程使用.
为了提供CPU的利,当一个线程被阻塞时,另一个线程就获得了运行的机会.
终止 Terminated
线程到达终止状态可能有几种原因:
-
线程的方法
run()
执行结束. -
线程通过某些方法(如
Destory()
) 被提前终止. -
在
run()
方法执行中发生了未捕获的异常.
线程的优先级
在 JAVA 语言中,每个线程都有一个优先级,不同线程可以被赋予不同的优先级.
通过
getPriority()
方法获取线程优先级,通过
setPriority()
方法设置线程优先级.
线程优先级共分为10个级别,最低为1,最高为10,默认优先级为5.
守护线程
前面提到的都是用户线程,另外还有一个是守护线程.两者没有什么太大不同,守护线程的唯一作用是为用户线程提供服务.
通过
setDaemon()
方法,将线程设置为守护线程.