线程知识点全总结

线程生命周期

8669504-5e002f216156580b.png
线程状态图
比较项 归属 是否释放锁 使用范围 是否需要捕获异常 是否能自我唤醒 进入状态
wait Object类 释放 同步控制方法或者同步控制块中 Blocked
sleep Thread类 没有释放 任何地方 Blocked
yield Thread类 没有释放 任何地方 有可能 runnable
join Thread类 没有释放 任何地方 runnable
为什么要用join:

一个线程要用到另一个线程的处理结果,就要用到t.join(),等待t线程执行完成后再执行
Thread.yield()方法作用是:暂停当前正在执行的线程对象,并执行其他线程,但无法确保让步。

Sleep也yield区别:

Sleep在停滞期间肯定不会被执行,yield可能立即又会被调度执行。

多线程:

并行:多个cpu实例或者多台机器同时执行一段处理逻辑,是真正的同时。
并发:一个cpu,通过cpu调度算法,让用户看上去同时执行,实际上从cpu操作层面不是真正的同时。并发往往在场景中有公用的资源
线程安全:代码供多线程使用时,线程的调度顺序不影响任何结果。

Synchronized 和Lock:

他们功能类似,synchronized试用于并发量小的,lock更适用于并发量大的
lock更灵活,可以自由定义多把锁的枷锁解锁顺序
Lock还有中断锁和定时锁。
当线程运行到synchronized同步方法中,就会拥有obj对象的对象锁
Wait会释放对象锁,sleep不会释放对象锁
Synchronized用对象锁:

8669504-7c002a20bf8b3150.png
8669504-1c911c28caf3a2c3.png
image.png
8669504-5a30d07b6ef5f867.png
image.png

一次打印:ABABABABABABABABABAB,2个任务公用一个对象锁,这个对象可以随意指定
用lock作为锁:
Lock lock = new ReentrantLock();对不同的runable传入同一个lock

8669504-bf49cd9c854c4735.png
A
8669504-ddfa5e05c57a32f5.png
B
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的一个普通方法调用,还是在主线程里执行。

如何控制某个方法允许并发访问线程的个数?

信号量(Semaphore),有时被称为信号灯,是在多线程环境下使用的一种设施, 它负责协调各个线程, 以保证它们能够正确、合理的使用公共资源。
以一个停车场为例。在这个停车场系统中,车位是公共资源,每辆车好比一个线程,看门人起的就是信号量的作用,需要用信号量来计数。

线程阻塞有哪些原因

A、线程执行了Thread.sleep(int millsecond);方法,当前线程放弃CPU,睡眠一段时间,然后再恢复执行
B、线程执行一段同步代码,但是尚且无法获得相关的同步锁,只能进入阻塞状态,等到获取了同步锁,才能回复执行。
C、线程执行了一个对象的wait()方法,直接进入阻塞状态,等待其他线程执行notify()或者notifyAll()方法。
D、线程执行某些IO操作,因为等待相关的资源而进入了阻塞状态。比如说监听system.in,但是尚且没有收到键盘的输入,则进入阻塞状态。

线程如何关闭?

Java提供了中断机制,Thread类下有三个重要方法。
public void interrupt()
public boolean isInterrupted()
public static boolean interrupted(); // 清除中断标志,并返回原状态

每个线程都有个boolean类型的中断状态。当使用Thread的interrupt()方法时,线程的中断状态会被设置为true。
在run方法中使用isInterrupted()方法判断线程是否被中断,如果是就调用Thread.currentThread().interrupt()结束线程。

private class MyThread extends Thread{

        @Override
        public void run() {
            while (!Thread.currentThread().isInterrupted()) {
                try {
                    System.out.println("test");
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    System.out.println("interrupt");
                    //抛出InterruptedException后中断标志被清除,标准做法是再次调用interrupt恢复中断
                    Thread.currentThread().interrupt();
                }
            }
            System.out.println("stop");
        }

        public void cancel(){
            interrupt();
        }
    }
ThreadLocal的作用:

每个线程都有一个ThreadLocal线程本地变量,各个线程本地变量互不干扰。TreadLocalMap类型的变量(该类是一个轻量级的Map),可以调用set(),get()方法存取值,可以贯穿整个线程声明周期。作用:提供一个线程内公共变量,减少同一个线程内多个函数或者组件之间一些公共变量的传递的复杂度。

public static class MyRunnable implements Runnable {
        private ThreadLocal threadLocal = new ThreadLocal();
        @Override
        public void run() {
            threadLocal.set((int) (Math.random() * 100D));
            try {
            Thread.sleep(2000);
            } catch (InterruptedException e) {
            }
            System.out.println(threadLocal.get());
        }
    }
  • 线程优先级:

0-10,priority,默认为5, setpriority()设置优先级。

  • 线程重复调用start会怎么样?

start方法重复调用的话,会出现java.lang.IllegalThreadStateException异常。

  • 实现Runable可以资源共享,继承Thread不能资源共享。
  • 在java中,每次程序运行至少启动2个线程。一个是main线程,一个是垃圾收集线程。

猜你喜欢

转载自blog.csdn.net/weixin_33816821/article/details/87642698