1.进程和线程
进程:由指令和数据组成,指令加载到CPU,数据加载到内存,那么进程启动。
进程栗子:浏览器,记事本...可以同时开多个 那么就是多进程
线程:一个线程就是一个指令流,线程将指令按顺序交给CPU
线程栗子:在记事本(进程)中保存,线程将保存的代码交给CPU,CPU执行IO操作。
注:java中线程作为最小调度单位,进程作为资源分配的最小单位
注2:windows中进程是不活动的,只是线程的容器
单核心CPU:
多核心CPU:
image.png
并发
Rob Pike(golang语言创始者):
并发(concurrent)是同一时间应对(dealing with)多件事情的能力。
并行(parallel)是同一时间动手做(doing)多件事情的能力。
并发:多线程问题,微观上单核心还是逐个执行每一个小指令
并行:栗子--java stream并行流 同时调度CPU所有核心来处理数据
2.为什么多线程可以提高效率?
2.1 同步和异步
1.同步
一般我们方法执行都是主线程在执行,方法会从上到下依次运行,上面的代码没有运行完后面的代码是不能运行的(同步的概念)
2.异步
多线程情况下,那么可以让新的线程去处理较为耗时的任务,主线程继续运行(异步的概念)。
栗子:用户点击执行某个复杂任务,如果不是多线程,那么必须等待执行任务的代码结束 才能告知用户这个指令执行完成。多线程可以让新线程去处理复杂的任务,主线程继续跑,跑完主线程告诉返回给用户,刚刚那个指令已经开启了。(异步)
3.线程创建方法
3.1 new Thread
//new thread
Thread newThread = new Thread("new_thread"){
@Override
public void run() {
log.info("print new thread and override run");
}
};
newThread.start();
//new thread by lambda
Thread newThreadLambda = new Thread(() -> log.info("print new thread override run by lambda"));
newThreadLambda.setName("new_thread_lambda");
newThreadLambda.start();
3.2 Runnable
//特点:任务和线程分开 写起来方便一些 毕竟实际中执行都是使用线程池
//new runnable
Runnable runnable = new Runnable() {
@Override
public void run() {
log.info("print new runnable thread and override run");
}
};
Thread runnableThread = new Thread(runnable);
runnableThread.start();
//new runnable by lambda
Runnable runnableLambda = () -> log.info("print new runnable thread and override run by lambda");
Thread runnableLambdaThread = new Thread(runnableLambda);
runnableLambdaThread.start();
3.3 Callable
//特点:继承了runnable(配合线程) 和 future(能够返回值,抛出异常)
//futureTask
FutureTask<Object> futureTask = new FutureTask<>(new Callable<Object>() {
@Override
public String call() throws Exception {
log.info("print new future task thread and override call");
Thread.sleep(3000);
return "FutureTask return";
}
});
new Thread(futureTask, "futureTask_thread").start();
//注意:main线程中 get方法要等call方法执行完 才能拿到返回值 才会执行
Object result = futureTask.get();
log.warn("result = {}", result.toString());
// futureTask by lambda
FutureTask<Object> futureTaskLambda = new FutureTask<>(() -> {
log.info("print new future task thread and override call by lambda");
Thread.sleep(5000);
return "FutureTask lambda return";
});
new Thread(futureTaskLambda, "futureTask_threadLambda").start();
Object resultLambda = futureTaskLambda.get();
log.warn("resultLambda = {}", resultLambda.toString());
4. 线程运行原理
4.1 栈
栈:包含:局部变量表,操作数栈,动态链接,方法返回值和一些附加信息
ps:在debug的时候我们看到的哪些属性 都是idea从栈帧里面获取的
线程启动--虚拟机为线程开辟一块栈内存
线程的每个方法运行在一个栈帧之上
4.2 线程的上下文切换 Context Switch
概念:因为一些原因导致cpu不在执行当前线程了,转而执行另一个线程代码
4.2.1 线程的cpu时间片用完
4.2.2 垃圾回收
因为垃圾回收会让所有线程停下来,
所有我们需要对jvm调优,来减少垃圾回收的次数
4.2.3 有更高优先级的线程需要运行
4.2.4 线程自己调用了sleep,yield,wait等方法
注意:上下文切换会操作系统帮你保存线程状态,包括:让程序计数器将指令地址记住(运行到哪一行了),局部变量,返回地址等 频繁的上下文切换 会
5. 线程安全问题
关键点:多个线程处理共享资源
关键点2:没有开多线程,每个方法有自己的线程栈,也就没线程安全问题(常规情况下)
image.png
感谢:
《Java并发编程的艺术》方腾飞,魏鹏,程晓明
《Java并发实现原理:JDK源码剖析》余春龙
全面深入学习java并发编程,java基础进阶中级必会教程_哔哩哔哩 (゜-゜)つロ 干杯~-bilibili
网友评论