线程知识点全总结
基础概念的理解
进程是资源分配的最小单位,线程是操作系统能够进行运算调度的最小单位。一个进程中可以并发多个线程,每条线程并行执行不同的任务。
CPU时间片轮转机制:
时间片轮转法(Round-Robin,RR)主要用于分时系统中的进程调度。为了实现轮转调度,系统把所有就绪进程按先入先出的原则排成一个队列。新来的进程加到就绪队列末尾。每当执行进程调度时,进程调度程序总是选出就绪队列的队首进程,让它在 CPU 上运行一个时间片的时间。时间片是一个小的时间单位,通常为 10~100ms 数量级。当进程用完分给它的时间片后,系统的计时器发出时钟中断,调度程序便停止该进程的运行,把它放入就绪队列的末尾;然后,把 CPU 分给就绪队列的队首进程,同样也让它运行一个时间片,如此往复。
时间片轮转机制代价:
进程进行时间片轮转机制,会有释放资源,上线文切换,会比较耗费CPU的时间轮转周期,所以需要尽量减少时间轮转。
多线程概念:
并行:多个cpu实例或者多台机器同时执行一段处理逻辑,是真正的同时。
并发:一个cpu,通过cpu调度算法,让用户看上去同时执行,实际上从cpu操作层面不是真正的同时,是交替执行的。并发往往在场景中有公用的资源。
线程安全:代码供多线程使用时,线程的调度顺序不影响任何结果。
线程生命周期:
NEW:创建状态,线程创建之后,但是还未启动。
RUNNABLE:运行状态,处于运行状态的线程,但有可能处于等待状态,
例如等待 CPU、IO 等。
WAITING:等待状态,一般是调用了 wait()、join()等方法。
TIMED_WAITING:超时等待状态,也就是带时间的等待状态。一般是调
用 了 wait(time) 、 join(time) 等方法。
BLOCKED:阻塞状态,等待锁的释放,例如调用了 synchronized 增加了
锁。
TERMINATED:终止状态,一般是线程完成任务后退出或者异常终止。
-
wait sleep yield join区别
比较项 | 归属 | 是否释放锁 | 使用范围 | 是否需要捕获异常 | 是否能自我唤醒 | 进入状态 |
---|---|---|---|---|---|---|
wait | Object类 | 释放 | 同步控制方法或者同步控制块中 | 否 | 否 | Blocked |
sleep | Thread类 | 没有释放 | 任何地方 | 是 | 是 | Blocked |
yield | Thread类 | 没有释放 | 任何地方 | 是 | 有可能 | runnable |
join | Thread类 | 没有释放 | 任何地方 | 是 | 能 | runnable |
为什么要用join:
一个线程要用到另一个线程的处理结果,就要用到t.join(),等待t线程执行完成后再执行,这样就能做到让2个线程顺序地执行。
Sleep也yield区别:
Thread.yield()方法作用是:暂停当前正在执行的线程对象,并执行其他线程,但无法确保让步。
Sleep在停滞期间肯定不会被执行,yield可能立即又会被调度执行。
Java 多线程之多个窗口售票问题
public class MyRunnable implements Runnable {
private int tickets = 100;
public void run() {
while (true) {
synchronized (this) {
if (tickets > 0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "售出了第" + (tickets--) + "张票");
}
}
}
}
}
卖票任务用3个线程同时处理:
public class RunnableDemo {
public static void main(String[] args) {
MyRunnable myRunnable = new MyRunnable();
Thread t1 = new Thread(myRunnable, "窗口一");
Thread t2 = new Thread(myRunnable, "窗口二");
Thread t3 = new Thread(myRunnable, "窗口三");
t1.start();
t2.start();
t3.start();
}
}
死锁:
死锁是指两个或两个以上的进程在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力作用,他们都无法推进下去。通俗一点就是两个进程都持有资源,但是又想抢对方的资源,互不相让了。
死锁4个必要条件:
(1) 互斥:一个资源每次只能被一个进程使用。
(2) 请求与保持:一个进程因请求资源而阻塞时,对已获得的资源保持不放。
(3) 不剥夺:进程已获得的资源,在末使用完之前,不能强行剥夺。
(4) 循环等待:若干进程之间形成一种头尾相接的循环等待资源关系。
如何避免死锁
三种用于避免死锁的技术:
加锁顺序(线程按照一定的顺序加锁)
加锁时限(线程尝试获取锁的时候加上一定的时限,超过时限则放弃对该锁的请求,并释放自己占有的锁)
死锁检测
同步方法和同步代码块的区别是什么?
同步方法默认用this或者当前类class对象作为锁;
同步代码块可以选择以什么来加锁;可以选择只同步会发生同步问题的部分代码而不是整个方法;
sleep和wait有什么区别
1,Sleep是属于Thread类的静态方法,wait属于Object的方法。
2,最主要是sleep方法没有释放锁,而是放弃CPU的调度。而wait方法释放了锁,使得其他线程可以进入同步控制块或者方法。
3,wait,notify和notifyAll只能在同步控制方法或者同步控制块里面使用,而sleep可以在任何地方使用
synchronized(x){
x.notify()
//或者wait()
}
4,sleep必须捕获异常,而wait,notify和notifyAll不需要捕获异常
5.使用wait,notify可以达到线程之间的通信目的
同步和互斥的区别:
所谓同步,是指散步在不同进程之间的若干程序片断,它们的运行必须严格按照规定的某种先后次序来运行,这种先后次序依赖于要完成的特定的任务。如果用对资源的访问来定义的话,同步是指在互斥的基础上(大多数情况),通过其它机制实现访问者对资源的有序访问。在大多数情况下,同步已经实现了互斥,特别是所有写入资源的情况必定是互斥的。少数情况是指可以允许多个访问者同时访问资源。
所谓互斥,是指散布在不同进程之间的若干程序片断,当某个进程运行其中一个程序片段时,其它进程就不能运行它们之中的任一程序片段,只能等到该进程运行完这个程序片段后才可以运行。如果用对资源的访问来定义的话,互斥某一资源同时只允许一个访问者对其进行访问,具有唯一性和排它性。但互斥无法限制访问者对资源的访问顺序,即访问是无序的。
有了进程为什么还要有线程?
进程只能在一个时间干一件事,如果想同时干两件事或多件事,进程就无能为力了。
进程在执行的过程中如果阻塞,例如等待输入,整个进程就会挂起,即使进程中有些工作不依赖于输入的数据,也将无法执行。
体现在通信机制上面,正因为进程之间互不干扰,相互独立,进程的通信机制相对很复杂。而线程由于共享数据段所以通信机制很方便。
start()和run()方法的区别
Thread和Runnable是实现java多线程的2种方式,runable是接口,thread是类,建议使用runable实现 java多线程,不管如何,最终都需要通过thread.start()来使线程处于可运行状态。
1) start:
用start方法来启动线程,真正实现了多线程运行,这时无需等待run方法体代码执行完毕而直接继续执行下面的代码。通过调用Thread类的 start()方法来启动一个线程,这时此线程处于就绪(可运行)状态,并没有运行,一旦得到cpu时间片,就开始执行run()方法,这里方法 run()称为线程体,它包含了要执行的这个线程的内容,Run方法运行结束,此线程随即终止。
2) run:
run()方法只是类的一个普通方法而已,如果直接在主线程调用run方法,程序中依然只有主线程这一个线程,其程序执行路径还是只有一条,还是要顺序执行,还是要等待run方法体执行完毕后才可继续执行下面的代码,这样就没有达到写线程的目的。
总结:调用start方法方可启动线程,而run方法只是thread的一个普通方法调用,还是在主线程里执行,调用当前thread的start方法,run方法才能在当前线程中执行。
如何控制某个方法允许并发访问线程的个数?
信号量(Semaphore),有时被称为信号灯,是在多线程环境下使用的一种设施, 它负责协调各个线程, 以保证它们能够正确、合理的使用公共资源。
以一个停车场为例。在这个停车场系统中,车位是公共资源,每辆车好比一个线程,看门人起的就是信号量的作用,需要用信号量来计数。
线程阻塞有哪些原因
A、线程执行了Thread.sleep(int millsecond);方法,当前线程放弃CPU,睡眠一段时间,然后再恢复执行
B、线程执行一段同步代码,但是尚且无法获得相关的同步锁,只能进入阻塞状态,等到获取了同步锁,才能回复执行。
C、线程执行了一个对象的wait()方法,直接进入阻塞状态,等待其他线程执行notify()或者notifyAll()方法。
D、线程执行某些IO操作,因为等待相关的资源而进入了阻塞状态。比如说监听system.in,但是尚且没有收到键盘的输入,则进入阻塞状态。
线程如何关闭?
结束一个线程有一个最基本的方法:Thread.stop()方法,但是这个方法已经是:@Deprecated的了,也就是被建议不要使用的方法,强制停掉线程就会让其锁释放掉,可能出现线程安全问题。
Java对线程提供了中断机制,Thread类下有三个重要方法。
public void interrupt() {
if (this != Thread.currentThread())
checkAccess();
synchronized (blockerLock) {
Interruptible b = blocker;
if (b != null) {
interrupt0(); // Just to set the interrupt flag
b.interrupt(this);
return;
}
}
interrupt0();
}
@FastNative
public static native boolean interrupted();
@FastNative
public native boolean isInterrupted();
每个线程都有个boolean类型的中断状态,可调用 isInterrupted()来获取flag。当使用Thread的interrupt()方法时,线程的中断flag会被设置为true,该方法仅仅是打一个可中断的flag,并不回去真正的将线程中断下来。
在run方法中调用isInterrupted()方法判断线程是否被打了中断tag,可在判断方法里做我们需要做的事情。
@Override
public void run() {
while(isInterrupted()) {
//处理线程结束
}
}
isInterrupted()仅仅是获取状态,而interrupted()调用获取中断标志之后,还会将状态置为false。
-
线程优先级:
0-10,priority,默认为5, setpriority()设置优先级。
-
线程重复调用start会怎么样?
start方法重复调用的话,会出现java.lang.IllegalThreadStateException异常。
public synchronized void start() {
if(threadStatus != 0)
throw new IllegalThreadStateException();
...
}
-
实现Runable可以资源共享,继承Thread不能资源共享。
-
在java中,每次程序运行至少启动2个线程。一个是main线程,一个是垃圾收集线程。
-
一个应用程序有最大线程数限制吗?
有,比如Linux下上限1000个,Windows下上限。因为每分配一个线程,就会创建一个栈空间,栈的空间大小是有限的,创建线程也是会消耗资源的。
-
有几种创建线程的方式?
有2种,Thread类源码的解释。
There are two ways to create a new thread of exection.
一种是继承Thread类,一种是实现接口Runnable。
-
Java守护线程是什么?
Java中有两种线程:一种是用户线程,另一种是守护线程。守护线程是一种特殊的线程,当进程中主线程不存在了,则守护线程自动销毁。典型的守护线程是垃圾回收线程,当进程中没有非守护线程了,则垃圾回收线程也就没有存在的必要了,自动销毁。
设置为守护线程方法:
MyThread thread = new MyThread();
thread.setDaemon(true); //该线程为守护线程
共有 0 条评论