多线程
笔者能力有限,总结有误的地方,请读者协作更正。
1.什么是线程?
线程是操作系统中运算调度的最小单位,包含在进程中; 使用多线程在处理密集任务的时候可以提速。Java语言对多线程提供了很好的支持。
2.线程和进程的区别?
一个进程可以有很多线程,每条线程执行不同的任务。 不同进程有不同的内存空间,所有的线程共享一块内存空间。 每个线程都有单独的栈内存用来存储本地数据。
3 .线程的实现方式?
继承Thread类,实现Runnable接口;
Java继承的单根性,实现Runnable接口,重写run()方法实现线程。
4.Start和Run的区别?
Start启动新创建的线程,start内部调用run方法;
直接调用run方法,只会在原来的线程中调用,没有新的线程启动,start方法会启动新线程。
5. Runable和Callable有什么不同?
Runnable从jdk1.0就开始有的,Callable是jdk1.5增加的; Callable的call()方法可以有返回值和抛出异常,Runnable的run()方法没有这些功能; Callable还可以返回装载有计算结果的Future对象。
6. Java的内存模型?
Java的内存模型规定和指引了java程序在不同的内存架构,CPU和操作系统之间有确定性的行为; 它们在多线程的情况下尤其重要,内存模型为多线程之间的可见性提供了保证;
内存模型中有一块共享的内存空间——主内存,持有所有线程的共享变量,各个线程的的本地内存持有的仅是共享变量的副本;
当线程A发生变化的时候,将副本信息刷新到主内存中;线程B在主内存中读取线程A改变的信息。
7. volatile变量是什么?
在并发编程中缺少同步的情况下,多线程对成员变量的操作是透明的,其它线程可见; vlatile可以保证下一个读取操作会在前一个写操作之后发生,只有成员变量才能使用它。
8. 什么是线程安全?Vector是一个线程安全类么?
多线程情况下,同时执行一段代码,运行结果和单线程保持一致,就是线程安全;
Vector是用同步方法来实现线程安全的;ArrayList不是线程安全的;
9. Java中的竞态条件?例子
多线程对一些资源的竞争,首先要执行的程序竞争失败重新排队,导致整个流程没有按照预期的顺序处理,出现一些不确定的,很难发现的bug,这种情况是竞态条件; 例如:无序处理
10. Java中如何停止一个线程?
没有停止线程的API; JDK1.0提供,stop(),suspend(),resume()等控制线程的方法,已经被被弃用,太暴力; 当run()或者call()方法执行完之后线程会自动结束; 手动结束,可以调用interrupt()来中断线程,有一个中断标志。
11. 一个线程发生异常时会怎样?
没有捕获线程会停止执行,抛异常UncaughtExceptionHandler; JVM内部提供了Thread.getUncaughtExceptionHandler()来查询线程是否设置异常处理。
12. 如何实现两个线程之间共享数据?
可以通过共享对象来实现;
13.Notify和NotifyAll的区别?
Notify()唤醒单个线程; notifyAll()唤醒所有的线程,让他们争夺锁。
14. 为什么wait,notify、notifyall这些方法不在thread类里面?
这些方法防放在Object类里面; 因为java提供的锁是对象级的锁,而不是线程级的锁,每个对象都有锁,通过线程获得; 定义在Thread里面,不符合对象锁的设计。
15. 什么是ThreadLocal变量?
本地线程变量; 让每一个线程都有ThredLocal,竞态条件就被消除;
对于频繁创建对象的线程,使用它可以减少对象的创建个数,在线程本地内存中持有变量副本,不用每次都创建;
例如:使用ThreadLocal可以让SimpleDateFormat变成线程安全的;
16. Interrupted和isInterrupted方法的区别?
前者会将中断状态清除,后者不会; Java多线程中的中断机制是用内部标识来实现的,调用interrupt来中断一个线程会设置一个中断标志true,查询中断状态的时候,标志会被清除; 后者用来查询中断状态不会改变中断状态标志; 前者是静态的,后者是非静态的;
17. 为什么wait和notify方法要在同步代码块中调用?
强制要求的,不这样做会抛异常IllegalMonitorStateException; 避免二者之间产生竞态条件;
18. Java中的同步集合与并发集合的区别?
同步集合与并发集合都为对线程提供了合适的线程安全的集合,并发集合扩展性更高;
Java5之前,只有同步集合,且在多线程并发的时候会导致争抢,阻碍看程序的扩展性;
Java5之后,出现并发集合,例如ConcurrentHashMap,不仅提供线程安全,还用锁分离和内部分区等,扩展性更好。
19. Java中堆和栈有什么不同?
栈是一块和线程紧密相关的内存区域,每个线程都有自己的栈内存,用于存储本地变量,方法参数和栈调用,一个栈中存储的变量对其它线程不可见;
堆是所有线程共享的一片公共内存区域,对象都在堆中创建; 为了提升效率,线程会从堆中弄一个缓存到自己的栈;
在多线程情况下,从公共内存中读取变量存在线程不安全问题,使用volatile变量可以保证线程安全。
20. 什么是线程池?为什么要使用它?
创建若干数量的线程来等待响应处理,就叫线程池,线程池里面的线程叫工作线程; Java API提供了Execution框架可以创建不同的线程池;
Why? 创建线程需要花费昂贵的资源和时间,任务来了,在创建的话,响应时间就会变长,影响效率;
可以创建单线程池,每次处理一个任务;
可以创建固定数量的线程池,或者可扩展的线程池来处理 多任务;
比喻:线程池比喻 公交场
21. 如何解决生产者消费者问题?
一个线程生产任务,提供给其它线程进行消费 ,这就是属于生产者消费者模型;
生产者消费者问题,可以通过线程之间的通讯来解决,java API提供了wait和notify方法来解决这个问题; 更好的方法是Semaphore或者BlockingQueue来实现生产者消费者模型。
22. 如何避免死锁?
死锁是两个或两个以上的进程在执行的时候,争夺资源造成相互等待,程序卡死的一种现象;
死锁的发生存在下面四个条件:
互斥条件,一个资源每次只能被一个进程调用;
请求与保持条件,一个进程获得资源阻塞时,对已经获得的资源保持不放;
不剥夺条件,进程已经获得资源,在未使用完成之前,不强行剥夺;
循环等待条件,进程之间头尾相接等待资源释放;
避免死锁就是阻止循环等待条件,将系统中的所有资源设置标志位,排序,规定所有的进程在申请资源的时候按顺序执行(升序或降序);
23. Java中活锁和死锁的区别?
活锁,就是进程的状态可以改变,但是不能够执行; 例如,走廊里两个人相遇,一个让一个,一直让不开的现象。
死锁,就是进程的状态不能改变,也不能够执行; 例如,走廊里两个人相遇,堵在那儿不动的现象。
24. 怎样检查一个线程是否拥有锁?
Java.lang.Thread中有一个方法holdsLock(),返回true,当前线程持有锁;
25. Java中synchronized和RentrantLock有什么不同?
它们都是锁;
使用synchronized关键字作为锁来实现互斥,它可以锁方法,锁语句块,锁对象,但是不能够扩展锁之外的方法或者边界,尝试获取锁时候中途不能取消等;
Java5之后的lock接口提供了更为复杂的控制来解决并发问题; RentrantLock类实现了Lock,它拥有synchronized相同的并发性和内存语义且扩展性更好。
26. 有3个线程,怎样保证他们按照顺序执行?
有很多种方法; 可以使用join()方法在一个线程中启动另一个线程,
比如:A,B,C三个线程;将A join到b中,b join到c中,先启动C线程,按照c-b-a的顺序执行;
27. Thread类中的yield方法有什么作用?
可以暂停当前正在执行的线程对象,让优先级高的先执行;
它是一个静态方法,保证当前线程放弃CPU占用,而不保证优先级高的就一定执行,有可能线程刚暂停又立马恢复;
28. Java中的ConcurrentHashMap的并发度是什么?
ConcurrentHashMap把实际的map划分为若干部分来实现它的可扩展性和线程安全;
这种划分是使用并发度获得的,它是ConcurrentHashMap类构造函数的一个构造参数,默认值是16,这样能在多线程情况下避免争用。
29. 如果你提交任务时候,线程池已经满,会发生什么?
会抛异常RejectedExecutionException;因为在非扩展线程池的情况下该线程任务不能够被调度。 **
- Java中线程池中的submit和execute方法有什么区别?**
两个方法都可以向线程池提交任务;
execute方法的返回值类型是void,定义在Execotor接口中;
Submit方法返回持有计算结果的future对象,它定义在ExecutorService接口中,它扩展了Exector接口;
其它的线程池类ThreadPoolExecutor和ScheduledThreadPoolExecutor都有这些方法;
31. 什么是阻塞式方法?
指程序会等待该方法完成,期间不会做其它的事情; ServerSocket类的accept方法就是阻塞式方法,会一直等待客户端连接;
阻塞指的是线程在调用结果返回之前,当前线程会被挂起,直到得到结果之后才会返回;
32. Java中的ReadWriteLock是什么?
读写锁,用来提升并发程序性能; Java5 新增的接口,一个读写锁维护一对关联的锁,一个用于读操作,一个用于写操作;
读锁是共享的,写锁是独占的。
33. 多线程中的忙循环是什么?
忙循环就是开发者用空循环让一个线程等待,一直持有CPU的控制;
不像wait(),sleep(),yield()等方法,放弃了CPU的控制;
好处是保留CPU缓存,减少线程等待,避免重建缓存。
34. 如果同步块内的线程抛出异常会发生什么?
线程会释放锁;
35. 单例模式的双检锁是什么 ?
它是用来创建线程安全的,写单例模式的老方法;
当单例实例第一次被创建时候它试图用单个锁进行性能优化,但是由于太过复杂几乎没人用。
36. 如何在java中创建线程安全的singleton?
双检索实现单例;
也可以通过JVM的类加载和静态变量初始化特征来创建单例;
或者利用枚举实现;
以上三种都是线程安全的可用的; **
- 写出3条你遵循的多线程最佳实践?**
1)给线程起一个有意义的名字 方便找bug或追踪线程执行;
2)避免锁定和缩小同步范围 锁花费的代价高昂,且上下文切换更耗费时间和空间,最低限度的使用同步锁,缩小临界区,有利于提升性能。
3)多用同步类,少用wait和notify CountDownLatch, Semaphore, CyclicBarrier 和 Exchanger 等这些同步类简化了编码操作; 而wait和notify很难实现相对复杂控制流控制; 使用高级的同步工具有利于优化线程;
4)多用并发集合少用同步集合 并发集合比同步集合扩展性好
38. 如何强制启动一个线程?
线程是被线程 调度器控制的,java中没有提供响应的API。
39. Java多线程中调用wait和sleep的方法有什么不同?
都可以让线程进入等待状态; wait方法用于线程间通信,如果等待条件为真且其它线程被唤醒是它会释放锁;
sleep方法仅仅是释放CPU的资源,让当前线程停止执行,不会释放锁;
- 其它
1.CyclicBarrier 和 CountDownLatch有什么不同?
CyclicBarrier和CountDownLatch都可以让一组线程等待其它线程; 不同点是CountdownLatch不能重新使用。
2.什么是FutureTask?
在java并发编程中,FutrueTask表示一个可以取消的异步运算;
它有启动运算,取消运算,查询运算,取回运算等方法,只有单运算完成才能取回结果,运算未完成get将会被阻塞。
异步运算也是调用Runnable接口,可以交给Executor来执行。
3.为什么应该在循环中检查等待条件?
处在等待状态的线程可能会收到错误报警和伪唤醒,不在循环中检查等待,程序就会在没有满足条件的情况下退出;
当一个线程被notify时候,并不认为它原来的状态仍然有效,因为这段时间它有可能改变。
4.如何在java中获取线程堆栈?
JVM会把所有线程的状态存到日志文件,或者输出到控制台;
Windows中Ctrl+Break组合键获取; Linux下用kill -3 命令获取; 业可以用jps这个工具找到id;
5.JVM中的那个参数是用来控制线程的栈堆大小的?
-Xss用来控制线程栈堆大小;
6.Java中的semaphore是什么?
是一种新的同步类,是一个计数信号;
信号量维护了一个许可集合,未经许可的线程请求会被阻塞,获得许可请求才可以进入排队状态;
Semaphore只对可用许可的信号进行计数;
信号量常用在多线程代码种,比如数据库连接池等
7.Swing是线程安全的么? 为什么?
不是线程安全的;
因为swing的提供的组件不能再多线程中进行修改,所有对GUI组件的更新都需要在AWT线程中完成;
而swin提供了同步和异步两种回调方法来进行更新;
8.Java中的invokeAndWait和invokeLater方法有什么区别?
作用都是从当前线程更新GUI组件; 前者同步更新GUI组件,比如一个进度条,一旦更新了,进度条就要做出相应改变;
后者请求派线程更新组件,比如一个进度条,一旦更新,并没有更新,需要等待派发线程完成更新,才最终更新。
9.Swing API中的哪些方法是线程安全的 ?
Swing不是线程安全的,它的一些方法是线程安全的;
比如:repaint(),revalidata(),JTextComponent的setText()方法和JTextArea的insert()方法和append()方法等。
10.Java中volatile变量和atomic变量有什么不同?
Java中的volatile变量可以确保先行关系,写操作会发生在后续的读操作之前,但是并不能保证原子性;
例如用volatile修饰count变量,那么count++操作就不是原子性的; AtomicInteger类提供的atomic方法可以让这种操作具有原子性;
11.Java中的fork join框架是什么 ?
Fork join框架是JDK7出现的一款工具,java开发人员可以通过它,充分利用现代服务器上的多出来器;
可以将所有可用的处理能力调用起来提升程序性能;
它使用了工作窃取算法,可以完成更多任务的工作线程,可以从其它线程中窃取任务来执行。
更多建议: