多线程篇
1.Java中实现多线程的几种方法
-
-
实现Runnable接口
-
实现Callable接口
-
线程池方式创建
-
-
优点:直接使用this即可获取当前线程,编程简单
-
缺点:已经继承了Thread类,无法再继承其他父类了
-
-
优点:线程类只是实现了Runnable或者Callable接口,还可以继承其他类。这种方式下,多个线程可以给共享一个target对象,所以比较适合多个相同线程来处理同一份资源的情况。
-
缺点:编程较为复杂,如果需要访问当前线程,则必须使用
Thread.currentThread()方法。 -
区别:
-
Runnable通过重写run方法实现,Callable通过call方法实现
-
Runnable可以提交给Thread来包装下直接启动一个线程来执行,Callable一般都是提交给ExcuteService来执行
-
Runnable的任务无返回值,Callable的任务执行后有返回值
-
call方法可以抛出异常,run方法不可以
-
运行Callable任务可以拿到一个Future对象,表示异步计算的结果。
3.
6.为什么wait和notify方法要在同步块中调用?
-
-
如果不这么做会抛出IllegalMonitorStateException异常
-
避免wait()和notify()之间产生竞态条件 wait()方法强制当前线程释放对象锁。这意味着在调用某对象的wait()方法之前,当前线程必须已经获得该对象的锁。因此线程必须在某个对象的同步方法或同步代码块中才能够调用该对象的wait()方法。 在调用对象的notify()和notifyAll()方法之前,调用线程必须已经得到该对象的锁,因此,必须在某个对象的同步方法或同步代码块中才能够调用该对象的notify()和notifyAll()方法。 调用wait()方法的原因通常是调用线程希望某个特殊的状态或变量被设置之后再继续执行。 调用notify()和notifyAll()方法的原因通常是调用线程希望告诉其他等待线程"特殊状态已被设置"。 这个状态作为线程间通信的通道,他必须是一个可变的共享状态或变量。
7.Thread类中的yield方法有什么作用?
yield()方法可以暂停当前正在执行的线程对象,让其它有相同优先级的线程执行。它是一个静态方法而且保证当前线程放弃CPU占用而不能保证其他线程一定能占用CPU,执行yield()的线程有可能在进入暂停状态后马上又被执行。
8.线程、进程的区别
-
-
线程时进程的一个实体,一个进程拥有多个线程,线程间共享地址空间和其他资源,所以通信和同步等操作线程比进程更加容易。
-
线程上下文的切换比进程上下文切换要快很多
-
进程切换时,涉及到当前进程的CPU环境的保存和新被调度运行进程的CPU环境的设置。
-
线程切换仅需要保存和设置少量寄存器内容,不涉及存储管理方面的操作。
9.
-
-
请求与保持条件:一个线程因请求资源而阻塞时,对已获得的资源保持不放。
-
不剥夺条件:进程已经获得的资源,在未使用完之前,不能强行剥夺。
-
循环等待条件:若干线程之间形成一种头尾相接的循环等待资源。
-
-
占有部分资源的线程进一步申请其他资源时,如果申请不到,主动释放它占有的资源,破坏 "不可抢占" 条件
-
按序申请资源,破坏 "循环等待" 条件
10.
-
-
软引用:可以用SoftReference来描述,指的是那些有用但是不是必须要的对象。系统在发生内存溢出前会对这类引用的对象进行回收。
-
弱引用:可以用WeakReference来描述,它的强度比软引用更低一点,弱引用对象无论内存是否足够,下一次GC的时候一定会被回收
-
虚引用:被称为欢迎引用,是最弱的引用关系,可以用PhantomReference来描述,它必须和ReferenceQueue一起使用,同样的当发生GC的时候,虚引用也会被回收。可以用虚引用来管理堆外内存。
12.
-
-
newFixedThreadPool:创建固定大小的线程池,每次提交一个任务就创建一个线程,知道线程达到线程池的最大大小。
-
newCacheThreadPool:创建一个可缓存的线程池,此线程池不会对线程池大小做限制,线程池大小完全依赖于操作系统(JVM)能够创建的最大线程大小。
-
newScheduledThreadPool:创建一个大小无限的线程池,此线程池支持定时以及周期性执行任务的需求。
13.简述一下你对线程池的理解
- 降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。
- 提高响应速度。当任务到达时,任务可以不需要等待线程创建就能立即执行。
- 提高线程的可管理性。线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一分配、调优和监控。
14.线程池的原理以及核心参数
-
最大线程数--maximumPoolSize
-
核心线程数--corePoolSize
-
活跃时间--keepAliveTime
-
阻塞队列--workQueue
-
拒绝策略--RejectedExecutionHandler 当提交一个新任务到线程池时,具体执行流程如下:
-
当我们提交任务,线程池会根据核心线程数(corePoolSize)大小创建若干任务数量线程执行任务
-
当任务的数量超过核心线程数(corePoolSize)数量,后续任务将会进入阻塞队列(workQueue)阻塞排队
-
当阻塞队列(workQueue)也满了之后,那么将会继续创建(maximumPoolSize-corePoolSize)个数量的线程来执行任务,如果任务处理完成,maximumPoolSize-corePoolSize额外创建的线程等待活跃时间(keepAliveTime)之后被自动销毁。
-
15.
-
-
CallRunsPolicy:只是调用者所在的线程来处理任务。
-
DiscardOldestPolicy:丢弃等待队列中最旧的任务,并执行当前任务。
-
DiscardPolicy:直接丢弃任务,也不抛出异常。
16.
浙公网安备 33010602011771号