搞懂线程、多线程和线程池,你的能力再上一层

2020-01-16  本文已影响0人  Hifun丶

实战是检验真理的唯一标准,这里笔者整理了一些关于线程、多线程和线程池的面试题,一起学习和思考,这里的代码和例子是Java写的,主要是能够通用的介绍相关知识,编程语言只是一种工具,没有说哪种工具就绝对好,关键是使用工具的人,以及我们项目的具体需求,当我们掌握了真正的知识点之后,对于不同的语言,那只是语法的不一样。好啦不装X了,这个文章前端后端都可以看,祝看到的人都有个好的未来!

1、开启线程的三种方式?

  1. 继承Thread类,重写run()方法,在run()方法体中编写要完成的任务 new Thread().start();

  2. 实现Runnable接口,实现run()方法 new Thread(new MyRunnable()).start();

  3. 实现Callable接口MyCallable类,实现call()方法,使用FutureTask类来包装Callable对象,使用FutureTask对象作为Thread对象的target创建并启动线程;调用FutureTask对象的get()方法来获得子线程执行结束后的返回值。

FutureTask<Integer> ft = new FutureTask<Integer>(new MyCallable());
new Thread(ft).start();

2、run()和start()方法区别

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

4、在Java中wait和seelp方法的不同

5、谈谈wait/notify关键字的理解

6、什么导致线程阻塞?

一般线程阻塞
  1. 线程执行了Thread.sleep(int millsecond)方法,放弃CPU,睡眠一段时间,一段时间过后恢复执行;

  2. 线程执行一段同步代码,但无法获得相关的同步锁,只能进入阻塞状态,等到获取到同步锁,才能恢复执行;

  3. 线程执行了一个对象的wait()方法,直接进入阻塞态,等待其他线程执行notify()/notifyAll()操作;

  4. 线程执行某些IO操作,因为等待相关资源而进入了阻塞态,如System.in,但没有收到键盘的输入,则进入阻塞态。

  5. 线程礼让,Thread.yield()方法,暂停当前正在执行的线程对象,把执行机会让给相同或更高优先级的线程,但并不会使线程进入阻塞态,线程仍处于可执行态,随时可能再次分得CPU时间。

    线程自闭,join()方法,在当前线程调用另一个线程的join()方法,则当前线程进入阻塞态,直到另一个线程运行结束,当前线程再由阻塞转为就绪态。

  6. 线程执行suspend()使线程进入阻塞态,必须resume()方法被调用,才能使线程重新进入可执行状态

7、线程如何关闭?

  1. 使用标志位

  2. 使用stop()方法,但该方法就像关掉电脑电源一样,可能会发生预料不到的问题

  3. 使用中断interrupt()

public class Thread {
    // 中断当前线程
    public void interrupt();
    // 判断当前线程是否被中断
    public boolen isInterrupt();
    // 清除当前线程的中断状态,并返回之前的值
    public static boolen interrupted();   
}
 但调用interrupt()方法只是传递中断请求消息,并不代表要立马停止目标线程。

8、讲一下java中的同步的方法

  1. synchronized修饰同步代码块或方法

    由于java的每个对象都有一个内置锁,用此关键字修饰方法时,内置锁会保护整个方法。在调用该方法前,需获得内置锁,否则就处于阴塞状态。

  2. volatile修饰变量

    保证变量在线程间的可见性,每次线程要访问volatile修饰的变量时都从内存中读取,而不缓存中,这样每个线程访问到的变量都是一样的。且使用内存屏障。

  3. ReentrantLock重入锁,它常用的方法有ReentrantLock():创建一个ReentrantLock实例

    lock()获得锁 unlock()释放锁

  4. 使用局部变量ThreadLocal实现线程同步,每个线程都会保存一份该变量的副本,副本之间相互独立,这样每个线程都可以随意修改自己的副本,而不影响其他线程。

    常用方法ThreadLocal()创建一个线程本地变量;get()返回此线程局部的当前线程副本变量;initialValue()返回此线程局部变量的当前线程的初始值;set(T value)将此线程变量的当前线程副本中的值设置为value

  5. 使用原子变量,如AtomicInteger,常用方法AtomicInteger(int value)创建个有给定初始值的AtomicInteger整数;addAndGet(int data)以原子方式将给定值与当前值相加

  6. 使用阻塞队列实现线程同步LinkedBlockingQueue<E>

9、如何保证线程安全?

  1. 原子性:提供互斥访问,同一时刻只能有一个线和至数据进行操作。

    JDK中提供了很多atomic类,如AtomicInteger\AtomicBoolean\AtomicLong,它们是通过CAS完成原子性。JDK提供锁分为两种:synchronized依赖JVM实现锁,该关键字作用对象的作用范围内同一时刻只能有一个线程进行操作。另一种是LOCK,是JDK提供的

    代码层面的锁,依赖CPU指令,代表性是ReentrantLock。

  2. 可见性:一个线程对主内存的修改及时被其他线程看到。

    JVM提供了synchronized和volatile,volatile的可见性是通过内存屏障和禁止重排序实现的,volatile会在写操作时,在写操作后加一条store屏障指令,将本地内存中的共享变量值刷新到主内存;会在读操作时,在读操作前加一条load指令,从内存中读取共享变量。

  3. 有序性:指令没有被编译器重排序。

    可通过volatile、synchronized、Lock保证有序性。

10、两个进程同时要求写或者读,能不能实现?如何防止进程的同步?

11、线程间操作List

12、Java中对象的生命周期

  1. 创建阶段(Created):为对象分配存储空间,开始构造对象,从超类到子类对static成员初始化;超类成员变量按顺序初始化,递归调用超类的构造方法,子类成员变量按顺序初始化,子类构造方法调用。

  2. 应用阶段(In Use):对象至少被一个强引用持有着。

  3. 不可见阶段(Invisible):程序运行已超出对象作用域

  4. 不可达阶段(Unreachable):该对象不再被强引用所持有

  5. 收集阶段(Collected):假设该对象重写了finalize()方法且未执行过,会去执行该方法。

  6. 终结阶段(Finalized):对象运行完finalize()方法仍处于不可达状态,等待垃圾回收器对该对象空间进行回收。

  7. 对象空间重新分配阶段(De-allocated):垃圾回收器对该对象所占用的内存空间进行回收或再分配,该对象彻底消失。

13、static synchronized 方法的多线程访问和作用

14、同一个类里面两个synchronized方法,两个线程同时访问的问题

15、volatile的原理

16、synchronized原理

17、谈谈NIO的理解

synchronized 和volatile 关键字的区别
synchronized与Lock的区别
ReentrantLock 、synchronized和volatile比较

  1. volatile:解决变量在多个线程间的可见性,但不能保证原子性,只能用于修饰变量,不会发生阻塞。volatile能屏蔽编译指令重排,不会把其后面的指令排到内存屏障之前的位置,也不会把前面的指令排到内存屏障的后面。多用于并行计算的单例模式。volatile规定CPU每次都必须从内存读取数据,不能从CPU缓存中读取,保证了多线程在多CPU计算中永远拿到的都是最新的值。

  2. synchronized:互斥锁,操作互斥,并发线程过来,串行获得锁,串行执行代码。解决的是多个线程间访问共享资源的同步性,可保证原子性,也可间接保证可见性,因为它会将私有内存和公有内存中的数据做同步。可用来修饰方法、代码块。会出现阻塞。synchronized发生异常时,会自动释放线程占有的锁,因此不会导致死锁现象发生。非公平锁,每次都是相互争抢资源。

  3. lock是一个接口,而synchronized是java中的关键字,synchronized是内置语言的实现。lock可以让等待锁的线程响应中断。在发生异常时,如果没有主动通过unLock()去释放锁,则可能造成死锁现象,因此使用Lock时需要在finally块中释放锁。

  4. ReentrantLock可重入锁,锁的分配机制是基于线程的分配,而不是基于方法调用的分配。ReentrantLock有tryLock方法,如果锁被其他线程持有,返回false,可避免形成死锁。对代码加锁的颗粒会更小,更节省资源,提高代码性能。ReentrantLock可实现公平锁和非公平锁,公平锁就是先来的先获取资源。ReentrantReadWriteLock用于读多写少的场合,且读不需要互斥场景。

  5. 并发编程有关知识点(这个是一般开发时用的少的,所以建议多去看看)
    ReentrantLock的内部实现
    lock原理
    死锁的四个必要条件?
    怎么避免死锁?
    对象锁和类锁是否会互相影响?
    什么是线程池,如何使用?
    Java的并发、多线程、线程模型
    谈谈对多线程的理解
    多线程有什么要注意的问题?
    谈谈你对并发编程的理解并举例说明
    谈谈你对多线程同步机制的理解?
    如何保证多线程读写文件的安全?
    多线程断点续传原理
    断点续传的实现

平时在开发的时候可能很多底层的东西不需要我们关心,或者根本就用不到,但是我们想提升自己的话,一定不能停留在表面,,我们也应该去了解一下关于线程相关的源码级别的东西。最近在读很多平时提及频率较高的知识点的相关源码。想着,其实肯定不会白读,多少是会有些帮助和好处的,最明显的好处就是拿到offer升职加薪啦。一起努力!

------来自一个程序员小学生

上一篇 下一篇

猜你喜欢

热点阅读