线程知识点全总结

基础概念的理解

进程是资源分配的最小单位,线程是操作系统能够进行运算调度的最小单位。一个进程中可以并发多个线程,每条线程并行执行不同的任务。

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); //该线程为守护线程

版权声明:
作者:Zad
链接:https://www.techfm.club/p/44883.html
来源:TechFM
文章版权归作者所有,未经允许请勿转载。

THE END
分享
二维码
< <上一篇
下一篇>>