带你从头到脚学习多线程2--基础篇
Q:从这篇文章你能学到什么?
A:谈到多线程,涉及的方面简直是多到爆。对于java后台来说,多线程是必修中的必修。但是对于我们android开发来说,多线程也是非常非常重要的,就算实际开发中比较少用到的,但是为了面试,我们也得拼命学,你说是不是?
屁话:
其实开发中慢慢懂的一些道理,就是开发项目过程中遇到一些新需求,这些需求用到的知识点你可能稍微有点了解,一般我们先照着这个你知道的方向去BD CV过来(进度永远大于一切,你懂的),然后有空再完善学习。那是因为你对这个知识还是有一点的基础了解的,这样你才不会一点头绪都没有去寻找答案。但是当你遇到一个完全陌生的需求,你根本不知道该用android什么技术去实现它。这时候你就慌了,其实是你没去接触到这个知识领域。我说这话的目的就是我们对于一些开发中还未接触的知识,我们需在平时有空的时候多去学习新领域,在脑海中形成一个基本的知识印象,等到真正开发实践就能够知道往哪个方向去解决问题,不会毫无方向。说了这么多的屁话,就是为了让你好好跟着我学习这边文章。
撸起袖子开始啦!注意:本编文章只是提供多线程学习的一些思路,对一些多线程知识一点都不了解的可能需要自己先去稍微学习下基本知识!
一、什么是线程,什么是进程?
说白了,进程就是我们手机上的一个软件,像我们的微信这个软件,这个就是一个进程。那什么又是线程呢?线程就是进程里面的一个最小调度单位,就像是微信里面的一个任务,像你正在和MM聊天,这个任务就是一个线程。懂了吧!
官方解释就是:一个基本的CPU执行单元 & 程序执行流的最小单元
1.比进程更小的可独立运行的基本单位,可理解为:轻量级进程
2.组成:线程ID + 程序计数器 + 寄存器集合 + 堆栈
3.注:线程自己不拥有系统资源,与其他线程共享进程所拥有的全部资源。
二、线程之间的状态转换
线程状态转换.png重点理解就绪、运行和阻塞三个状态
三、 线程分类
线程主要分为:守护线程、非守护线程(用户线程)
3.1 守护线程
定义:守护用户线程的线程,即在程序运行时为其他线程提供一种通用服务
常见:如 垃圾回收线程
设置方式:
//设置该线程为守护线程
thread.setDaemon(true);
3.2 非守护线程(用户线程)
主要包括:主线程 & 子线程。
a. 主线程(UI线程)
定义:Android系统在程序启动时会自动启动一条主线程
作用:处理四大组件与用户进行交互的事情(如UI、界面交互相关)
注:因为用户随时会与界面发生交互,因此主线程任何时候都必须保持很高的响应速度,所以主线程不允许进行耗时操作,否则会出现ANR
b. 子线程(工作线程)
定义:手动创建的线程
作用:耗时的操作(网络请求、I/O操作等)
3.3 守护线程 与 非守护线程的区别
区别:虚拟机是否已退出:
当所有用户线程结束时,因为没有守护的必要,所以守护线程也会终止,虚拟机也同样退出;
反过来,只要任何用户线程还在运行,守护线程就不会终止,虚拟机就不会退出
4.多线程引入
4.1 定义
多个线程同时进行,即多个任务同时进行
其实,计算机任何特定时刻只能执行一个任务;
多线程只是一种错觉:只是因为JVM快速调度资源来轮换线程,使得线程不断轮流执行,所以看起来好像在同时执行多个任务而已
4.2 作用
Android官方声明:在多线程编程时有两大原则:
1.不要阻塞UI线程(即主线程):单线程会导致主线程阻塞,然后出现ANR错误:主线程被阻塞超过5s则会出现错误
2.不要在UI线程之外更新UI组件
所以,我们需要多线程(1个主线程+x个工作线程)来解决上述两个问题:
1)将耗时任务放在工作线程中进行
对应原则:不要阻塞UI线程(即主线程),即当我们有耗时的任务,如果在UI线程中执行,那就会阻塞UI线程了,必须要抛到工作线程中去执行;
2)将更新UI组件放在主线程中进行
对应原则:不要在UI线程之外访问UI组件,即更新UI组件时,一定得在UI线程里执行,故需要在工作线程中执行的任务结果返回到UI线程中去更新组件
五、常见的创建多线程的四种方式
1.继承Thread类
public static void main(String[] args) {
Thread mThread=new MyThread();
mThread.start();
}
public static class MyThread extends Thread{
@Override
public void run() {
System.out.println("");
}
}
2.实现Runable接口
public static void main(String[] args) {
Thread mThread=new Thread(new MyRunnable());
mThread.start();
}
public static class MyRunnable implements Runnable {
@Override
public void run() {
System.out.println("");
}
}
3.实现callable接口(区别:含返回值)
okhttp源码中把请求添加到等待队列和运行队列就是实现了callable接口的线程
FutureTask<String> task=new FutureTask<>(new Callable<String>() {
@Override
public String call() throws Exception {
System.out.println(Thread.currentThread().getName());
return "done";
}
});
new Thread(task).start();
4.使用线程池
Executors.newCachedThreadPool().submit(new Runnable() {
@Override
public void run() {
System.out.println("doing something");
}
});
这篇文章主要介绍一下关于多线程的基本概念,这只是预热,下面我们学习多线程的并发知识,下一篇《带你从头到脚学习多线程3--并发篇》https://www.jianshu.com/p/c4731687930c