-
多线程学习(上)
1、基本概念:程序、进程、线程
程序
- 完成特定任务、用某种语言编写的一组指令的集合。一段静态的代码,静态对象。
进程
- 程序的一次执行过程,或是正在运行的一个程序,动态的过程:产生、存在、和消亡
- 程序是静态的,进程是动态的
- 进程是资源分配的单位
线程
-
线程是程序内部的一条执行路径
-
一个进程,同一时间并行多个线程,多线程
-
线程作为调度和执行的单位,每个线程拥有独立的运行栈和程序计数器
-
存在安全问题
-
一个java应用程序,至少有三个线程:main()主线程,
使用多线程的优点
- 提高应用程序的响应,增强用户体验
- 提高计算机系统CPU的利用率
- 改善程序结构,将复杂的进程分为多个线程,独立运行
何时需要多线程
- 程序需要同时执行两个或多个任务
- 程序实现一些需要等待的任务时,如:用户输入,文件读写操作、网络操作
- 需要后台运行的程序
单核CPU和多核CPU
单核CPU
- 假的多线程,在一个时间单元内,只能执行一个线程的任务
- 实际上是单线程,运行中将其他进程挂起,执行,执行速度很快,所以在使用上感觉是多线程。
多核CPU
- 真正意义上的多线程
并行与并发
- 并行:多个CPU同时执行多个任务
- 并发:一个CPU “同时” 执行多个任务,如:秒杀
2、线程的创建和使用
线程的创建
-
方式一
-
继承于Thread类
步骤:
- 创建一个继承于Thread的子类
- 重写Thread类的run()方法--》此线程执行的操作
- 创建Thread类的子类对象(可使用匿名对象)
- 通过此对象调用start()
- 若要再创建新的线程,需创建新的实例来调用start()方法
//例:遍历100以内的所有偶数 //1.创建一个继承于Thread类的子类 public class MyThread extends Thread{ //2.重写Thread类的run() @Override public void run() { for (int i = 0; i <=1000 ; i++) { if(i%2==0){ System.out.println(Thread.currentThread().getName()+":"+i); } } } } class ThreadTest { public static void main(String[] args) { //3.创建Thread类的子类的对象 MyThread myThread= new MyThread(); //4.通过此对象调用start()①启动当前线程②调用当前线程的run() myThread.start(); // 如下操作任然是在main()线程执行的 for (int i = 0; i <=1000 ; i++) { if(i%2!=0){ // Thread.currentThread().getName()获取线程名 System.out.println(Thread.currentThread().getName()+":"+i); } } } }
-
-
方式二
-
实现Runnable接口
步骤:
-
创建一个实现了Runnable接口的类
-
实现类中实现Runnable中的抽象方法:run()
-
创建实现类的对象
-
将此对象作为参数传递到Thread类的构造器中,创建Thread类的对象
-
通过Thread类的对象调用start()
//1. 创建一个实现了Runnable接口的类 class MIThread implements Runnable{ // 2. 实现类中实现Runnable中的抽象方法:run() @Override public void run() { for (int i = 0; i < 100; i++) { if(i%2==0){ System.out.println(Thread.currentThread().getName()+":"+i); } } } } class ThreadTes{ public static void main(String[] args) { // 3. 创建实现类的对象 MIThread mi= new MIThread() ; // 4. 将此对象作为参数传递到Thread类的构造器中,创建Thread类的对象 Thread t1=new Thread(mi); // 5. 通过Thread类的对象调用start(),启动当前线程,调用当前线程的run方法,底层是Thread含有Runnable类型参数的构造器 t1.start(); // 再启动一个线程 Thread t2=new Thread(mi); t2.start(); } }
-
-
-
比较两种创建方式
-
开发中优先选择,实现Runnable接口的方式
-
原因:
- 实现的方式没有类的单继承的局限
- 实现方式更适合处理多线程有共享数据的情况
-
联系: public class Thread implements Runnable
相同点:两种方式都需要重写run() 将线程 要执行的逻辑声明在run()中;
-
线程的调度
-
调度策略
- 时间片
- 抢占式,高优先级的线程抢占CPU
-
java的调度方法
- 先来先服务,先进先出队列
- 高优先级,优先调度的抢占式策略
线程优先级
-
优先级
- MAX_PRIORITY : 10
- MIN_PRIORITY: 1
-
NORM_PRIORITY : 5
-
获取和设置当前线程的优先级
- getPriority()
- setPriority( int p)
-
高优先级的线程要抢占低优先级线程的CPU执行权,只是从概率上讲,高优先级有很高概率会执行,但并不意味着高优先级的线程执行完,低优先级的才执行;
3、线程的生命周期
线程生命周期的五种状态
-
新建:Thread类或子类对象被声明创建时
-
就绪:新建的线程被start()后,获取了其他所有的资源,只是没分配到CPU资源
-
运行:获取CPU资源,执行
-
阻塞:某种特殊情况下,让出CPU并临时终止自己的执行
-
死亡:完成它的全部工作或线程提前被强制性的终止,异常导致结束;
-
状态图