基础概念
什么是线程和进程
线程
线程是 CPU 调度的最小单位,不能独立于进程存在,可以共享进程中的资源。
进程
操作系统进行资源分配的最小单位。资源指的就是内存空间、磁盘IO...
进程与进程之间是相互独立的。
CPU 核心数和线程数的关系
物理核心数:
物理 CPU 就是计算机上实际配置的 CPU 个数。
CPU 核数:
单块 CPU 上面能处理数据的芯片组的数量,如双核bai、四核等
逻辑 CPU 数
操作系统可以使用逻辑 CPU 来模拟出真实 CPU 的效果。在之前没有多核处理器的时候,一个 CPU 只有一个核,而现在有了多核技术,其效果就好像把多个 CPU 集中在一个 CPU 上。
当计算机没有开启超线程时,逻辑 CPU 的个数就是计算机的核数。而当超线程开启后,逻辑 CPU 的个数是核数的两倍。
一个物理核心上有多个处理器。
CPU 总核数 = 物理 CPU 个数 X 每颗物理 CPU 的核数
CPU 逻辑数 = 物理 CPU 个数 X 每颗物理 CPU 的核数 X 超线程数
CPU 时间片轮转机制 (RR 调度)
时间片轮转调度是一种最古老,最简单,最公平且使用最广的算法。每个进程被分配一个时间段,称作它的时间片,即该进程允许运行的时间。
进程调用会进行上下文切换,损耗性能。
并行和并发
并行
可以同时运行的任务数。
并发
并发是不能脱离时间单位的,指的是单位时间内能够处理的任务数。
高并发的意义、好处和注意事项
好处
- 可以充分的利用 CPU 的资源
- 加快响应用户的时间
- 可以是代码模块化,异步化。
注意事项
- 线程安全问题
- 死锁问题
- 线程数问题
- 一个进程在 Linux 中1000/window 中 2000,线程会分配栈空间,会消耗资源。
Java 线程
Java 程序本身就是一个多线程的程序。
public class Main {
public static void main(String[] args) {
ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean();
ThreadInfo[] threadInfos = threadMXBean.dumpAllThreads(false, false);
for (ThreadInfo threadInfo : threadInfos) {
System.out.println("id + " + threadInfo.getThreadId() + "name + " + threadInfo.getThreadName() + "state + " +threadInfo.getThreadState());
}
}
}
id + [1] : name + mainstate + RUNNABLE
id + [2] : name + Reference Handlerstate + RUNNABLE
id + [3] : name + Finalizerstate + WAITING
id + [4] : name + Signal Dispatcherstate + RUNNABLE
id + [10] : name + Common-Cleanerstate + TIMED_WAITING
id + [11] : name + Monitor Ctrl-Breakstate + RUNNABLE
id + [12] : name + Notification Threadstate + RUNNABLE
新启线程的方式
两种,在Thread
中声明的。
通过类 Thread
package com.sail;
public class NewThead {
private static class UseThread extends Thread {
@Override
public void run() {
super.run();
// 做操作
System.out.println("UseThread:" + Thread.currentThread().getName());
}
}
public static void main(String[] args) {
System.out.println("main:" + Thread.currentThread().getName());
UseThread useThread = new UseThread();
useThread.start();
}
}
main:main
UseThread:Thread-0
通过接口Runnable
package com.sail;
public class NewThreadTwo {
private static class UserRunnable implements Runnable{
@Override
public void run() {
//做事情
System.out.println("UserRunnable:" + Thread.currentThread().getName());
}
}
public static void main(String[] args) {
System.out.println("main:" + Thread.currentThread().getName());
UserRunnable userRunnable = new UserRunnable();
new Thread(userRunnable).start();
}
}
main:main
UserRunnable:Thread-0
区别
Thread 是对线程的抽象。
Runnable 是对任务(业务逻辑)的抽象。
如何让线程安全的停止工作
stop()
会导致线程占用的资源不会正常的释放。
interrupt()
是对线程进行中断,并不会真正的终止线程,只是给线程一个中断标识(ture),告诉线程该中断了。
可以说明在 JDK 中,线程是协作式的,并不是抢占式的。
interrupted()
判定当前线程是否被中断,监听到 interrupt()
后,会将 中断标识结果为由 true
变成 false
;
isInterrupted()
判定当前线程是否被中断,监听到 interrupt()
后,isInterrupted
结果为 true
,其实就是返回了中断标识。
启动线程
run()
run()
只是一个成员方法,可以被反复调用,单独调用 run()
并不会启动线程。
start()
调用 native
方法启动线程
如果多次调用 start()
会抛出异常。
生命周期
image.pngyield()
当前线程让出 CPU
执行权,重新分配 CPU
的占用权,不会让出锁。
join()
A 正在执行时,B 调用 join()
,A 会让出执行权,等 B 执行完之后再执行。
Q:如何保证两个线程顺序执行
A:使用 join()
方法。
线程的优先级
setPriority(1); 1 ~ 10 默认为5
真正能不能发挥作用由操作系统决定。
不能控制线程的执行顺序。
守护线程
处理一些支持性的工作,通过 new thread
启动的线程都是用户线程,非守护线程。由JDK启动或者配置的称为守护线程。
在一个进程中,所有的非守护线程执行完之后,进程就跟着停止了。
自己配置守护线程
线程对象.setDaemon(true);
守护线程中的 finally
不一定起作用,取决于 CPU
分配的时间片是否刚好执行到此线程。
用户线程的 finally
是一定会执行的。
synchronized 内置锁
保证某一个时刻只有一个线程访问某个方法。
用法
-
同步块
image.png
-
方法
image.png其实锁的就是
this
对象。
对象锁
上面的用法都是锁的一个对象,叫做对象锁。
如果同时有多个对象,还是可以存在并行进行。
类锁
在 static 方法上加锁的话,锁的是一个 class
对象,虚拟机为每个类加载都会有的唯一的对象。其实也是对象锁,只是锁的 class
类对象。
错误的加锁
没有锁的同一个对象,导致发送错误。
volatile
最轻量的同步机制,保证的是 可见性
当一个值在多线程下修改的时候,能够立即同步到其他线程。
网友评论