最新下载
热门教程
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
Java中的引用类型和使用场景代码示例解析
时间:2022-06-29 02:10:08 编辑:袖梨 来源:一聚教程网
本篇文章小编给大家分享一下Java中的引用类型和使用场景代码示例解析,文章代码介绍的很详细,小编觉得挺不错的,现在分享给大家供大家参考,有需要的小伙伴们可以来看看。
Java中的引用类型有哪几种?
Java中的引用类型分成 强引用 , 软引用 , 弱引用 , 虚引用 。
1、强引用
没有引用指向这个对象,垃圾回收会回收
package git.snippets.juc; import java.io.IOException; public class NormalRef { public static void main(String[] args) throws IOException { M m = new M(); m = null; System.gc(); System.in.read(); } static class M { M() {} @Override protected void finalize() throws Throwable { System.out.println("finalized"); } } }
2、软引用
当有一个对象被一个软引用所指向的时候,只有系统内存不够用的时候,才会被回收,可以用做缓存(比如缓存大图片)
示例如下代码:注:执行以下方法的时候,需要把VM options设置为-Xms20M -Xmx20M。
package git.snippets.juc; import java.io.IOException; import java.lang.ref.SoftReference; import java.util.concurrent.TimeUnit; /** * heap将装不下,这时候系统会垃圾回收,先回收一次,如果不够,会把软引用干掉 * 软引用,适合做缓存 * 示例需要把Vm options设置为:-Xms20M -Xmx20M */ public class SoftRef { public static void main(String[] args) throws IOException { SoftReferencereference = new SoftReference<>(new byte[1024 * 1024 * 10]); System.out.println(reference.get()); System.gc(); try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(reference.get()); byte[] bytes = new byte[1024 * 1024 * 10]; System.out.println(reference.get()); System.in.read(); } }
上述代码在第一次执行System.out.println(reference.get())时候,由于堆的最大最小值都是 20M ,而我们分配的byte数组是 10M ,没有超过最大堆内存,所以执行垃圾回收,软引用不被回收,后续又调用了byte[] bytes = new byte[1024 * 1024 * 10]; 再次分配了 10M 内存,此时堆内存已经超过设置的最大值,会进行回收,所以最后一步的System.out.println(reference.get());无法 get 到数据。
3、弱引用
只要垃圾回收,就会回收。如果有一个强引用指向弱引用中的这个对象,如果这个强引用消失,这个对象就应该被回收。一般用在容器里面。
代码示例如下:
package git.snippets.juc; import java.lang.ref.WeakReference; import java.util.HashMap; import java.util.concurrent.TimeUnit; /** * 弱引用遭到gc就会回收 * ThreadLocal应用,缓存应用,WeakHashMap */ public class WeakRef { public static void main(String[] args) { WeakReferencereference = new WeakReference<>(new T()); System.out.println(reference.get()); System.gc(); System.out.println(reference.get()); } static class T { T() {} @Override protected void finalize() { System.out.println("finalized"); } } }
如果执行了一次GC,reference.get()获取到的值即为空。
4、弱引用的使用场景
弱引用的一个典型应用场景就是ThreadLocal,以下是ThreadLocal的的简要介绍
set方法:
public void set(T value) { Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) map.set(this, value); else createMap(t, value); }
get方法:
public T get() { Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) { ThreadLocalMap.Entry e = map.getEntry(this); if (e != null) { @SuppressWarnings("unchecked") T result = (T)e.value; return result; } } return setInitialValue(); }
ThreadLocalMap是当前线程的一个成员变量,所以,其他线程无法读取当前线程设置的ThreadLocal值。
ThreadLocal.ThreadLocalMap threadLocals = null;
ThreadLocal的主要应用场景
场景一:每个线程需要一个独享的对象:假设有100个线程都需要用到SimpleDateFormat类来处理日期格式,如果共用一个SimpleDateFormat,就会出现线程安全问题,导致数据出错,如果加锁,就会降低性能,此时使用ThreadLocal,给每个线程保存一份自己的本地SimpleDateFormat,就可以同时保证线程安全和性能需求。
场景二:每个线程内部保存全局变量,避免传参麻烦:假设一个线程的作用是拿到前端用户信息,逐层执行Service1,Service2,Service3,Service4层的业务逻辑,其中每个业务层都会用到用户信息,此时一个解决办法就是将 User 信息对象作为参数层层传递,但是这样会导致代码冗余且不利于维护。此时可以将 User 信息对象放入当前线程的 Threadlocal 中,就变成了全局变量,在每一层业务层中,需要使用的时候直接从 Threadlocal 中获取即可。
场景三:Spring的声明式事务,数据库连接写在配置文件,多个方法可以支持一个完整的事务,保证多个方法是用的同一个数据库连接(其实就是放在ThreadLocal里面)
了解了ThreadLocal简要介绍以后,我们可以深入理解一下ThreadLocal的一个内部原理,前面提到,ThreadLocal的 set 方法实际上是往当前线程的一个threadLocals表中插入一条记录,而这个表中的记录都存在一个 Entry 对象中,这个对象有一个key和一个value, key 就是当前线程的ThreadLocal对象。
static class Entry extends WeakReference> { /** The value associated with this ThreadLocal. */ Object value; Entry(ThreadLocal> k, Object v) { super(k); value = v; } }
这个 Entry 对象继承了WeakReference, 且构造函数调用了super(k), 所以Entry中的 key 是通过一个弱引用指向的ThreadLocal,所以,我们在主方法中调用
ThreadLocal
tl 是通过强引用指向这个ThreadLocal对象。
当前线程的threadLocalMap中的 key 是通过弱引用指向 ThreadLocal 对象,这样就可以保证,在 tl 指向空以后,这个 ThreadLocal 会被回收,否则,如果threadLocalMap中的 key 是强引用指向ThreadLocal对象话,这个ThreadLocal对象永远不会被回收。就会导致内存泄漏。
但是,即便 key 用弱引用指向 ThreadLocal 对象, key 值被回收后, Entry 中的 value 值就无法被访问到了,且 value 是通过强引用关联,所以,也会导致内存泄漏,所以,每次在ThreadLocal中的对象不用了,记得要调用remove方法,把对应的 value 也给清掉。
5、虚引用
用于管理堆外内存回收
虚引用关联了一个对象,以及一个队列,只要垃圾回收,虚引用就被回收,一旦虚引用被回收,虚引用会被装到这个队列,并会收到一个通知(如果有值入队列,会得到一个通知)所以,如果想知道虚引用何时被回收,就只需要不断监控这个队列是否有元素加入进来了。
虚引用里面关联的对象用get方法是无法获取的。
import java.lang.ref.PhantomReference; import java.lang.ref.Reference; import java.lang.ref.ReferenceQueue; import java.util.LinkedList; import java.util.List; // 配置 -Xms20M -Xmx20M public class PhantomRef { private static final List
6、虚引用的应用场景
JDK的 NIO 包中有一个DirectByteBuffer, 这个buffer指向的是堆外内存,所以当这个buffer设置为空的时候,Java的垃圾回收无法回收,所以,可以用虚引用来管理这个buffer,当我们检测到这个虚引用被垃圾回收器回收的时候,可以做出相应的处理,去回收堆外内存。
相关文章
- 《原神》5.2卡池抽取建议 11-14
- 《原神》5.2版本新怪物介绍 11-14
- 《原神》希诺宁增伤触发方法 11-14
- 《原神》循音觅奇活动入口 11-14
- 《原神》循音觅奇兑换码获取方法 11-14
- 《原神》花羽会活动飞行技巧介绍 11-14