Thread 与 Runnable 线程
1.基本概念
程序(program/software)是一段静态的代码,它是应用程序执行的蓝本、脚本。
类比:开发的售票系统
进程(Process)是指一个正在运行的程序,有自己的地址空间
正在运行的售票系统
多个进程互不影响各自卖各自的票:机票、火车票、汽车票等各种正在售票的售票系统
- 线程(Thread)是指进程中的一个执行路径,一个进程中可以并发多个线程,共享一个内存空间,线程之间可以自由切换,并发执行,一个进程最少有一个线程(单线程程序)。
多个网站、窗口同时卖一趟列车的火车票,票数固定,多人一起抢票
2. 进程的特点和状态
- 进程的特点
==动态性、并发性、独立性== - 进程执行时的间断性,决定了进程可能具有多种状态。事实上,运行中的进程具有以下三种基本状态。
- 1)就绪状态(Ready)
- 2)运行状态(Running)
- 3)阻塞状态(Blocked)
例如:火车站(类比CPU)的列车(类比进程)调度
一个火车站可以同时运行多个列车,但同时只能有一个列车到站、出站
3. 什么是多线程
- 多线程的定义
==进程内部的一个执行单元,它是程序中一个单一的顺序控制流程。如果在一个进程中同时运行了多个线程,用来完成不同的工作或各自完成各自的一样的工作,则称之为多线程。== - 线程和进程的区别
- 进程是系统资源分配的单位,可包括多个线程
- 线程是独立调度和分派的基本单位,共享进程资源
- 引入进程是为了多个程序并发执行,提高资源的利用率和系统吞吐量
- 引入线程是为了减少程序在并发执行时付出的时空开销
3. 线程的创建
- Java程序启动时,会立刻创建主线程,main就是在这个线程上运行。当不再产生新线程时,程序是单线程的。
- 两种方法来创建新线程
继承Java.lang.Thread类,并覆盖run()方法
class MyThread extends Thread { public void run( ) { /* 覆盖该方法*/ } }
实现Java.lang.Runnable接口,并实现run() 方法
class MyRunnable implements Runnable{ public void run( ) { /* 实现该方法*/ } }
4. 线程的启动
- 新建的线程不会自动开始运行,必须通过start( )方法启动
(1)启动继承Thread的线程
MyThread myThread1 = new MyThread (); myThread1 .start(); MyThread myThread2 = new MyThread (); myThread2 .start();
(2)启动实现Runnable接口的线程
MyRunnable myRunnable = new MyRunnable(); Thread thread1 = new Thread(myRunnable); Thread thread2 = new Thread(myRunnable); thread1.start(); thread2.start();
5.Thread类的常用方法
方 法 | 功 能
--- | ---
public Thread() | 创建线程对象
public Thread(String name) | 创建线程对象并指定线程的名称
static Thread currentThread() | 得到当前线程
final String getName( ) | 返回线程的名称
final void setName(String name) |将线程的名称设置为由name指定的名称
void start( ) | 调用run( )方法启动线程,开始线程的执行
void run( ) | 存放线程体代码
static void sleep(long millis) | 线程休眠millis毫秒
6. 两种实现线程的方式的区别
- 继承Thread类
- (1)不能再继承其他的类,因为单继承限制
- (2)多个线程之间共享数据比较复杂(静态static)
- (3)同步锁也比较复杂(使用线程类的class对象或者静态属性对象或者更复杂的方式)
- -
- 实现Runnable接口
- (1)可以继承其他的类
- (2)多个线程之间共享数据比较简单
- (3)同步锁也比较简单(直接使用this对象就行)
- (4)在线程内获得当前执行的线程信息时比较麻烦,需要使用Thread.currentThread()
7. 线程状态转换
7.1 线程的生命周期
- 线程的生命周期中的五种状态:就绪、运行、可运行(临时状态),阻塞、死亡(终止)
- 就绪 - 使用new关键字创建一个线程后,尚未调用其start方法之前
- 可运行
- 调用线程对象的start方法之后
- 这个状态当中,线程对象可能正在运行(运行状态),也可能等待运行(就绪状态)
- 阻塞
- 一种“不可运行”的状态,在得到一个特定的事件之后会返回到可运行状态
- 死亡
- 线程的run方法运行完毕或者在运行中出现未捕获的异常时
7.2 线程的优先级
- 线程的优先级用1-10之间的整数表示,数值越大优先级越高,默认的优先级为5
- 1:Thread.MIN_PRIORITY
- 5:Thread.NORM_PRIORITY
- 10:Thread.MAX_PRIORITY
- 线程可以通过setPriority方法更改优先级。优先级不能超出1-10的取值范围,否则抛出IllegalArgumentException
- 高优先级的线程比低优先级的线程有更高的几率得到执行
- 注意:不要假定高优先级的线程一定先于低优先级的线程执行,不要有逻辑依赖于线程优先级,否则可能产生意外结果
7.3 控制线程的方法
- 线程的休眠:
- 当前正在执行的线程休眠指定时间后再加入抢资源的队伍(即恢复就绪状态)
- Thread.sleep(毫秒)、sleep(毫秒,纳秒)
- 线程的让步:
- 当前正在执行的线程让出本次抢到的CPU资源,重新加入抢资源的队伍
- Thread.yield()
- 线程的阻塞:
- 已启动的某线程对象A.join()、join(毫秒)、join(毫秒,纳秒)
- 例如线程B中调用A.join()方法,那么表示B被阻塞,让A先执行完或者让A执行一段时间后,再恢复B至就绪状态
7.4 线程间通信
- 依赖Object类有wait和notify方法
方 法 | 功 能 |
---|---|
public final void wait() | 当前线程必须拥有此对象监视器。该线程发布对此监视器的所有权并等待。 |
public final void wait(long timeout) | 在其他线程调用此对象的 notify() 方法或 notifyAll() 方法,或者超过指定的时间量前,导致当前线程等待。 |
public final void wait(long timeout,int nanos) | 在其他线程调用此对象的 notify() 方法或 notifyAll() 方法,或者其他某个线程中断当前线程,或者已超过某个实际时间量前,导致当前线程等待。 |
public final void notify() | 唤醒在此对象监视器上等待的单个线程。 |
public final void notifyAll() | 唤醒在此对象监视器上等待的所有线程。 |
public final void notify() | 唤醒在此对象监视器上等待的单个线程。 |
8.同步
8.1 同步的实现方式
- 同步方法:直接在方法前面加synchronized
- 同步代码块:
- synchronized(对象锁){需要同步的操作代码}
- 对象锁(类比:令牌),只有持有对象锁的线程才能进入同步块中执行代码,否则只有等待
8.2 同步锁(lock) 也叫互斥锁(mutex)、监视锁(Monitor)
同步锁
- 在Java虚拟机中,每个对象和类(class对象)都关联了一把锁,或者说每个对象本身就是一把锁。
- 同步方法默认使用的就是this对象作为同步锁
- 如果每个线程不是共用一个线程类的对象的,那么使用this就不是同一把锁,所以有必要为这些线程重新指定一把锁
- 使用同步块所在类的class对象(使用最多)
- 使用同步块所在类的一个静态属性对象