java多线程笔试题目,java多线程并发面试题
如何解决写爬虫IP受阻的问题?立即使用。
并行和并发有什么区别?(推荐学习:java常见问题)
平行是指两个或两个以上的事件同时发生;并发意味着两个或多个事件在同一时间间隔发生。
并发是不同实体上的多个事件,并发是同一实体上的多个事件。
在一个处理器上“同时”处理多个任务,以及在多个处理器上同时处理多个任务。比如hadoop分布式集群。
因此,并发编程的目标是充分利用处理器的每个内核,以达到最高的处理性能。
线程和进程的区别?
简而言之,进程是程序运行和资源分配的基本单位。一个程序至少有一个进程,一个进程至少有一个线程。在执行过程中,进程拥有独立的内存单元,多个线程共享内存资源,减少了切换次数,从而达到更高的效率。
线程是一个进程的实体,是cpu调度和分派的基本单位,是比程序更小的可以独立运行的基本单位。同一进程中的多个线程可以并发执行。
守护线程是什么?
守护线程是一个服务线程,确切地说是为其他线程服务的。
创建线程有哪几种方式?
继承线程类,创建一个线程类。
定义Thread类的子类并覆盖这个类的run方法,这个run方法的方法体表示线程要完成的任务。因此,run()方法被称为执行程序。
当您创建thread子类的实例时,您创建了一个Thread对象。
调用thread对象的start()方法启动线程。
通过Runnable接口创建线程类。
定义runnable接口的实现类,并覆盖该接口的run()方法,其方法体也是该线程的线程执行器。
创建一个Runnable实现类的实例,用这个实例作为Thread的目标创建Thread对象,这才是真正的Thread对象。
调用thread对象的start()方法启动线程。
通过Callable和Future创建线程
创建一个可调用接口的实现类,实现call()方法,该方法将是带有返回值的线程执行器。
创建可调用实现类的实例,用FutureTask类包装可调用对象,封装可调用对象的call()方法的返回值。
使用FutureTask对象作为Thread对象的目标创建并启动一个新线程。
调用FutureTask对象的get()方法获取子线程执行后的返回值。
说一下 runnable 和 callable 有什么区别?
有点深。它还显示了Java程序员可以学习的知识的广度。
Runnable接口中run()方法的返回值是void,它做的只是执行run()方法中的代码;
Callable接口中的call()方法有一个返回值,它是一个泛型类型。当它与Future和FutureTask配合使用时,可以用来获得异步执行的结果。
线程有哪些状态?
线程通常都有五种状态,创建、就绪、运行、阻塞和死亡。
创建状态。生成线程对象时,不调用对象的start方法,这意味着线程处于创建状态。
就绪状态。当调用thread对象的start方法时,线程进入就绪状态,但此时线程调度器还没有将线程设置为当前线程,处于就绪状态。在线运行后,程序从等待或睡眠中回来时就准备好了。
运行状态。线程调度器将就绪状态的线程设置为当前线程,然后线程进入运行状态,开始运行run函数中的代码。
阻塞状态。当一个线程正在运行时,它被挂起,通常是为了等待一段时间(例如,一个资源准备好了)才继续运行。休眠、挂起、等待等方法都会导致线程阻塞。
死亡的状态。如果一个线程的run方法结束或者stop方法被调用,那么这个线程就会死亡。对于一个已经死亡的线程,start方法不能再用来让它准备好。
sleep() 和 wait() 有什么区别?
Sleep():方法是Thread类的静态方法,使调用线程进入睡眠状态,将执行机会让给其他线程。睡眠时间结束后,线程进入就绪状态,与其他线程争夺cpu执行时间。
因为sleep()是一个静态方法,它不能改变对象的机器锁。在synchronized块中调用sleep()方法时,虽然线程进入睡眠状态,但对象的机器锁并没有被释放,其他线程仍然无法访问该对象。
Wait (): wait()是Object类的一个方法。当一个线程执行wait方法时,它进入一个与该对象相关的等待池,同时释放该对象的机器锁,以便其他线程可以访问它。可以通过notify和notifyAll方法唤醒等待的线程。
notify()和 notifyAll()有什么区别?
如果线程调用了对象的wait()方法,线程就会在对象的等待池中,等待池中的线程不会竞争对象的锁。
当一个线程调用一个对象的notifyAll()方法(唤醒所有等待线程)或notify()方法(随机只唤醒一个等待线程)时,被唤醒的线程会进入该对象的锁池,锁池中的线程会争夺该对象的锁。
也就是说,一旦调用notify,只有一个线程会从等待池进入锁池,notifyAll会将对象的等待池中的所有线程都移到锁池中,等待锁竞争。
高优先级线程竞争对象锁的可能性很高。如果一个线程不竞争对象锁,它将留在锁池中。只有当线程再次调用wait()方法时,它才会返回等待池。
竞争对象锁的线程将继续执行,直到同步代码块完成,并且它将释放对象锁。此时,锁池中的线程将继续竞争对象锁。
线程的 run()和 start()有什么区别?
每个线程都是通过对应于特定线程对象的方法run()来完成自己的操作,方法run()称为线程体。通过调用Thread类的Start()方法启动线程。
Start()方法来启动一个线程,真正实现了多线程运行。此时可以直接继续执行下面的代码,而不用等待run方法的体代码执行完;此时,该线程处于就绪状态,没有运行。
然后,线程类调用Run()方法来完成其运行状态。在这里,run()方法被称为线程体,它包含要执行的线程的内容。当run方法完成时,线程被终止。然后CPU调度其他线程。
run()方法在这个线程里,只是线程里的一个函数,不是多线程的。如果直接调用run(),其实相当于调用一个普通的函数。要直接使用的run()方法必须等待run()方法完成,然后才能执行下面的代码。所以只有一条执行路径,根本没有线程特性。因此,多线程时应该使用start()方法,而不是run()方法。
创建线程池有哪几种方式?
.newFixedThreadPool(int nThreads)
创建一个固定长度的线程池。每次提交任务时,创建一个线程,直到达到线程池的最大数量。此时,螺纹尺寸将不再变化。当线程因意外错误而结束时,线程池将被一个新线程补充。
.newCachedThreadPool()
创建一个可缓存的线程池。如果线程池的大小超过了处理需求,它会自动回收空闲线程。当需求增加时,可以自动添加新线程。线程池的大小没有限制。
.newSingleThreadExecutor()
这是一个单线程执行器,它创建一个单独的工作线程来执行任务。如果这个线程异常终止,将创建一个新的线程来替换它;其特点是能够保证任务按照队列中的顺序串行执行。
.newScheduledThreadPool(int corePoolSize)
创建一个固定长度的线程池,任务以延迟或定时的方式执行,类似于Timer。
线程池都有哪些状态?
线程池有5种状态:运行、关闭、停止、整理和终止。
线程池各个状态切换框架图:
线程池中 submit()和 execute()方法有什么区别?
接收的参数不同。
Submit有返回值,但是execute没有。
提交有助于异常处理。
在 java 程序中怎么保证多线程的运行安全?
线程安全在三个方面体现:
原子性:提供互斥访问,同一时间只能有一个线程对数据进行操作,(原子的,同步的);
可见性:一个线程对主存的修改可以被其他线程及时看到,(synchronized,volatile);
有序性:一个线程观察其他线程中指令执行的顺序,观察结果一般会因指令重排而无序(发生在之前原则)。
多线程锁的升级原理是什么?
在Java中,有四种锁状态,分别是无状态锁、有偏锁、轻量级锁和重量级锁。这些状态会随着比赛逐渐升级。锁可以升级,但不能降级。
锁升级的图示过程:
什么是死锁?
死锁是指两个或两个以上的进程在执行过程中争夺资源或互相通信而导致的阻塞现象。如果没有外力,他们就无法前进。此时,系统处于死锁状态或系统出现死锁。这些总是互相等待的进程称为死锁进程。
是操作系统层面的错误,是进程死锁的缩写。它是由Dijkstra在1965年研究银行家算法时首次提出的。它是计算机操作系统乃至整个并发编程领域最难处理的问题之一。
怎么防止死锁?
死锁的四个必要条件:
互斥条件:一个进程不允许其他进程访问分配的资源。如果其他进程访问该资源,它们只能等到占用该资源的进程在该资源被使用后将其释放。
以及请求和持有条件:一个进程获得某个资源后,向其他资源发出请求,但该资源可能被其他进程占用,请求被阻塞,但仍然持有自己的资源。
不可剥夺的条件:指进程所获得的资源,在没有被完全使用之前不能被剥夺,使用之后只能自行释放。
循环等待条件:是指一个进程发生死锁后,几个进程形成首尾相连的循环等待资源关系。
这四个条件是死锁的必要条件。只要系统存在死锁,这些条件必然成立,但只要不满足上述条件之一,就不会出现死锁。
了解死锁产生的原因,尤其是四个必要条件,可以尽可能的避免、预防和解除死锁。
因此,在系统设计、进程调度等方面,要注意如何防止这四个必要条件不成立,如何确定合理的资源分配算法,避免进程永久占用系统资源。
此外,应该防止进程在等待时占用资源。所以要合理规划资源的分配。
ThreadLocal 是什么?有哪些使用场景?
线程的局部变量是限于线程内部的变量,由线程自身拥有,不在多个线程之间共享。提供Java ThreadLocal类支持线程局部变量是实现线程安全的一种方式。
但是,在管理环境(如web服务器)中使用线程局部变量时,应该特别小心。在这种情况下,工作线程的生命周期比任何应用程序变量的生命周期都长。
一旦工作完成后没有释放任何线程局部变量,Java应用程序就有内存泄漏的风险。
说一下 synchronized 底层实现原理?
Synchronized可以保证一个方法或代码块在运行时,同一时刻只能有一个方法进入临界区,还可以保证共享变量的内存可见性。
Java中的每个对象都可以用作锁,这是同步的基础:
常见的同步方法,锁是当前实例对象。
静态同步方法,锁是当前类的类对象
同步方法块,锁是括号里的对象。
synchronized 和 volatile 的区别是什么?
volatile的本质是告诉jvm,寄存器(工作内存)中当前变量的值是不确定的,需要从主存中读取;Synchronized锁定当前变量,只有当前线程可以访问它,其他线程被阻塞。
Volatile只能在可变级别使用;Synchronized可以在变量、方法和类级别使用。
Volatile只能实现变量的修改可见性,不能保证原子性;Synchronized可以保证变量的可见性和原子性。
Volatile不会造成线程阻塞;同步可能会导致线程阻塞。
编译器不会优化由volatile标记的变量;同步标记变量可以由编译器优化。
synchronized 和 Lock 有什么区别?
首先,synchronized是一个java内置关键字。在jvm级别,Lock是一个java类。
Synchronized不能判断是否获取锁状态,Lock可以判断是否获取锁;
Synchronized会自动释放锁(线程A执行完同步代码后会释放锁;b线程执行中的异常会释放锁),最终需要手动释放锁(unlock()方法释放锁),否则容易造成线程死锁;
带有synchronized关键字的两个线程1和2。如果当前线程1获得锁,线程2将等待。如果线程1被阻塞,线程2将一直等待,但锁锁不一定等待。如果不能获得锁,线程可以一直结束而不用等待。
同步锁可以重入,不间断,不公平,锁锁可以重入,判断,公平(两者都可以);
锁适用于大量同步代码的同步,同步锁适用于少量代码的同步。
synchronized 和 ReentrantLock 区别是什么?
Synchronized和if,else,for,while是同一个关键字,ReentrantLock是一个类,这是两者的本质区别。
由于ReentrantLock是一个类,它提供了比synchronized更多更灵活的特性。它可以被继承,有方法,有各种类变量。ReentrantLock over synchronized的可扩展性体现在几个方面:
ReentrantLock可以设置获取锁的等待时间,从而避免死锁。
ReentrantLock可以获得各种锁的信息。
ReentrantLock可以灵活实现多渠道通知。
另外,两者的锁定机制其实是不同的:ReentrantLock底层调用Unsafe的park方法进行锁定,同步的操作应该是对象头中的标记字。
说一下 atomic 的原理?
原子包中类的基本特点是多线程环境下多个线程同时对单个变量(包括基本类型和引用类型)进行操作时具有排他性,即多个线程同时更新变量的值时,只有一个线程可以成功,而不成功的线程可以像自旋锁一样不断尝试,直到执行成功。
原子类中的核心方法将调用不安全类中的几个局部方法。我们首先要知道的一点是Unsafe类,全称是:sun.misc.Unsafe这个类包含了大量对C代码的操作,包括很多直接的内存分配和原子操作调用。之所以标记为不安全,是想告诉你,这个类中大量的方法调用会有潜在的安全隐患,需要谨慎使用,否则会导致严重的后果。比如通过unsafe分配内存时,如果自己指定一些区域,可能会导致一些。以上是java多线程常见面试问题的详细介绍。更多请关注我们的其他相关文章!
郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。