1、Hashmap的工作原理
HashMap基于hashing原理,我们通过put()存储对象和get()获取对象。当我们将键值对传递给put()方法是,调用键值对象的hashCode()方法来计算hashcode,然后找到bucket位置来存储值对象。获取对象时,通过equals()方法找到正确的键值对,返回值对象。HashMap使用链表节点解决碰撞问题,发生碰撞时将会存在下一个节点去。HashMap在每一链表节点中存储键值对。
2、HashMap在那些情况下不安全(重写 覆盖时会丢失数据)
在hashmap存储put操作时会调用addEntry方法。同一个数组位置调用addEntry,两个线程忽悠覆盖的操作,数据丢失。
3、创建线程的方式实现同步有几种方法
a)创建线程的方式:
1.继承Thread类,并复写run方法创建该类对象,调用start方法开启线程。
2.实现Runnable接口,复写run方法,创建Thread类对象,将Runnable子类对象传递给Thread类对象,调用start开启线程。(推荐方式,线程对象和线程任务对象分离,降低了耦合性,利于维护)
3.创建FutureTask对象,创建Callable子类对象,复写call(相当于run方法),将其传递给FutureTask对象。创建Thread类对象,将FutureTask对象传递给Thread对象。调用start开启。
b)实现同步的方法:
1.同步方法和代码块:synchronized关键字修饰。在调用该方法前,需要获得内置锁,否则处于阻塞状态。
注意:synchronized关键字可以修饰静态方法,此时如果调用改进太方法,将会所致整个类。
2.wait和notify
wait():使一个线程处于等待状态,并且释放锁持有的对象的lock。
sellp():使一个正在运行的线程处于睡眠状态,是一个静态方法,调用次方法要扑捉异常InterruptedException异常。
notify():唤醒一个处于等待状态的线程,注意的实在调用此方法的时候并不能确切的唤醒某一个等待状态的线程,而是有JVM确定唤醒哪一个线程而不是按优先级。
notifyAll():唤醒所有处于等待状态的线程,注意并不是给所有线程一个对象的锁,而是让他们竞争。
3.使用特殊域变量(volatile)实现同步线程
volatile关键字为域变量的访问提供了一种免锁机制,使用volatile修饰域相当于告诉虚拟机该域会被更新,不能用来修饰final类型的变量。
4.使用重入锁实现线程同步
ReentrantLock类是可重入、互斥、实现了Lock接口的锁,它与使用synchronized方法和快具有相同的基本行为和语义,并且扩展了其能力。
5.使用局部变量实现线程同步
6.使用阻塞队列实现线程同步
4.简述synchornized和java.util.concurrent.locks.lock的异同
相同点:Lock能完成synchronize所实现的所有功能
不同点:Lock有比synchronize更精确的线程语义和更好的性能。synchronized会自动释放锁,而Lock一定要求程序员手工释放,并且必须在finally从句中释放。如果不在finaly中释放会抛出异常,线程卡死。
5.什么是线程池
线程池是一种多线程处理方式,处理过程中将任务添加队列,然后在创建爱你县城后自动启动这些任务。线程池线程都是后台线程。每个线程都是使用默认的堆栈大小,以默认的优先级运行。如果某个线程在托管代码中空闲(如正在等待某个事件),则线程池将插入另一个辅助线程来使所有处理器保持繁忙。如果所有线程池线程都始终保持繁忙,但队列中包含挂起的工作,则线程池将在一段时间后创建另一个辅助线程但线程的数目永远不会超过最大值。超过最大值的线程可以排队,但他们要等到其他线程完成后才启动。
6.创建线程池的方法
a)newFixedThreadPool(int nThreads)
创建一个固定长度的线程池,每当提交一个任务就创建一个线程,直到达到线程池的最大数量。发生错误结束时,线程池会补充一个新的线程。
b)newCachedThreadPool()
创建一个可缓存的线程池,如果线程池的规模超过了处理需求,将自动回收空闲线程,需求增加时,可以自动添加新线程,线程池规模不存在限制。
c)newSingleThreadExecutor()
这是一个单线程Executor,他创建单个工作线程来执行任务,如果 这个线程异常结束 ,会创建一个新的来代替他。特点是能确保依照任务在队列中的顺序来串执行。
d)newScheduledThreadPool(int corePoolSize)
创建安一个固定长度的线程池,而且以延迟或定时的方式来执行任务,类似于Timer。