多线程基础整理
1.进程 与 线程
进程:是指一个内存中运行的应用程序,每个进程都有一个独立的内存空间
线程:是进程中的一个执行路径,共享一个内存空间,线程之间可以自由切换,并发执行. 一个进程最少有一个线程
线程实际上是在进程基础之上的进一步划分,一个进程启动之后,里面的若干执行路径又可以划分
成若干个线程
2.线程调度
分时调度
所有线程轮流使用 CPU 的使用权,平均分配每个线程占用 CPU 的时间。
抢占式调度
优先让优先级高的线程使用 CPU,如果线程的优先级相同,那么会随机选择一个(线程随机性),Java使用的为抢占式调度。
CPU使用抢占式调度模式在多个线程间进行着高速的切换。对于CPU的一个核新而言,某个时刻,只能执行一个线程,而 CPU的在多个线程间切换速度相对我们的感觉要快,看上去就是 在同一时刻运行。 其实,多线程程序并不能提高程序的运行速度,但能够提高程序运行效率,让CPU的 使用率更高。
3.并发与并行
并发:指两个或多个事件在同一个时间段内发生。
并行:指两个或多个事件在同一时刻发生(同时发生)。
4.多线程类Thread的常用方法
Thread常用的构造方法
Thread() 分配新的 Thread对象。
Thread?(Runnable target) 分配新的 Thread对象。
Thread?(Runnable target, String name) 分配新的 Thread对象。
普通的方法以及静态的方法,想要结束线程,不能使用stop(),可能是导致正在执行的线程来不及释放内存,可以设置标志位,来判断是否放要结束
long getId() 返回此Thread的标识符。
String getName() 返回此线程的名称。
int getPriority() 返回此线程的优先级。
void setPriority?(int newPriority) 更改此线程的优先级。
void start() 导致此线程开始执行; Java虚拟机调用此线程的run方法。
static void sleep?(long millis) 导致当前正在执行的线程休眠(暂时停止执行)指定的毫秒数,具体取决于系统计时器和调度程序的精度和准确性。
static void sleep?(long millis, int nanos) 导致当前正在执行的线程休眠(暂时停止执行)指定的毫秒数加上指定的纳秒数,具体取决于系统定时器和调度程序的精度和准确性。
void setDaemon?(boolean on) 将此线程标记为 daemon线程或用户线程
5.实现Runnable 与 继承Thread 相比的优势
/**
* 1. 通过创建任务,然后给线程分配任务的方式来实现多线程,更适合多个线程同时执行相同任务的情况
* 2. 可以避免单继承带来的局限性
* 3. 任务与线程本身时分离的,提高了程序的壮性
* 4. 后续学习的线程池技术,只接受runnalble类型的任务,而不接受Thread类型的线程
*
* 5. Thread也有用处,当使用匿名内部类方法,即只调用一次时,很方便
/**
* 匿名内部类
*/
new Thread(){
@Override
public void run() {
//线程执行的内容
}
}.start();
6.线程分为守护线程和用户线程
用户线程:当一个进程不包含任何存货的用户线程时,进行结束(平时创建的都是用户线程,如mian)
守护线程:守护用户线程,当最后一个用户线程结束时,守护线程自动死亡(创建完用户线程时。调用Thread的setDaemon(true)方法,即可改为守护线程)
7.显式锁 和 隐式锁的区别
(1)出生不同
Sync:Java中的关键字,是由JVM来维护的。是JVM层面的锁。
Lock:是JDK5以后才出现的具体的类。使用lock是调用对应的API。是API层面的锁
sync是底层是通过monitorenter进行加锁(底层是通过monitor对象来完成的,其中的wait/notify等方法也是依赖于monitor对象的。只有在同步块或者是同步方法中才可以调用wait/notify等方法的。因为只有在同步块或者是同步方法中,JVM才会调用monitory对象的);通过monitorexit来退出锁的。
而lock是通过调用对应的API方法来获取锁和释放锁的
(2)使用方式不同:所谓的显示和隐式就是在使用的时候,使用者要不要手动写代码去获取锁和释放锁的操作。
(3)等待是否中断
Sync是不可中断的。除非抛出异常或者正常运行完成
Lock可以中断的。中断方式:
1:调用设置超时方法tryLock(long timeout ,timeUnit unit)
2:调用lockInterruptibly()放到代码块中,然后调用interrupt()方法可以中断
(4)加锁的时候,是否可以公平
Sync;非公平锁
lock:两者都可以的。默认是非公平锁。在其构造方法的时候可以传入Boolean值。
true:公平锁
false:非公平锁
(5)锁绑定多个条件来condition
Sync:没有。要么随机唤醒一个线程;要么是唤醒所有等待的线程。
Lock:用来实现分组唤醒需要唤醒的线程,可以精确的唤醒,而不是像sync那样,不能精确唤醒线程。
8.第三种多线程类Callable
有返回值时,当主线程调用get()方法,主线程进入等待状态,直到有结果后主线程再继续执行
9.Thread.Statue
线程状态。 线程可以处于以下状态之一:
NEW
尚未启动的线程处于此状态。
RUNNABLE
在Java虚拟机中执行的线程处于此状态。
BLOCKED
被阻塞等待监视器锁定的线程处于此状态。
WAITING
无限期等待另一个线程执行特定操作的线程处于此状态。
TIMED_WAITING
正在等待另一个线程执行最多指定等待时间的操作的线程处于此状态。
TERMINATED
已退出的线程处于此状态。
线程在给定时间点只能处于一种状态。 这些状态是虚拟机状态,不反映任何操作系统线程状态。
10.线程池
Java中的四种线程池. ExecutorService,一定时间后会自动关闭
(1) 缓存线程池
/** 缓存线程池.
* (长度无限制)
* 执行流程:
* 1. 判断线程池是否存在空闲线程
* 2. 存在则使用
* 3. 不存在,则创建线程 并放入线程池, 然后使用
*/
//创建线程池
ExecutorService service = Executors.newCachedThreadPool();
//执行线程池中的一个线程,传的是一个线程任务,不是线程
service.execute(new Runnable() {
@Override
public void run() {
//执行的任务
}
});
(2)定长线程池
/** 定长线程池.
* (长度是指定的数值)
* 执行流程:
* 1. 判断线程池是否存在空闲线程
* 2. 存在则使用
* 3. 不存在空闲线程,且线程池未满的情况下,则创建线程 并放入线程池, 然后使用
* 4. 不存在空闲线程,且线程池已满的情况下,则等待线程池存在空闲线程
*/
ic static void main(String[] args) {
//创建一个固定线程池,长度为2
ExecutorService service = Executors.newFixedThreadPool(2);
service.execute(new Runnable() {
//执行的任务
}
});
(3)单线程线程池
效果与定长线程池 创建时传入数值1 效果一致.
/*** 单线程线程池.
* 执行流程:
* 1. 判断线程池的那个线程是否空闲
* 2. 空闲则使用
* 3. 不空闲,则等待 池中的单个线程空闲后 使用
*/
(4)周期性任务定长线程池
/**
* 1. 定时执行一次
* 参数1.定时执行的任务
* 参数2.时长数字
* 参数3.时长数字的时间单位TimeUnit常量指定
*/
ScheduledExecutorService service = Executors.newScheduledThreadPool(3);
service.schedule(new Runnable() {
@Override
public void run() {
//执行的任务
}
},1, TimeUnit.SECONDS);
/**
* 2. 周期性执行任务
* 参数1.定时执行的任务(第一次执行在什么时间以后)
* 参数2.延迟时长数字(每个多久执行一次)
* 参数3.周期时长数字
* 参数4.时长数字的时间单位,TimeUnit常量指定
*/
ScheduledExecutorService service = Executors.newScheduledThreadPool(3);
service.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
//执行的任务
}
},5,1,TimeUnit.SECONDS);
每个线程都有自己的栈空间,但共用一个堆内存##
程基础整理