○ 实现Runnable接口可以避免Java单继承特性而带来的局限;增强程序的健壮性,代码能够被多个线程共享,代码与数
据是独立的;适合多个相同程序代码的线程区处理同一资源的情况。
○ 继承Thread类和实现Runnable方法启动线程都是使用start方法,然后JVM虚拟机将此线程放到就绪队列中,如果有
处理机可用,则执行run方法。
○ 实现Callable接口要实现call方法,并且线程执行完毕后会有返回值。其他的两种都是重写run方法,没有返回值。
15. 线程安全
• 定义
○ 某个类的行为与其规范一致。
○ 不管多个线程是怎样的执行顺序和优先级,或是wait,sleep,join等控制方式,,如果一个类在多线程访问下运转一切正
常,并且访问类不需要进行额外的同步处理或者协调,那么我们就认为它是线程安全的。
• 如何保证线程安全?6
○ 对变量使用volitate
○ 对程序段进行加锁(synchronized,lock)
• 注意
○ 非线程安全的集合在多线程环境下可以使用,但并不能作为多个线程共享的属性,可以作为某个线程独享的属性。
○ 例如Vector是线程安全的,ArrayList不是线程安全的。如果每一个线程中new一个ArrayList,而这个ArrayList只是在
这一个线程中使用,肯定没问题。
16. 多线程如何进行信息交互
• Object中的方法,wait(), notify(),notifyAll();
17. 多线程共用一个数据变量需要注意什么?
• 当我们在线程对象(Runnable)中定义了全局变量,run方法会修改该变量时,如果有多个线程同时使用该线程对象,那么
就会造成全局变量的值被同时修改,造成错误.
• ThreadLocal是JDK引入的一种机制,它用于解决线程间共享变量,使用ThreadLocal声明的变量,即使在线程中属于全局变
量,针对每个线程来讲,这个变量也是独立的。
• volatile变量每次被线程访问时,都强迫线程从主内存中重读该变量的最新值,而当该变量发生修改变化时,也会强迫线程将
最新的值刷新回主内存中。这样一来,不同的线程都能及时的看到该变量的最新值。
18. 什么是线程池?如果让你设计一个动态大小的线程池,如何设计,应该有哪些方法?
• 什么是线程池
○ 线程池顾名思义就是事先创建若干个可执行的线程放入一个池(容器)中,需要的时候从池中获取线程不用自行创建,
使用完毕不需要销毁线程而是放回池中,从而减少创建和销毁线程对象的开销。
• 设计一个动态大小的线程池,如何设计,应该有哪些方法
○ 一个线程池包括以下四个基本组成部分:
• 线程管理器(ThreadPool):用于创建并管理线程池,包括创建线程,销毁线程池,添加新任务;
• 工作线程(PoolWorker):线程池中线程,在没有任务时处于等待状态,可以循环的执行任务;
• 任务接口(Task):每个任务必须实现的接口,以供工作线程调度任务的执行,它主要规定了任务的入口,任务执行
完后的收尾工作,任务的执行状态等;
• 任务队列(TaskQueue):用于存放没有处理的任务。提供一种缓冲机制;
○ 所包含的方法
• private ThreadPool() 创建线程池
• public static ThreadPool getThreadPool() 获得一个默认线程个数的线程池
• public void execute(Runnable task) 执行任务,其实只是把任务加入任务队列,什么时候执行有线程池管理器
决定
• public void execute(Runnable[] task) 批量执行任务,其实只是把任务加入任务队列,什么时候执行有线程池管
理器决定
• public void destroy() 销毁线程池,该方法保证在所有任务都完成的情况下才销毁所有线程,否则等待任务完成
才销毁
• public int getWorkThreadNumber() 返回工作线程的个数
• public int getFinishedTasknumber() 返回已完成任务的个数,这里的已完成是只出了任务队列的任务个数,可能
该任务并没有实际执行完成
• public void addThread() 在保证线程池中所有线程正在执行,并且要执行线程的个数大于某一值时。增加线程
池中线程的个数
• public void reduceThread() 在保证线程池中有很大一部分线程处于空闲状态,并且空闲状态的线程在小于某一
值时,减少线程池中线程的个数
19. Java是否有内存泄露和内存溢出
• 静态集合类,使用Set、Vector、HashMap等集合类的时候需要特别注意。当这些类被定义成静态的时候,由于他们的生命
周期跟应用程序一样长,这时候就有可能发生内存泄漏。
例子
class StaticTest
{
private static Vector v = new Vector(10);
public void init()
{
for (int i = 1; i < 100; i++)
{
Object object = new Object();
v.add(object);
object = null;
}
}
}
在上面的代码中,循环申请了Object对象,并添加到Vector中,然后设置为null,可是这些对象呗vector引用着,因此不能
被GC回收,因此造成内存泄漏。因此要释放这些对象,还需要将它们从vector删除,最简单的方法就是将vector设置为null
• 监听器: 在Java编程中,我们都需要和监听器打交道,通常一个应用中会用到很多监听器,我们会调用一个控件,诸如
addXXXListener()等方法来增加监听器,但往往在释放的时候却没有去删除这些监听器,从而增加了内存泄漏的机会。