首页 > 编程语言 > 详细

Java多线程常见问题

时间:2019-12-31 20:12:12      阅读:88      评论:0      收藏:0      [点我收藏+]

记录Java多线程的常见概念和原理

参考:

如何停止一个线程

  • 使用volatile变量终止正常运行的线程 + 抛异常法/Return法
  • 组合使用interrupt方法与interruptted/isinterrupted方法终止正在运行的线程 + 抛异常法/Return法
  • 使用interrupt方法终止 正在阻塞中的 线程

线程安全

当多个线程访问某个类时,不管运行时环境采用何种调度方式或者这些线程将如何交替执行,并且在主调代码中不需要任何额外的同步或协同,这个类都能表现出正确的行为,那么这个类就是线程安全的。

为什么wait(), notify()和notifyAll()被定义在Object类里

Wait-notify机制是在获取对象锁的前提下不同线程间的通信机制。在Java中,任意对象都可以当作锁来使用,由于锁对象的任意性,所以这些通信方法需要被定义在Object类里。

为什么wait(), notify()和notifyAll()必须在同步方法或者同步块中被调用

wait/notify机制是依赖于Java中Synchronized同步机制的,其目的在于确保等待线程从Wait()返回时能够感知通知线程对共享变量所作出的修改。如果不在同步范围内使用,就会抛出java.lang.IllegalMonitorStateException的异常。

原子性

Java中只有对基本类型变量的赋值和读取是原子操作,如i = 1的赋值操作,但是像j = i或者i++这样的操作都不是原子操作,因为他们都进行了多次原子操作,比如先读取i的值,再将i的值赋值给j,两个原子操作加起来就不是原子操作了。所以,如果一个变量被volatile修饰了,那么肯定可以保证每次读取这个变量值的时候得到的值是最新的,但是一旦需要对变量进行自增这样的非原子操作,就不会保证这个变量的原子性了。

volatile关键字

  • 概念:为了提高处理速度,处理器不直接和内存进行通信,而是先将系统内存的数据读到内部缓存(L1,L2或其他)后再进行操作,但操作完不知道何时会写到内存。如果对声明了volatile的变量进行写操作,JVM就会向处理器发送一条Lock前缀的指令,将这个变量所在缓存行的数据写回到系统内存。一个处理器的缓存回写到内存会导致其他处理器的缓存失效; 当处理器发现本地缓存失效后,就会从内存中重读该变量数据,即可以获取当前最新值。这样针对volatile变量通过这样的机制就使得每个线程都能获得该变量的最新值。
  • 特点:保证了可见性,不能保证原子性

如何保证线程安全

  • 通过加锁(Lock/Synchronized)保证对临界资源的同步互斥访问
  • 使用volatile关键字,轻量级同步机制,但不保证原子性
  • 使用不变类 和 线程安全类(原子类,并发容器,同步容器等)

ThreadLocal及其引发的内存泄露

ThreadLocal是Java中的一种线程绑定机制,可以为每一个使用该变量的线程都提供一个变量值的副本,并且每一个线程都可以独立地改变自己的副本,而不会与其它线程的副本发生冲突。每个线程内部有一个 ThreadLocal.ThreadLocalMap 类型的成员变量 threadLocals,这个 threadLocals 存储了与该线程相关的所有 ThreadLocal 变量及其对应的值,也就是说,ThreadLocal 变量及其对应的值就是该Map中的一个 Entry,更直白地,threadLocals中每个Entry的Key是ThreadLocal 变量本身,而Value是该ThreadLocal变量对应的值。
实线代表强引用,虚线代表弱引用。map中的key为一个ThreadLocal实例。这个Map的确使用了弱引用,不过弱引用只是针对key,每个key都弱引用指向ThreadLocal对象。一旦把threadlocal实例置为null以后,那么将没有任何强引用指向ThreadLocal对象,因此ThreadLocal对象将会被 Java GC 回收。但是value却不能回收,因为存在一条从current thread连接过来的强引用。 只有当前thread结束以后, current thread就不会存在栈中,强引用断开,Current Thread、Map及value将全部被Java GC回收。技术分享图片
得出一个结论就是:只要这个线程对象被Java GC回收,就不会出现内存泄露。但是如果只把ThreadLocal引用指向null而线程对象依然存在,那么此时Value是不会被回收的,这就发生了我们认为的内存泄露。比如,在使用线程池的时候,线程结束是不会销毁的而是会再次使用的,这种情形下就可能出现ThreadLocal内存泄露。

Java多线程常见问题

原文:https://www.cnblogs.com/xlsryj/p/12126450.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!