深度分析!面试99%被问到的多线程和并发篇,看完你就懂了

lwh1019 lwh1019     2022-11-29     517

关键词:

1、Java中实现多线程有几种方法

继承Thread类;
实现Runnable接口;
实现Callable接口通过FutureTask包装器来创建Thread线程;
使用ExecutorService、Callable、Future实现有返回结果的多线程(也就是使用了ExecutorService来管理前面的三种方式)。

2、如何停止一个正在运行的线程

1、使用退出标志,使线程正常退出,也就是当run方法完成后线程终止。
2、使用stop方法强行终止,但是不推荐这个方法,因为stop和suspend及resume一样都是过期作废的方法。
3、使用interrupt方法中断线程。

// An highlighted block
class MyThread extends Thread 
    volatile boolean stop = false;
    public void run() 
        while (!stop) 
            System.out.println(getName() + " is running");
             try 
                sleep(1000);
             catch (InterruptedException e)
             
            System.out.println("week up from blcok...");    
            stop = true; // 在异常处理代码中修改共享变量的状态 
        
    System.out.println(getName() + " is exiting。。。"); 
    

    
class InterruptThreadDemo3 
public static void main(String[] args) throws InterruptedException  
     MyThread m1 = new MyThread();
    System.out.println("Starting thread...");
    m1.start();
    Thread.sleep(3000);
    System.out.println("Interrupt thread。。。: " +m1.getName());
    m1.stop = true; // 设置共享变量为true 
    m1.interrupt(); // 阻塞时退出阻塞状态
    Thread.sleep(3000); // 主线程休眠3秒以便观察线程m1的中断情况
    System.out.println("Stopping application。。。"); 
;

  

4、sleep()和wait() 有什么区别?

对于sleep()方法,我们首先要知道该方法是属于Thread类中的。而wait()方法,则是属于Object类中的。
sleep()方法导致了程序暂停执行指定的时间,让出cpu该其他线程,但是他的监控状态依然保持者,当指定的时间到了又会自动恢复运行状态。在调用sleep()方法的过程中,线程不会释放对象锁。
当调用wait()方法的时候,线程会放弃对象锁,进入等待此对象的等待锁定池,只有针对此对象调用notify()方法后本线程才进入对象锁定池准备,获取对象锁进入运行状态。

5、volatile 是什么?可以保证有序性吗?

一旦一个共享变量(类的成员变量、类的静态成员变量)被volatile修饰之后,那么就具备了两层语义:
1) 保证了不同线程对这个变量进行操作时的可见性,即一个线程修改了某个变量的值,这新值对其他线程来说是立即可见的,volatile关键字会强制将修改的值立即写入主存。
2) 禁止进行指令重排序。

volatile 不是原子性操作什么叫保证部分有序性?
当程序执行到volatile变量的读操作或者写操作时,在其前面的操作的更改肯定全部已经进行,且结果已经对后面的操作可见;在其后面的操作肯定还没有进行;

// An highlighted block
x = 2;//语句1
y = 0;//语句2
flag = true; //语句3
x = 4; //语句4 
y = -1; //语句5

  

由于?ag变量为volatile变量,那么在进行指令重排序的过程的时候,不会将语句3放到语句1、语句2前面,也不会讲语句3放到语句4、语句5后面。但是要注意语句1和语句2的顺序、语句4和语句5的顺序是不作任何保证的。使用 Volatile 一般用于状态标记和单例模式的双检锁

6、Thread 类中的start() 和 run() 方法有什么区别?

start()方法被用来启动新创建的线程,而且start()内部调用了run()方法,这和直接调用run()方法的效果 不一样。当你调用run()方法的时候,只会是在原来的线程中调用,没有新的线程启动,start()方法才会启动新线程。

7、为什么wait, notify 和 notifyAll这些方法不在thread类里面?

明显的原因是JAVA提供的锁是对象级的而不是线程级的,每个对象都有锁,通过线程获得。如果线程需要等待某些锁那么调用对象中的wait()方法就有意义了。如果wait()方法定义在Thread类中,线程正在 等待的是哪个锁就不明显了。简单的说,由于wait,notify和notifyAll都是锁级别的操作,所以把他们定义在Object类中因为锁属于对象。

8、为什么wait和notify方法要在同步块中调用?

1。 只有在调用线程拥有某个对象的独占锁时,才能够调用该对象wait(),notify()和notifyAll()方法。
2。 如果你不这么做,你的代码会抛出IllegalMonitorStateException异常。
3。 还有一个原因是为了避免wait和notify之间产生竞态条件。

wait()方法强制当前线程释放对象锁。这意味着在调用某对象的wait()方法之前,当前线程必须已经获得该对象的锁。因此,线程必须在某个对象的同步方法或同步代码块中才能调用该对象的wait()方法。
在调用对象的notify()和notifyAll()方法之前,调用线程必须已经得到该对象的锁。因此,必须在某个对象的同步方法或同步代码块中才能调用该对象的notify()或notifyAll()方法。

调用wait()方法的原因通常是,调用线程希望某个特殊的状态(或变量)被设置之后再继续执行。
调用notify()或notifyAll()方法的原因通常是,调用线程希望告诉其他等待中的线程:“特殊状态已经被设置”。 这个状态作为线程间通信的通道,它必须是一个可变的共享状态(或变量)。

9、Java中interrupted 和 isInterruptedd方法的区别?

interrupted() 和 isInterrupted()的主要区别是前者会将中断状态清除而后者不会。Java多线程的中断机制是用内部标识来实现的,调用Thread.interrupt()来中断一个线程就会设置中断标识为true。当中断线程调用静态方法Thread.interrupted()来检查中断状态时,中断状态会被清零。而非静态方法isInterrupted()用来查询其它线程的中断状态且不会改变中断状态标识。简单的说就是任何抛出InterruptedException异常的方法都会将中断状态清零。无论如何,一个线程的中断状态有可能被其它线程调用中断来改变。

10、Java中synchronized 和 ReentrantLock 有什么不同?

相似点:

这两种同步方式有很多相似之处,它们都是加锁方式同步,而且都是阻塞式的同步,也就是说当如果一个线程获得了对象锁,进入了同步块,其他访问该同步块的线程都必须阻塞在同步块外面等待,而进行线程阻塞和唤醒的代价是比较高的。

区别:

这两种方式最大区别就是对于Synchronized来说,它是java语言的关键字,是原生语法层面的互斥,需要jvm实现。而ReentrantLock它是JDK 1.5之后提供的API层面的互斥锁,需要lock()和unlock()方法配合try/?nally语句块来完成。

Synchronized进过编译,会在同步块的前后分别形成monitorenter和monitorexit这个两个字节码指令。在执行monitorenter指令时,首先要尝试获取对象锁。如果这个对象没被锁定,或者当前线程已经拥有了那个对象锁,把锁的计算器加1,相应的,在执行monitorexit指令时会将锁计算器就减1,当计算器为0时,锁就被释放了。如果获取对象锁失败,那当前线程就要阻塞,直到对象锁被另一个线程释放为止。

由于ReentrantLock是java.util.concurrent包下提供的一套互斥锁,相比Synchronized,ReentrantLock类提供了一些高级功能,主要有以下3项:
1。等待可中断,持有锁的线程长期不释放的时候,正在等待的线程可以选择放弃等待,这相当于Synchronized来说可以避免出现死锁的情况。
2。公平锁,多个线程等待同一个锁时,必须按照申请锁的时间顺序获得锁,Synchronized锁非公平锁,ReentrantLock默认的构造函数是创建的非公平锁,可以通过参数true设为公平锁,但公平锁表现的性能不是很好。
3。锁绑定多个条件,一个ReentrantLock对象可以同时绑定对个对象。

11、有三个线程T1,T2,T3,如何保证顺序执行?

在多线程中有多种方法让线程按特定顺序执行,你可以用线程类的join()方法在一个线程中启动另一个线程,另外一个线程完成该线程继续执行。为了确保三个线程的顺序你应该先启动最后一个(T3调用T2,T2调用T1),这样T1就会先完成而T3最后完成。实际上先启动三个线程中哪一个都行,因为在每个线程的run方法中用join方法限定了三个线程的执行顺序。

// An highlighted block
public class JoinTest2 
// 1。现在有T1、T2、T3三个线程,你怎样保证T2在T1执行完后执行,T3在T2执行完后执行
    public static void main(String[] args) 
        final Thread t1 = new Thread(new Runnable() 
            @Override
            public void run()   
            System.out.println("t1");
             
        );
        
        final Thread t2 = new Thread(new Runnable()  
            @Override
            public void run()  
                 try 
                    // 引用t1线程,等待t1线程执行完
                    t1.join();
                  catch (InterruptedException e)
                    e.printStackTrace(); 
                 
            System.out.println("t2");
            
        );
        
    Thread t3 = new Thread(new Runnable() 
        @Override
        public void run()   
            try 
            // 引用t2线程,等待t2线程执行完
                t2.join();
             catch (InterruptedException e) 
                e.printStackTrace(); 
            
         System.out.println("t3"); 
         
    );
t3.start();//这里三个线程的启动顺序可以任意,大家可以试下!
t2.start();
t1.start();
     

  

12、SynchronizedMap和ConcurrentHashMap有什么区别?

SynchronizedMap()和Hashtable一样,实现上在调用map所有方法时,都对整个map进行同步。而ConcurrentHashMap的实现却更加精细,它对map中的所有桶加了锁。所以,只要有一个线程访问map,其他线程就无法进入map,而如果一个线程在访问ConcurrentHashMap某个桶时,其他线程, 仍然可以对map执行某些操作。
ConcurrentHashMap在性能及安全性方面,明显Collections.synchronizedMap()更加有优势。同时,同步操作精确控制到桶,这样即使在遍历map时,如果其他线程试图对map进行数据修改,也不会抛出ConcurrentModi?cationException。

13、什么是线程安全

线程安全就是说多线程访问同一代码,不会产生不确定的结果。

在多线程环境中,当各线程不共享数据的时候,即都是私有(private)成员,那么一定是线程安全的。 但这种情况并不多见,在多数情况下需要共享数据,这时就需要进行适当的同步控制了。

线程安全一般都涉及到synchronized, 就是一段代码同时只能有一个线程来操作 不然中间过程可能会产生不可预制的结果。

如果你的代码所在的进程中有多个线程在同时运行,而这些线程可能会同时运行这段代码。如果每次运行结果和单线程运行的结果是一样的,而且其他的变量的值也和预期的是一样的,就是线程安全的。

14、Thread类中的yield方法有什么作用?

Yield方法可以暂停当前正在执行的线程对象,让其它有相同优先级的线程执行。它是一个静态方法而且 只保证当前线程放弃CPU占用而不能保证使其它线程一定能占用CPU,执行yield()的线程有可能在进入 到暂停状态后马上又被执行。

15、Java线程池中submit() 和 execute()方法有什么区别?

两个方法都可以向线程池提交任务,execute()方法的返回类型是void,它定义在Executor接口中, 而submit()方法可以返回持有计算结果的Future对象,它定义在ExecutorService接口中,它扩展了Executor接口,其它线程池类像ThreadPoolExecutor和ScheduledThreadPoolExecutor都有这些方法。

16、说一说自己对于 synchronized 关键字的了解

synchronized关键字解决的是多个线程之间访问资源的同步性,synchronized关键字可以保证被它修饰的方法或者代码块在任意时刻只能有一个线程执行。
另外,在 Java 早期版本中,synchronized属于重量级锁,效率低下,因为监视器锁(monitor)是依赖于底层的操作系统的 Mutex Lock 来实现的,Java 的线程是映射到操作系统的原生线程之上的。如果要挂起或者唤醒一个线程,都需要操作系统帮忙完成,而操作系统实现线程之间的切换时需要从用户态 转换到内核态,这个状态之间的转换需要相对比较长的时间,时间成本相对较高,这也是为什么早期的synchronized 效率低的原因。庆幸的是在 Java 6 之后 Java 官方对从 JVM 层面对synchronized 较大优化,所以现在的 synchronized 锁效率也优化得很不错了。JDK1.6对锁的实现引入了大量的优化,如自旋锁、适应性自旋锁、锁消除、锁粗化、偏向锁、轻量级锁等技术来减少锁操作的开销。

17、说说自己是怎么使用 synchronized 关键字,在项目中用到了 吗synchronized关键字最主要的三种使用方式:

修饰实例方法:

作用于当前对象实例加锁,进入同步代码前要获得当前对象实例的锁

修饰静态方法:

也就是给当前类加锁,会作用于类的所有对象实例,因为静态成员不属于任何一个实例对象,是类成员( static 表明这是该类的一个静态资源,不管new了多少个对象,只有一份)。所以如果一个线程A调用一个实例对象的非静态 synchronized 方法,而线程B需要调用这个实例对象所属类的静态 synchronized 方法,是允许的,不会发生互斥现象,因为访问静态 synchronized 方法占用的锁是当前类的锁,而访问非静态 synchronized 方法占用的锁是当前实例对象锁。

修饰代码块:

指定加锁对象,对给定对象加锁,进入同步代码库前要获得给定对象的锁。

总结:

synchronized 关键字加到 static 静态方法和 synchronized(class)代码块上都是是给 Class 类上锁。synchronized 关键字加到实例方法上是给对象实例上锁。尽量不要使用 synchronized(String a) 因为JVM中,字符串常量池具有缓存功能!

18、什么是线程安全?Vector是一个线程安全类吗?

如果你的代码所在的进程中有多个线程在同时运行,而这些线程可能会同时运行这段代码。如果每次运行结果和单线程运行的结果是一样的,而且其他的变量 的值也和预期的是一样的,就是线程安全的。一个线程安全的计数器类的同一个实例对象在被多个线程使用的情况下也不会出现计算失误。很显然你可以将集合类分成两组,线程安全和非线程安全的。Vector 是用同步方法来实现线程安全的, 而和它相似 的ArrayList不是线程安全的。

19、 volatile关键字的作用?

一旦一个共享变量(类的成员变量、类的静态成员变量)被volatile修饰之后,那么就具备了两层语义:

1.保证了不同线程对这个变量进行操作时的可见性,即一个线程修改了某个变量的值,这新值对其他线程来说是立即可见的。
2.禁止进行指令重排序。
3.volatile本质是在告诉jvm当前变量在寄存器(工作内存)中的值是不确定的,需要从主存中读取;synchronized则是锁定当前变量,只有当前线程可以访问该变量,其他线程被阻塞住。
4.volatile仅能使用在变量级别;synchronized则可以使用在变量、方法、和类级别的。
5.volatile仅能实现变量的修改可见性,并不能保证原子性;synchronized则可以保证变量的修改可见性和原子性。
6.volatile不会造成线程的阻塞;synchronized可能会造成线程的阻塞。

volatile标记的变量不会被编译器优化;synchronized标记的变量可以被编译器优化。

20、常用的线程池有哪些?

newSingleThreadExecutor:

创建一个单线程的线程池,此线程池保证所有任务的执行顺序按照任 务 的提交顺序执行 。

newFixedThreadPool:

创建固定大小的线程池,每次提交一个任务就创建一个线程,直到线程达到线程池的最大大小 。

newCachedThreadPool:

创建一个可缓存的线程池,此线程池不会对线程池大小做限制,线程池大小完全依赖于操作系统 ( 或 者 说 JVM) 能 够 创 建 的 最 大 线程大小 。

newScheduledThreadPool:

创建一个大小无限的线程池,此线程池支持定时以及周期性执行任务的需求 。

newSingleThreadExecutor:

创建一个单线程的线程池。此线程池支持定时以及周期性执行任务的需求。

21、简述一下你对线程池的理解

(如果问到了这样的问题,可以展开的说一下线程池如何用、线程池的好处、线程池的启动策略)合理利用线程池能够带来三个好处。
第一:降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。
第二:提高响应速度。当任务到达时,任务可以不需要等到线程创建就能立即执行。
第三:提高线程的可管理性。线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配,调优和监控。

22、Java程序是如何执行的

我们日常的工作中都使用开发工具(IntelliJ IDEA 或 Eclipse 等)可以很方便的调试程序,或者是通过打包工具把项目打包成 jar 包或者 war 包,放入 Tomcat 等 Web 容器中就可以正常运行了,但你有没有想过 Java 程序内部是如何执行的?其实不论是在开发工具中运行还是在 Tomcat 中运行,Java 程序的 执行流程基本都是相同的,它的执行流程如下:

先把 Java 代码编译成字节码,也就是把.java 类型的文件编译成 .class 类型的文件。这个过程的大致执行流程:Java 源代码 -> 词法分析器 -> 语法分析器 -> 语义分析器 -> 字符码生成器 -> 最终生成字节码,其中任何一个节点执行失败就会造成编译失败;

把 class 文件放置到 Java 虚拟机,这个虚拟机通常指的是 Oracle 官方自带的 Hotspot JVM;
Java 虚拟机使用类加载器(Class Loader)装载 class 文件;
类加载完成之后,会进行字节码效验,字节码效验通过之后 JVM 解释器会把字节码翻译成机器码交
由操作系统执行。但不是所有代码都是解释执行的,JVM 对此做了优化,比如,以 Hotspot 虚拟机
来说,它本身提供了 JIT(Just In Time)也就是我们通常所说的动态编译器,它能够在运行时将热
点代码编译为机器码,这个时候字节码就变成了编译执行。Java 程序执行流程图如下:

技术图片

新鲜出炉!java线程池精华篇深度讲解,看完你还怕面试被问到吗?

...候看到一个粉丝朋友的私信跟我说自己现在正在复习准备面试,自己在复习到线程池这一块的时候有点卡壳,总感觉自己差了点什么。想要我帮他指导一下。这不趁着假期我也有时间我把自己这么多年的理解和从网上找的资料放... 查看详情

深度分析:面试90%被问到的多线程创建线程线程状态线程安全,一次性帮你全搞定!(代码片段)

一、多线程1.概述多线程(multithreading),是指从软件或者硬件上实现多个线程并发执行的技术。就是在单个程序中同时运行多个线程来完成不同的工作。2.并发与并行并发:指两个或多个事件在同一个时间段内发生。并行:指两... 查看详情

深度分析:面试90%被问到的sessioncookietoken,看完这篇你就掌握了!(代码片段)

Cookie和SessionHTTP协议是一种无状态协议,即每次服务端接收到客户端的请求时,都是一个全新的请求,服务器并不知道客户端的历史请求记录;Session和Cookie的主要目的就是为了弥补HTTP的无状态特性。Session是什么客户端请求服务... 查看详情

深度分析:面试阿里,字节跳动,美团90%被问到的list集合,看完还不懂算我输

1List集合1.1List概述在Collection中,List集合是有序的,可对其中每个元素的插入位置进行精确地控制,可以通过索引来访问元素,遍历元素。在List集合中,我们常用到ArrayList和LinkedList这两个类。关于JavaList的一些重要观点是;JavaList... 查看详情

深度分析:面试阿里,字节跳动,美团几乎都会被问到的阻塞队列

基本概念阻塞队列(BlockingQueue)是一个支持两个附加操作的队列。这两个附加的操作支持阻塞的插入和移除方法。1)支持阻塞的插入方法:意思是当队列满时,队列会阻塞插入元素的线程,直到队列不满。2)支持阻塞的移除方... 查看详情

深度分析:面试阿里,字节跳动,美团几乎都会被问到的阻塞队列(代码片段)

基本概念阻塞队列(BlockingQueue)是一个支持两个附加操作的队列。这两个附加的操作支持阻塞的插入和移除方法。1)支持阻塞的插入方法:意思是当队列满时,队列会阻塞插入元素的线程,直到队列不满。2)支持阻塞的移除方... 查看详情

面试总被问到http缓存机制及原理?看完你就彻底明白了

前言Http缓存机制作为web性能优化的重要手段,对于从事Web开发的同学们来说,应该是知识体系库中的一个基础环节,同时对于有志成为前端架构师的同学来说是必备的知识技能。但是对于很多前端同学来说,仅仅只是知道浏览... 查看详情

深度分享:面试阿里,字节跳动,美团90%会被问到的hashmap知识(代码片段)

一,HashTable哈希表,它相比于hashMap结构简单点,它没有涉及红黑树,直接使用链表的方式解决哈希冲突。我们看它的字段,和hashMap差不多,使用table存放元素privatetransientEntry<?,?>[]table;privatetransientintcount;privateintthreshold;private... 查看详情

面试中常被问到的(17)多线程及多进程优缺点

多进程:优点:进程资源相互独立,互不干扰,子程序崩溃不影响主程序的稳定性。进程比较稳定健壮通过增加cpu核心数,可以很容易扩充性能尽量减少线程加锁解锁的影响,极大提高性能每一个进程可以... 查看详情

面试中常被问到的(19)常见几种锁

互斥锁:mutex,在任何时刻,只能有一个线程访问该对象,当获取锁操作失败,线程会进入睡眠(等待队列中被挂起)直至等待锁释放被唤醒读写锁:分为读锁和写锁,处于读操作时,可以... 查看详情

史上最全!2020面试阿里,字节跳动90%被问到的jvm面试题(附答案)

...是收到小伙伴的私信问我能不能帮忙整理出一份JVM相关的面试题出来,说自己在大厂去面试的时候这一块问的是特别多的,每次自己学的时候每次都学不到重点去。这不他来了,一份详细的JVM面试真题给大家整理在下方了!一、... 查看详情

关于物流项目面试可能会被问到的20题总结

文章目录1.简单介绍一下该项目5.数据来源及数据采集11、数据采集如何完成12、数据量大小3.技术架构(技术选项及框架版本)18、离线数仓数仓分层的作用是什么?我来介绍我们这个项目用到的模型:使用到了拉链表7.业务报表... 查看详情

软件测试面试一定会被问到的10个技术问题(附答案)

本文记得熟读并背诵,99%通过技术二本文记得熟读并背诵,99%通过技术二1、测试用例问题范例回答:一般都是让你对一个“书本”“水杯”“电梯”这些老例子做测试用例,这些答案百度一下全部都是,掌握好测试用例的方法... 查看详情

深度分析:mybatis的底层实现原理,看完你学会了吗?

前言最近在和粉丝聊天的时候被粉丝问到jdbc和mybatis底层实现这一块的问题,而且还不止一个小伙伴问到,于是我似乎认识到了问题的严重性,我花了两天时间整理了一下自己的认识和网上查阅的资料写了这篇文章,话不多说,... 查看详情

整理一下面试经常被问到的关于集合的问题

集合类的框架图1.集合分两大类,Map和Collection。而Collection又有子接口List(数据存储顺序和插入顺序是一样的)、Set(里面的元素具有唯一性) 2.Collection中分为:  2.1:List主要有:List继承了Collection,是有序的列表。实现类... 查看详情

面试阿里,腾讯90%会被问到的25个问题,附答案!(代码片段)

想要确保您的下一次Java面试成功吗?查看这篇文章,了解有关常见Java面试问题的更多信息,以及面试技巧!简介作为最广泛使用和部署的语言,Java是Web领域的三大核心技术之一。它由JamesGosling,PatrickNaughton和MikeSheridan于1991年... 查看详情

面试中常被问到的(18)父子进程,孤儿进程及僵尸进程

并发:单核cpu多任务,宏观同一段时间内表现出同时运行,微观上实际为时间片轮转,同一时刻只能有一个进程占据cpu资源运行,多个任务执行存在先后顺序并行:多核cpu多任务,严格物理意义上的同... 查看详情

深度分析:java并发编程之线程池技术,看完面试这个再也不慌了!(代码片段)

线程池的好处Java中的线程池是运用场景最多的并发框架,几乎所有需要异步或并发执行任务的程序都可以使用线程池。在开发过程中,合理地使用线程池,相对于单线程串行处理(SerialProcessing)和为每一个任务分配一个新线程... 查看详情