线程
程序 进程 线程
程序:指令集 静态概念
进程:操作系统 调度程序 动态概念
线程:在进程内多条执行路径
线程的基本概念
线程是一个程序内部的顺序控制流。
线程和进程的区别
每个进程都有独立的代码和数据空间(进行上下文),进程间的切换会有较大的开销。
线程可以看成是轻量级的进程,同一类线程共享代码和数据空间,每个线程有独立的运行栈和程序计数器,线程切换的开销小。
多进程:在操作系统中能同时运行多个任务(程序)。
多线程:在同一应用程序中有多个顺序流同时执行。
java的线程是通过java.lang.Thread类实现的。
VM启动时会有一个由主方法(public static void main(){})所定义的线程。
可以通过创建Thread的实例来创建新的线程。
每个线程都是通过某个特定Thread对象所对应的方法run()来完成其操作的,方法run()称为线程体。
通过调用Thread类的start()方法来启动一个线程。
线程的创建和启动
可以有两种方式创建新的线程。
第一种
定义线程类实现Runnable接口
Thread myThread = new Thread(target); // target为Runnable接口类型
Runnable中只有一个方法:
public void run(); // 用以定义线程运行体
使用Runnable接口可以为多个线程提供共享的数据。
在实现Runnbale接口的类的run方法定义中可以使用Thread的静态方法:
public static Thread currentThread() 获取当前线程的引用
第二种
可以定义一个Thread的子类并重写其run方法如:
class MyThread extends Thread {
public void run() {…}
}
然后生成该类的对象:
MyThread myThread = new MyThread(…);
线程状态转换
新建状态:
使用 new 关键字和 Thread 类或其子类建立一个线程对象后,该线程对象就处于新建状态。它保持这个状态直到程序 start() 这个线程。
就绪状态:
当线程对象调用了start()方法之后,该线程就进入就绪状态。就绪状态的线程处于就绪队列中,要等待JVM里线程调度器的调度。
运行状态:
如果就绪状态的线程获取 CPU 资源,就可以执行 run(),此时线程便处于运行状态。处于运行状态的线程最为复杂,它可以变为阻塞状态、就绪状态和死亡状态。
阻塞状态:
如果一个线程执行了sleep(睡眠)、suspend(挂起)等方法,失去所占用资源之后,该线程就从运行状态进入阻塞状态。在睡眠时间已到或获得设备资源后可以重新进入就绪状态。可以分为三种:
等待阻塞:运行状态中的线程执行 wait() 方法,使线程进入到等待阻塞状态。
同步阻塞:线程在获取 synchronized 同步锁失败(因为同步锁被其他线程占用)。
其他阻塞:通过调用线程的 sleep() 或 join() 发出了 I/O 请求时,线程就会进入到阻塞状态。当sleep() 状态超时,join() 等待线程终止或超时,或者 I/O 处理完毕,线程重新转入就绪状态。
死亡状态:
一个运行状态的线程完成任务或者其他终止条件发生时,该线程就切换到终止状态。
停止线程
1、自然终止:线程体正常执行完毕
2、外部干涉:
线程类中 定义 线程体使用的标识
线程体使用该标识
提供对外的方法改变该标识
外部根据条件调用该方法
阻塞
1、join:合并线程
2、yield:暂停自己的线程
3、sleep:休眠,不释放锁
与时间相关:倒计时
模拟网络延时
菜鸟教程
通过 Callable 和 Future 创建线程
1. 创建 Callable 接口的实现类,并实现 call() 方法,该 call() 方法将作为线程执行体,并且有返回值。
2. 创建 Callable 实现类的实例,使用 FutureTask 类来包装 Callable 对象,该 FutureTask 对象封装了该 Callable 对象的 call() 方法的返回值。
3. 使用 FutureTask 对象作为 Thread 对象的 target 创建并启动新线程。
4. 调用 FutureTask 对象的 get() 方法来获得子线程执行结束后的返回值。
实例
public class CallableThreadTest implements Callable { public static void main(String[] args) { CallableThreadTest ctt = new CallableThreadTest(); FutureTask ft = new FutureTask<>(ctt); for(int i = 0;i < 100;i++) { System.out.println(Thread.currentThread().getName()+" 的循环变量i的值"+i); if(i==20) { new Thread(ft,“有返回值的线程”).start(); } } try { System.out.println(“子线程的返回值:”+ft.get()); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } } @Override public Integer call() throws Exception { int i = 0; for(;i<100;i++) { System.out.println(Thread.currentThread().getName()+" "+i); } return i; } }
线程的基本信息
isAlive() 判断线程是否还"活"着,即线程是否还未终止。
getPriority() 获得线程的优先级数值
setPriority() 设置线程的优先级数值
setName() 给线程一个名字
getName() 取得线程的名字
currentThread() 取得当前正在运行的线程对象也就是取得自己本身
线程同步
同步:也称为并发,多个线程访问同一份资源,确保资源安全,叫做线程安全。
synchronized --> 同步
同步块
synchronized(引用类型|this|类.class){
}
同步方法
synchronized
线程死锁
多线程如果设计的不合理的话,很可能就会出现死锁.当两个或者多个线程同时想要去获取共享资源的锁时,但每个线程都要等其他线程把他们各自的锁给释放,才能继续运行,这就是死锁.出现死锁必须具备以下几点
- 要有两个或两个以上的线程
- 至少有两个共享资源的锁
- 至少存在两个线程各自拥有一个锁
- 现在这两个线程在等待获取彼此的锁,这就出现死锁了
生产者消费者模式
生产者消费者问题是多线程的一个经典问题,它描述是有一块缓冲区作为仓库,生产者可以将产品放入仓库,消费者则可以从仓库中取走产品。
采用某种机制保护生产者和消费者之间的同步;
任务调度(定时器|定时任务)
Timer定时器类
TimerTask任务类