java多线程

193 阅读8分钟

1、什么是线程死锁

死锁是指两个或两个以上的进程(线程)在执行过程中,互相占用对方想要的资源不释放而造成堵塞的现象。

2、形成死锁的四个必要条件

(1)互斥条件:在一段时间内,某个资源只能有一个进程控制,如果还有其他进程请求资源,只能等待,直至占用资源的进程用毕释放 (2)请求和保持条件:指进程已经保持至少一个资源,但又提出了新的资源请求,而该资源已经被其他线程占有,此时请求进程阻塞,但又对自己占有的资源保持不放 (3)不可抢占条件:进程已获得的资源未使用完之前不能被抢占,只能在进程使用完时由自己释放 (4)循环等待条件:在发生死锁时,必然存在一个进程——资源的循环链,即进程集合(A在等B,B在等C,C在等A)

3、处理死锁的方法

(1)预防死锁 (2)避免死锁 (3)检测死锁 (4)解除死锁

4、如何避免线索死锁

(1)避免一个线程同时获得多个锁 (2)避免一个线程在锁内同时占用多个资源,尽量保证每一个锁只占用一个资源 (3)尝试使用定时锁,使用lock.tryLock(timeOut)来替代使用内部锁机制

5、创建线程的四种方式

(1)继承Thread类 (2)实现Runnable接口 (3)实现Callable接口 (4)使用匿名内部类

6、runnable和callable有什么区别

Runnable接口run方法无返回值,Callable接口call方法有返回值,是个泛型,和Future、FutureTask配合可以用来获取异步执行的结果 Runnable接口run方法只能抛出运行时异常,且无法捕获处理,Callable接口call方法允许抛出异常,可以获取异常信息。Callable接口支持返回执行结果,需要调用FutureTask。get()得到,此方法会阻塞主进程的继续执行,如果不调用不会阻塞。

7、线程的run()和start()有什么区别

每个线程都是通过某个特定THread对象所对应的方法run()来完成其操作的,run()方法称为线程体,通过调用Thread类的start()方法来启动一个线程。

8、什么是Callable和Future?

Callable接口类似Runnable接口,从名字上就可以看出来了,但是Runnable不会返回结果,并且无法抛出返回结果的异常,而Callable功能更强大一些,线程被执行后,可以有返回值,这个返回值被Future拿到,也就是说,future可以拿到异步执行任务的返回值 Future接口表示异步任务,是一个可能还没有完成的异步任务的结果,所以说Callable用于产生结果,Future用于获取结果

9、线程同步及线程调度相关的方法

wait():使一个线程处于等待状态,并且释放所有持有的对象的锁

sleep():使一个正在运行的线程处于睡眠状态,是一个静态方法,调用此方法要处理interruptedException异常

notify():唤醒一个处于等待的线程,当然在调用此方法的时候,并不确切的唤醒某个等待状态的线程,而是由JVM确定唤醒哪个进程,而且与优先级无关

notityAll():唤醒所有处于等待状态的线程,该方法并不是将对象的锁给所有线程,而是让他们竞争,只有获得锁的线程才能进入就绪状态

10、java中你怎么样唤醒一个阻塞的线程

首先,wait()、notify()方法是针对对象的,调用任意对象的wait()方法都将导致线程阻塞,阻塞的同时也将释放该对象的锁,相应地,调用任意对象的notify()方法则将随机解除该对象阻塞的线程,但它需要重新获取该对象的锁,直到获取成功才能往下执行;

其次,wait()、notify方法必须在synchronized块或方法中被调用,并且要保证同步块或方法的锁对象与调用wait()、notify()方法的对象是同一个,如此一来在调用wait之前当前线程就已经成功获取某对象的锁,执行wait阻塞后当前线程就将之前获取的对象锁释放。

11、notify和notifyAll有什么区别?

如果线程调用了对象的wait()方法,那么线程会处于该对象的等待池中,等待池中的线程不会去竞争该对象的锁。 notifyAll会唤醒所有的线程,notify()只会唤醒一个线程。notifyAll调用后,会将全部线程由等待池移到锁池,然后参与锁的竞争,竞争成功则继续执行,如果不成功则留在锁池等待被释放后再次参与竞争。而notify()只会唤醒一个线程,具体唤醒哪一个线程由虚拟机控制。

12、什么是线程同步和线程互斥,有哪几种实现方式?

当一个线程对共享的数据进行操作时,应使之成为一个原子操作,即在没有完成相关操作之前,不允许其他线程打断它,否则,就会破坏数据的完整性,必然会得到错误的处理结果,这就是线程的同步。 实现线程同步的方法,同步代码块方法: 1、synchronized关键字修饰的方法 2、同步代码块:synchronized关键字修饰的代码块 3、使用特殊变量域volatile实现线程同步:volatile关键字为域变量的访问提供了一种免锁机制 4、使用重入锁实现线程同步:reentrantLock类是可冲入、互斥、实现了lock接口的锁它与sychronized方法具有相同的基本行为和语义。

13、如果提交任务时,线程池队列已满,这时会发生什么

有两种可能: (1)如果使用的是无界队列LinkedBlockingQueue,也就是无界对列的话,没关系,继续添加任务到阻塞队列中等待执行,因为LinkedBlockingQueue可以近乎认为是一个五穷大的队列,可以无限存放任务。 (2)如果使用的是有界队列比如ArrayBlockingQueue,任务首先会被添加到ArrayBlockingQueue中,ArrayBlockingQueue满了,会根据maximumPoolSize的值增加线程数量,如果增加了线程数量还是处理不过来,ArrayBlockQueue继续满,那么则会使用拒绝策略RejectExecutionHandler处理满了的任务,默认AbortPolicy

13、在java程序中怎么保证多线程的运行安全?

(1)使用安全类,比如java.util.concurrent下的类,使用原子类Atomiclnteger (2)使用自动锁synchronized (3)手动锁

14、线程之间如何通信及线程之间如何同步

通过在线程之间共享就可以了,然后通过wait/notify/notifyAll、await/signal/sianlAll进行换起和等待,比方说阻塞队列BlockQueue就是为线程之间共享数据而设计

15、说说自己是怎么使用synchronize关键字,在项目中用到了吗

修饰实例方法:作用于当前对象实例加锁,进入同步代码前要获得当前对象实例的锁 修饰静态方法:也就是给当前类加锁,会作用于类的所有对象实例,因为静态成员不属于任何一个实例对象,是类成员。所以如果一个线程A调用一个实例对象所属类的静态synchronized方法,而线程B需要调用一个实例对象的非静态synchronized方法,是允许的,不会发生互斥现象,因为访问静态synchronized方法占用的锁是当前类的锁,而访问非静态synchronized方法占用的锁是当前实例对象锁。 修饰代码块:指定加锁对象,对给定对象加锁,进入同步代码库前要获得给定对象的锁

16、单例模式了解吗?给我解释一下双重检验锁方式实现单例模式

单例模式主要有2种模式:懒汉式、饿汉式

17、什么是CAS

CAS是compare and swap的缩写,即我们所说的比较交换 CAS是一种基于锁的操作,而且是乐观锁。在java中锁分为乐观锁,在java中锁分为乐观锁和悲观锁,悲观锁是将资源锁住,等一个之前获得锁的线程释放之后,下一个线程才可以访问;而乐观锁采取了一种宽泛的态度,通过某种方式不加锁了处理资源,性能较悲观锁有很大的提升。

18、volatile 关键字的作用

对于可见性,Java 提供了 volatile 关键字来保证可见性和禁止指令重排。 volatile 提供 happensbefore 的保证,确保一个线程的修改能对其他线程是可见的。当一个共享变量被 volatile 修饰 时,它会保证修改的值会立即被更新到主内存中,当有其他线程需要读取时,它会去内存中读取新 值。 从实践角度而言,volatile 的一个重要作用就是和 CAS 结合,保证了原子性,volatile 常用于多线程环境下的单次操作(单次读或者单次写)。