0815(031天 线程/进程02)
每日一狗(田园犬西瓜瓜)
线程/进程02
文章目录
1. 进程
程序执行的整个过程叫做进程
1.1 三大特性
- 独立性
- 动态性
- 并发性
1.2 问:僵尸进程个孤儿进程
僵尸进程:子进程完了,父进程没有把子进程占用的资源进程回收,这个子进程就是僵尸进程(必须解决,站着内啥不内啥,严令禁止,杜绝浪费)
孤儿进程:父进程比子进程先退出,他会被初始化进程init进程所收养,由init负责改进程的资源管理(占就占呗,反正还没跑完)
1.3 并发和并行
- 并行:多CPU同时执行一段处理逻辑,是真正的同时执行(不是反复横跳)
- 并发:通过CPU调度算法,让人感觉是同时执行的,但是并不是,是他跑得快,你感觉不出来。同一时刻只有一个进程在执行
1.4 问:进程和线程的区别
- 调度:线程是CPU调度和分配的基本单位,进程是系统资源分配和调度的基本单位。
- 并发性:不只是进程之间可以实现并发执行,同一个进程中的多个线程之间也可以并发执行。
- 归属:一个进程至少拥有一个线程,一个线程只能也必须隶属于一个进程。
- 拥有资源:进程是拥有资源的一个最小的独立单位,线程不拥有系统资源,线程可以访问,但产权还是进程的。
- 操控者:进程是由操作系统来进行控制调度的,线程是由进程来进行调度处理的
- 切换成本:线程自身的数据通常都存储在寄存器中,以及一个程序执行是使用的堆栈,所以线程之间切换的成本远低于进程之间的切换成本。
- 独立性:同一进程中的线程本身就共享进程中的系统资源,彼此在调度会互相影响,而进程之间的资源都是互相独立的。
2. 线程
进程无法将CUP性能榨干,能让它闲着吗?不能够呀,聪明的开发人员由在进程之下划分了多个线程。
2.1 主线程
- 不是你写的程序起的,而是系统自动起的
- 人为命名,没有什么特殊的地方,就是一个进程
程序运行时,系统会先开一个进程,然后再自动开一个主线程,你的主方法就会在这个主线程中执行。
虽然说主线程和进程中的其他进程没啥不一样的,但是所谓的主线程main可以在执行完处理逻辑后调用System.exit()。exit会让整个进程over终止,那所有线程自然都会退出。
2.2 进程中的线程
进程中的线程都是平级的,没有父子之分。
- 单个线程只能属于单个进程,一个进程可以有多个线程
- 进程中的线程共享系统分配给进程的所有资源(系统分配资源的最小单位就是进程)
- 真正运行的是线程,而不是进程(进程是壳子,是用来存放资源的东西,而真正干活的是线程,进程不负责运行程序)
- 线程在执行过程中需要协作同步
2.4 多线程
- 提高代码伸缩性(多线程的单核是并发,在多核是并行)
- 提高CPU利用率(减少了CPU的空闲时间,你CPU先得有空闲时间我才有用,CPU忙的要死,你个这嘎嘎换,没什么用)
- 基于Internet的应用有必要使用多线程
不一定提高运行效率
在大规模的计算时,cpu本身就忙着,在用多线程,cpu会将正在执行的程序相关数据进行存储,这是有时间代价的。
这时候就需要进行线性扩展,让两个三个多个计算机来帮忙!
缺点:上下文切换的开销
代码实现
不能直接调用run方法,直接调用就是单线程干的事
执行过程不可重现 把握不住
package com.yang2;
public class Test01 {
public static void main(String[] args) {
Thread t1 = new Thread() {
public void run() {
for (int i = 0; i < 20; i++) {
System.out.println("左手重播"); // 阻塞200ms
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
Thread t2 = new MyThread();
t1.start();
t2.start();
}
}
class MyThread extends Thread {
@Override
public void run() {
for (int i = 0; i < 20; i++) {
System.out.println("右手重播");
try {
Thread.sleep(200); // 阻塞200ms
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
线程启动时调用start()
方法会默认调度执行其中的public void run() {}
方法
可以看到用run执行的程序的线程和主方法是统一个线程
package com.yang2;
public class Test02 {
public static void main(String[] args) {
System.out.println(Thread.currentThread());
// Thread[main,5,main]
Thread t1 = new Thread() {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println("右手" + Thread.currentThread());
try {
Thread.sleep(20);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
t1.start(); // 右手Thread[Thread-0,5,main]
Thread t2 = new MyThread1();
t2.run(); // 左手Thread[main,5,main]
}
}
class MyThread1 extends Thread {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println("左手" + Thread.currentThread());
try {
Thread.sleep(20);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
2.5 问:实现线程的四种方法
-
继承Thread(单根继承,使用受限)
- 直接继承
- 匿名内部类
-
实现Runnable接口
- 实现
- 匿名内部类
- lambda表达式
-
FutureTask可以有返回值的
-
使用线程池
Thread
public class Test1 {
public static void main(String[] args) {
System.out.println(Thread.currentThread()); //获取当前线程对象
//方法1.2
Thread t2=new Thread() {
@Override
public void run() {
for(int i=0;i<50;i++) {
System.out.println("右手慢动作重播"+Thread.currentThread());
try {
Thread.sleep(150);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
// t2.run();
Thread t1=new MyThread3();
t1.start();
t2.start();
}
}
//方法1.1
class MyThread3 extends Thread{
@Override
public void run() {
for(int i=0;i<50;i++) {
System.out.println("左手一个慢动作"+Thread.currentThread());
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
Runnable
public class Test2 {
public static void main(String[] args) {
Thread t1 = new Thread(new MyRunnable());
t1.start();
// 方法2.2
new Thread(new Runnable() {
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println("中间手...."+Thread.currentThread());
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
// 方法2.3
new Thread(() -> {
for (int i = 0; i < 10; i++) {
System.out.println("右手...."+Thread.currentThread());
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
}
}
//方法2.1
class MyRunnable implements Runnable {
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println("左手...."+Thread.currentThread());
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
FutureTask
package com.yang2;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
public class Test06 {
public static void main(String[] args) throws InterruptedException, ExecutionException {
// 为了获取返回值,所以需要使用FutureTask中提供的get方法
FutureTask<Integer>[] arr = new FutureTask[10];
for (int i = 1; i <= 10; i++) {
int begin = (i - 1) * 100 + 1;
int end = i * 100;
// 定义Callable的实现
Callable<Integer> c = new Callable<Integer>() {
@Override
public Integer call() throws Exception {
int res = 0;
for (int i = begin; i <= end; i++) {
res += i;
}
return res;
}
};
// 构建FutureTask对象,其中包含Callable对象
FutureTask<Integer> ft = new FutureTask<Integer>(c);
arr[i - 1] = ft;
// 启动线程,构建Thread对象时,要求参数类型为Runnable接口类型
// 线程执行时会自动调用run方法,而FutureTask中提供的run方法会调用Callable对象的call方法
new Thread(ft).start();
}
int res = 0;
for (FutureTask<Integer> tmp : arr) {
// main线程执行到这里时,会自动阻塞等待子线程tmp执行结束,执行结束后获取call方法的返回值
res += tmp.get();
}
System.out.println(res);
}
}
线程池
package com.yang2;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.FutureTask;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
public class Test08 {
public static void main(String[] args) {
// 固定线程池
// ExecutorService es = Executors.newFixedThreadPool(3); // 就三个
// 缓存线程池
ExecutorService es = Executors.newCachedThreadPool(); // 这有十个
FutureTask<Integer>[] arr = new FutureTask[10];
for (int i = 1; i <= 10; i++) {
int begin = (i - 1) * 100 + 1;
int end = i * 100;
arr[i - 1] = new FutureTask<>(new Callable<Integer>() {
@Override
public Integer call() throws Exception {
System.out.println(Thread.currentThread());
int res = 0;
for (int i = begin; i <= end; i++) {
res += i;
}
return res;
}
});
es.submit(arr[i - 1]);
}
int res = 0;
for (Future<Integer> tmp : arr) {
try {
res += tmp.get(1, TimeUnit.SECONDS);
} catch (InterruptedException | ExecutionException | TimeoutException e) {
e.printStackTrace();
}
}
System.out.println(res);
}
}
方法没有返回值
想要将运算结果拿出来在计算就难受 了
- 用实现类搞个属性,存起来
package com.yang2;
public class Test04 {
public static void main(String[] args) throws InterruptedException {
MyRunnable04[] arr = new MyRunnable04[10];
for (int i = 1; i <= 10; i++) {
MyRunnable04 r = new MyRunnable04((i - 1) * 100 + 1, i * 100);
arr[i - 1] = r;
new Thread(r).start();
}
Thread.sleep(2000);
for (MyRunnable04 r : arr) {
System.out.println(r.getRes());
}
}
}
class MyRunnable04 implements Runnable {
private int begin, end, res = 0;
public MyRunnable04(int begin, int end) {
this.begin = begin;
this.end = end;
}
public int getRes() {
return res;
}
@Override
public void run() {
for (int i = begin; i <= end; i++) {
res += i;
}
}
}
- 用全局变量实现类加
package com.yang2;
public class Test05 {
static int sum = 0;
public static void main(String[] args) throws InterruptedException {
for (int i = 1; i <= 10; i++) {
int begin = (i - 1) * 100 + 1;
int end = i * 100;
new Thread(new Runnable() {
@Override
public void run() {
int res = 0;
for (int i = begin; i <= end; i++) {
res += i;
}
sum += res;
}
}).start();
}
Thread.sleep(5000);
System.out.println(sum);
}
}
2.6 问:针对OOM
OOM可能原因
- 数据库的cursor没有及时关闭
- 未关闭输入输出流
- Bitmap使用后未调用recycle()
- static等关键字
- 非静态内部类持有外部类的引用context泄露
- 流量/数据量峰值:在某一段时间内,他的使用需求量突然激增。超出了设计初期的阈值
- 内存泄露
2.7 线程池
- 一个池子
- 减少线程的创建,提高对象的服用,提升性能
- 核心线程、阻塞队列、工作流程、拒绝策略
工作流程
核心线程->阻塞队列->工作线程->饱和策略/拒绝策略
核心线程永不回收(就是把已经存在的线程数量回收到核心线程数时就不回收了)
问:一个构造器的参数
public ThreadPoolExecutor(等等){}
- int corePoolSize:核心线程数量
- int maximumPoolSize,:线程池中允许的最大线程数
- long keepAliveTime,:线程空闲最大时长
- TimeUnit unit,:时长单位
- BlockingQueue workQueue,:阻塞任务队列
- ThreadFactory threadFactory,:线程工厂
- RejectedExecutionHandler handler:拒绝策略/饱和处理策略
应用
package com.yang2;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.FutureTask;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
public class Test08 {
public static void main(String[] args) {
// ExecutorService es = Executors.newFixedThreadPool(3); // 就三个
ExecutorService es = Executors.newCachedThreadPool(); // 这有十个
FutureTask<Integer>[] arr = new FutureTask[10];
for (int i = 1; i <= 10; i++) {
int begin = (i - 1) * 100 + 1;
int end = i * 100;
arr[i - 1] = new FutureTask<>(new Callable<Integer>() {
@Override
public Integer call() throws Exception {
System.out.println(Thread.currentThread());
int res = 0;
for (int i = begin; i <= end; i++) {
res += i;
}
return res;
}
});
es.submit(arr[i - 1]);
}
int res = 0;
for (Future<Integer> tmp : arr) {
try {
res += tmp.get(1, TimeUnit.SECONDS);
} catch (InterruptedException | ExecutionException | TimeoutException e) {
e.printStackTrace();
}
}
System.out.println(res);
}
}
优点
- 重用存在的线程,减少对象创建、消亡的开销,性能佳
- 可有效控制最大并发线程数,提高系统资源的使用率,同时避免过多资源竞争,避免堵塞
- 提供定时执行、定期执行、单线程、并发数控制等功能。
享元模式
用一个池子来管理一堆资源对象,需要的时候你来我这个池子要,池子给你一个,用完了不要释放对象,你在还给我这个池子,你下次再用,我再来问你要。
公共接口:池子里的东西都间接或直接的实现该接口
interface IShape {
void draw();
}
实现类:池子里放的东西
class Circle implements IShape {
@Override
public void draw() {
System.out.println("这里是三角形");
}
}
调度管理类:要东西问这个类要,用完记得归还
class ObjectManager {
static IShape[] arr = new IShape[20];
static {
for (int i = 0; i < 20; i++) {
arr[i] = new Circle();
}
}
public static IShape getInstance() {
IShape res = null;
for (int i = arr.length - 1; i >= 0; i--) {
if (arr[i] != null) {
res = arr[i];
arr[i] = null;
break;
}
}
return res;
}
public static void releaseInstance(IShape obj) {
for (int i = 0; i < arr.length - 1; i++) {
if (arr[i] == null) {
arr[i] = obj;
break;
}
}
}
}
测试类:有借有还再借不难,但是这个没用多线程,所以全都是第一个类
package com.yang2;
public class Test07 {
public static void main(String[] args) {
for (int i = 0; i < 100; i++) {
IShape tmp = ObjectManager.getInstance();
System.out.println(tmp);
ObjectManager.releaseInstance(tmp);
}
}
}
2.8 其他问题
守护线程
打辅助的
当前进程中除了守护线程之外没线程了,守护线程这会就关了。
线程组
可以对线程组内的线程进行统一管理
扩展小芝士
- 并行的话要求软硬件同时支持并行才行。
- 接手跑路代码(能不改源码就不改源码,要用人家的功能继承过来用)
- juc包:并发包(java.util.concurrent)
- 谈池字先谈享元模式
- 单线程池子存在,还有用,控制性能
- 线程池有叫执行框架