多线程在面试的时候,问到的几率比较大,我们来看看多线程需要掌握些什么?
自由发挥
-
典型案例:StringBuild:线程不安全,效率高 JDK1.5,StringBuffer:线程安全,效率低 JDK1.0
多线程创建?
一:继承Thread类
(1)定义Thread类的子类,并重写该类的run方法,该run方法的方法体就代表了线程要完成的任务。因此把run()方法称为执行体。
(2)创建Thread子类的实例,即创建了线程对象。
(3)调用线程对象的start()方法来启动该线程。
二:实现 Runnable 接口(由于Java不支持多继承,所以实现接口方式更利于代码的扩展,同时线程池也是非常高效,易于实现和使用)
(1)定义runnable接口的实现类,并重写该接口的run()方法,该run()方法的方法体同样是该线程的线程执行体。
(2)创建 Runnable实现类的实例,并依此实例作为Thread的target来创建Thread对象,该Thread对象才是真正的线程对象。
(3)调用线程对象的start()方法来启动该线程。
三:应用程序可以使用 Executor 框架来创建线程池
线程池?
线程的五大状态:
1.新建状态(new):我们常用的是实现Runnable接口,然后创建线程对象
2.就绪状态(runnable):也称可运行状态,线程不会自动运行,需调用start()方法启动线程,那么该线程位于可运行线程池中就绪,处于就绪状态的线程并不一定立即运行run()方法,线程还必须同其他线程竞争CPU时间,只有获得CPU时间才可以运行线程。
3.运行状态(Running): 当线程获得CPU时间后,它才进入运行状态,真正开始执行run()方法.
4.阻塞状态(Blocked):阻塞状态是线程因为某种原因放弃CPU使用权,暂时停止运行。直到线程进入就绪状态,才有机会转到运行状态。阻塞情况有三种:
(1)、等待阻塞:运行的线程执行o.wait()方法,该线程会释放占用的所有资源,JVM会把该线程放入“等待池”中。进入这个状态后,是不能自动唤醒的,必须依靠其他线程调用notify()或notifyAll()方法才能被唤醒,
(2)、同步阻塞:运行的线程在获取对象的同步锁时,若该同步锁被别的线程占用,则JVM会把该线程放入“锁池”中。
(3)、其他阻塞:运行的线程执行Thred.sleep()或t.join()方法,或者发出了I/O请求时,JVM会把该线程置为阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。
5、死亡状态(Dead):结束生命周期的方式有:run()方法或main()执行结束正常退出,一个未捕获的异常终止了run()方法突然死亡。已死亡的线程不可复活。
sleep() 和 wait() 的区别
可以从几个方面来理解:
1.sleep()是来自Thread类静态方法,wait()是来自Object类。
2.锁:最主要是sleep方法没有释放锁,而wait方法释放了锁,让其他线程可以使用同步控制块或者方法。
3.使用范围:wait,notify和notifyAll只能在同步控制方法或者同步控制块里面使用,而sleep可以在任何地方使用。
什么是死锁?如何避免死锁?
死锁:在两个或多个并发进程中,如果每个进程都在等待对方执行完毕才能继续执行,若无外力作用,它们都将陷入无限的等待对方释放资源,我们称这一组进程产生了死锁
如何避免:1.加锁顺序:指定获取锁的顺序,并强制线程按照指定的顺序获取锁。因此,如果所有的线程都是以同样的顺序加锁和释放锁,就不会出现死锁了。2.加锁时限:尝试获取锁的时候加一个超时时间,这也就意味着在尝试获取锁的过程中若超过了这个时限该线程则放弃对该锁请求。
HashMap和HashTable有什么区别?
主要有以下三点:
①继承的父类不同,但是都实现了Map接口。(Hashtable 继承自 Dictionary 类,而 HashMap 继承自AbstractMap 类 )。
②HashTable的key和value都不允许为null值,而HashMap的key和value则都是允许null值的。
③Hashtable 是同步的,线程安全的,而 HashMap 是非同步的,非线程安全的。因此, HashMap 更适合于单线程环境(多线程环境下可以采用concurrent并发包下的concurrentHashMap),而 Hashtable适合于多线程环境。
怎么理解什么是同步?
同步用来控制共享资源在多个线程间的访问,以保证同一时间内只有一个线程能访问到这个资源。在非同步保护的多线程程序里面,一个线程正在修改一个共享变量的时候,可能有另一个线程也在使用或者更新它的值。同步避免了脏数据的产生