Java多线程详解

2021/7/15 9:06:26

本文主要是介绍Java多线程详解,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

一、区分多任务与多线程

多任务 : 同一时间处理多个任务

多线程 : 相当于开设了 多条通道,提高了CPU使用的效率

  • 例子: 多个人同时登陆游戏

    ​ 主线程-main方法 和 其他方法-子线程 同时运行

    • 普通方法调用:

      image-20210702002514803

特点:只有主线程一条执行路径

    • 多线程:

      image-20210702002434194

特点:多条执行路径,主线程和子线程并行交替执行

二、区分程序、进程、线程

进程 Process

  • 在OS中运行的程序=>进程 e.g.QQ 播放器 游戏...

  • 一个进程可以有多个线程!如同时听声音、看图像、看弹幕...

  • 进程是执行程序的一次执行过程,动态的,

  • 系统资源分配的单位

  • 一个进程包含至少一个线程

线程 Thread :

  • CPU调度和执行的单位(CPU时间片)
  • 独立的执行路径
  • main()称为主线程,作为系统的入口,用于执行整个程序
  • 在程序运行时,即使没有自己创建线程,后台也会有多个线程,如主线程、gc线程
  • 多线程情况下,线程的运行需要调度器(CPU)安排调度,先后顺序无法人为干预!
  • 对同一份资源操作时,会出现资源抢夺的问题,需要加入并发控制
  • 线程会带来额外的开销:CPU调度时间+并发控制开销
  • 每个线程在自己的工作内存交互,内存控制不当会造成数据不一致!

三、线程创建的方式

主要有三种 :Thread、Runnable、Callable

Thread => class 继承Thread类

Runnable => 接口 实现Runnable接口

Callable => 接口 实现Callable接口 (了解)

main() : 用户线程

gc线程: 守护线程

开启线程的方法:

  • 1、Thread

    • 自定义线程类 继承 (extends)Thread类
    • 重写run()方法,编写线程执行体
    • 创建线程对象,调用start()方法!

    注意:线程不一定立即执行,而是由CPU安排调度

    不建议使用:避免OOP单继承局限性

  • 2、实现Runnable接口

  • 定义MyRunnable类实现Runnable接口

  • 实现run()方法,编写线程执行体

  • 创建线程对象,调用start()方法启动线程

    • 推荐使用Runnable对象,因为Java单继承的局限性(接口可以多次实现) 灵活方便,方便同一个对象被对多个线程使用
public class StartThread implements Runnable{
    
    @Override
    public void run(){
        // 线程体
        for(int i = 0; i <20; i++){
            System.out.println("我在学习Java多线程!");
        }
    }
    
    public static void main(String[] args){
        // 创建实现类对象
        StartThread st = new StartThread();
        // 创建代理类对象
        Thread thread = new Thread(st);
        
        // 启动线程
        thread.start();              
    }
}
  • 3、实现Callable接口 (了解)

    (1). 实现Callable接口,需要返回值类型(Boolean String....) 相当于返回一条消息

    (2). 重写call方法,需要抛出异常

    (3). 创建目标对象

    (4). 创建执行服务

    ExecutorService ser = Executors.newFixedThreadPool(1);
    

    (5). 提交执行

    Future<Boolean>result1 = ser.submit(t1);
    

    (6). 获取结果

    boolean r1 = result1.get();
    

    (7). 关闭服务

    ser.shutdownNow();
    

静态代理? vs Thread

即 Static Proxy

静态代理模式:

  • 真实对象和代理对象都要实现同一个接口
  • 代理对象要 代理 真实对象,即
// 静态代理 You ==>真实对象 WeddingCompany代理对象
        WeddingCompany weddingCompany = new WeddingCompany(new You());
        weddingCompany.marry();

new WeddingCompany(new You()).marry();
new Thread(()-> System.out.println("这里是目标对象!")).start();

好处:

  • 代理对象可以做很多真实对象做不了的事情
  • 真实对象可以专注做自己的事情

lambda表达式

  • λ 希腊字母表中排序第十一位的字母,英文名称为Lambda

  • 作用:避免匿名内部类定义过多

  • 实质:函数式编程

    new Thread(()-> System.out.println("多线程学习中!"));
    

(params) -> expression [表达式] or

(params) -> statement[语句] or

(params) -> { statements}

为什么要使用Lambda表达式?

  • 避免匿名内部类定义过多
  • 让代码看起来很简洁
  • 去掉没有意义的代码,只留下核心逻辑

函数式接口 ?

即 Functional Interface

  • Java8 更新了lambda表达式

定义:

  • 任何接口,如果只包含唯一一个抽象方法,即为一个函数式接口

  • 对于函数式接口。可以通过Lambda表达式来创建该接口!

    public interface Runnable {
        public abstract void run();
    }
    

    总结:

    • lambda表达式只能在只有一行代码的情况下才能简化成为一行

      如果有多行代码,需要用代码块包裹(前提:接口为函数式接口且只有一个)

    • lambda表达式中的多个参数也可以去掉参数类型,要去掉就全部去掉,多个参数需要用括号包起来

线程状态 (五大状态)

  • 创建状态
  • 就绪状态
  • 阻塞状态
  • 运行状态
  • 死亡状态

创建状态 -- 启动线程 --> 就绪状态

阻塞状态 -- 阻塞解除 --> 就绪状态

就绪状态 --获得CPU资源 --> 运行状态

就绪状态 <--释放CPU资源 -- 运行状态

运行状态 -- 等待用户输入/线程休眠--> 阻塞状态

运行状态 --线程自然执行完毕/外部干涉终止线程--> 死亡状态

image-20210704224827814

线程常用方法:

  • setPriority(int newPriority) 更改线程的优先级
  • static void sleep(long millis) 在指定的毫秒数内让当前正在执行的线程休眠
  • void join() 等待该线程终止(新线程此时无法进入就绪状态)
  • static void yield() 暂停当前正在执行的线程对象,并执行其他线程
  • void interrupt() 中断线程,不推荐使用
  • boolean isAlive() 测试线程是否处于活动状态

停止线程

  • 不推荐使用JDK提供的stop()、destory()方法 --> 已废弃

  • 推荐让线程自己停止下来

  • 建议使用一个标志位作为终止变量

    当flag = false 则终止线程运行



这篇关于Java多线程详解的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!


扫一扫关注最新编程教程