深入浅出解析Java ThreadLocal原理

目录
  • 1.了解ThreadLocal
    • 简介
    • 使用
  • 2.源码解析 – 探究实现思路
    • threadLocals变量与ThreadLocalMap
    • set(T value) 方法
    • get() 方法
    • remove() 方法
    • 实现思路总结
  • 3.InheritableThreadLocal与继承性
    • ThreadLocal的不可继承性
    • InheritableThreadLocal实现继承性的源码剖析
    • 如何理解这个继承性
    • 总结
  • 4.存在的内存泄露问题
    • 使用强引用会如何?
    • 使用弱引用会如何?
    • set()、get()、remove() 方法中相关实现
    • 总结
  • 5.ThreadLocal应用

分享一下最近看的ThreadLocal的源码的一些体会。

1.了解ThreadLocal

简介

  • ThreadLocal是JDK中java.lang包下提供的类。
  • ThreadLocal是线程安全的,并且没有使用到锁。
  • 常用来存放线程独有变量,解决参数传递问题。
  • 当我们创建一个ThreadLocal包装的变量后,每个访问这个变量的线程会在自己的线程空间创建这个变量的一个副本,在每次操作这个变量的时候,都是在自己的线程空间内操作,解决了线程安全问题。

使用

  • (是线程安全的) 在这个demo中,localStr是共享的,随后在每个线程中给localStr设置值为自己线程的名字,然后再将当前线程的日志输出。
  • sleep5毫秒是为了体现出是否存在线程安全问题。
  • 从运行结果可以看到,是不存在线程安全问题的:
/**
 * @author ATFWUS
 * @version 1.0
 * @date 2021/11/8 21:23
 * @description
 */
@Slf4j
public class ThreadLocalTest {

    static ThreadLocal<String> localStr = new ThreadLocal<>();

    public static void main(String[] args) {
        List<Thread> list = new LinkedList<>();
        for(int i = 0; i < 1000; i++){
            Thread t = new Thread(new Runnable() {
                @Override
                public void run() {
                    localStr.set(Thread.currentThread().getName() + " localStr");
                    try {
                        Thread.sleep(5);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    log.debug(localStr.get());
                }
            }, "t" + String.valueOf(i));
            list.add(t);
        }
        for (Thread t : list) {
            t.start();
        }

    }
}

而对于普通变量来说,很明显是存在线程安全问题的:

/**
 * @author ATFWUS
 * @version 1.0
 * @date 2021/11/8 21:23
 * @description
 */
@Slf4j
public class ThreadLocalTest {

    static ThreadLocal<String> localStr = new ThreadLocal<>();
    static String shareStr;

    public static void main(String[] args) {
        List<Thread> list = new LinkedList<>();
        for(int i = 0; i < 1000; i++){
            Thread t = new Thread(new Runnable() {
                @Override
                public void run() {
                    shareStr = Thread.currentThread().getName() + "  shareStr";
                    try {
                        Thread.sleep(5);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    log.debug(shareStr);
                }
            }, "t" + String.valueOf(i));
            list.add(t);
        }
        for (Thread t : list) {
            t.start();
        }

    }
}

2.源码解析 – 探究实现思路

threadLocals变量与ThreadLocalMap

  • 每个线程的本地变量并不存放于ThreadLocal对象中,而是存在调用线程的threadLocals变量中。因为是线程对象的成员变量,所以生命周期等同于线程的生命周期。

  • 而threadLocals是ThreadLocalMap类的实例。
  • ThreadLocalMap实际上是一个类似HashMap的实现,是ThreadLocal的静态内部类。
  • 看下Doug Lea写的注释: ThreadLocalMap是一个定制的hash map,仅适用于维护线程本地值。在ThreadLocal类之外没有暴露任何的操作。这个类是私有的,允许在类线程中声明字段。为了处理非常大并长期存在(对象)的用法,哈希表的entries使用weakReference作为键。但是,由于没有使用引用队列,因此只有当表开始耗尽空间时,才能保证删除过时的entries。

  • 暂不探究ThreadLocalMap的内部实现细节,暂时只需要知道实现了一个hash map,并且Entry的key是弱引用即可,具体的set() get() remove() 方法在下文中会有。

set(T value) 方法

  • 进入set(T value) 方法后,先尝试获取map,如果获取到了map,直接设置值,否则新建一个map。
    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() 方法

  1. 进入get()方法后,首先获取当前线程,然后进入getMap(Thread t)中获取ThreadLocalMap对象,直接返回t.threadLocals。
  2. 如果map不为空,直接返回map中当前ThreadLocal作为键对应的值。
  3. 如果map为空,需要先进行初始化。调用setInitialValue()方法进行初始化。
  • setInitialValue()中先获取一个初始值,默认为null。
  • 如果map存在当前线程中,直接设置初始值。
  • 如果map不存在当前线程中,需要先创建一个map。
  • createMap(Thread t, T firstValue)中就是new了一个ThreadLocalMap对象,并且初始化了一个entry对。
    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 getMap(Thread t) {
        return t.threadLocals;
    }
    private T setInitialValue() {
        T value = initialValue();
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
        return value;
    }
    protected T initialValue() {
        return null;
    }
    void createMap(Thread t, T firstValue) {
        t.threadLocals = new ThreadLocalMap(this, firstValue);
    }

remove() 方法

  • remove() 方法中,先判断map是否存在,不存在直接将map中this作为键的entry删掉。
     public void remove() {
         ThreadLocalMap m = getMap(Thread.currentThread());
         if (m != null)
             m.remove(this);
     }

实现思路总结

  • ThreadLocal搭配线程的threadLocals变量实现,当调用set(T value) 和 get() 方法时,如果线程中的threadLocals仍然为null,会为其初始化。
  • ThreadLocal对象往threadLocals存储具体变量时,key是ThreadLocal对象的自身引用,value是真正的变量,且key是弱引用。

3.InheritableThreadLocal与继承性

InheritableThreadLocal英语翻译一下就是可继承的ThreadLocal,让我们看下它和ThreadLocal的继承性体现在哪。

这里的继承性指的是:子线程是否能访问父线程的变量。

ThreadLocal的不可继承性

threadLocals是当前线程的成员变量,在子线程中不可见

/**
 * @author ATFWUS
 * @version 1.0
 * @date 2021/11/9 14:29
 * @description
 */
@Slf4j
public class InheritableThreadLocalTest {

    static ThreadLocal<String> localStr = new ThreadLocal<>();

    public static void main(String[] args) {
        localStr.set("main线程为其设置的值");
        new Thread(new Runnable() {
            @Override
            public void run() {
                log.debug("访问localStr : " + localStr.get());
            }
        }).start();
        System.out.println(localStr.get());
    }
}

InheritableThreadLocal实现继承性的源码剖析

看一下InheritableThreadLocal的源码:

源码非常简短,下面简单分析一下:

  • InheritableThreadLocal类继承自ThreadLocal类,重写了childValue(T parentValue)、getMap()、createMap(Thread t, T firstValue) 三个方法。
  • createMap(Thread t, T firstValue)会在初始化的时候调用,重写createMap(Thread t, T firstValue) 意味着,InheritableThreadLocal的实例使用的是线程对象中的inheritableThreadLocals,而不再是原来的threadLocals。
  • getMap() 方法也是确保使用的是inheritableThreadLocals。
  • childValue(T parentValue) 方法中,直接返回了parentValue,这个方法会在ThreadLocal的构造方法中被调用,为了弄清这个意图,我们有必要看看Thread类初始化方法的源码。

从Thread的构造方法看,发现所有的构造方法都会调用init()方法进行初始化,init()方法有两个重载形式。

我们进入参数较多的init方法查看一下:

    private void init(ThreadGroup g, Runnable target, String name,
                      long stackSize, AccessControlContext acc,
                      boolean inheritThreadLocals) {
        if (name == null) {
            throw new NullPointerException("name cannot be null");
        }

        this.name = name;
				// 新线程还未创建出来,当前线程就是即将要创建线程的父线程
        Thread parent = currentThread();
        SecurityManager security = System.getSecurityManager();
        if (g == null) {
            /* Determine if it's an applet or not */

            /* If there is a security manager, ask the security manager
               what to do. */
            if (security != null) {
                g = security.getThreadGroup();
            }

            /* If the security doesn't have a strong opinion of the matter
               use the parent thread group. */
            if (g == null) {
                g = parent.getThreadGroup();
            }
        }

        /* checkAccess regardless of whether or not threadgroup is
           explicitly passed in. */
        g.checkAccess();

        /*
         * Do we have the required permissions?
         */
        if (security != null) {
            if (isCCLOverridden(getClass())) {
                security.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION);
            }
        }

        g.addUnstarted();

        this.group = g;
        this.daemon = parent.isDaemon();
        this.priority = parent.getPriority();
        if (security == null || isCCLOverridden(parent.getClass()))
            this.contextClassLoader = parent.getContextClassLoader();
        else
            this.contextClassLoader = parent.contextClassLoader;
        this.inheritedAccessControlContext =
                acc != null ? acc : AccessController.getContext();
        this.target = target;
        setPriority(priority);
        // 如果父线程的inheritThreadLocals 不为空
        if (inheritThreadLocals && parent.inheritableThreadLocals != null)
        		 // 设置子线程中的inheritableThreadLocals设置为父线程的inheritableThreadLocals
            this.inheritableThreadLocals =
                ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
        /* Stash the specified stack size in case the VM cares */
        this.stackSize = stackSize;

        /* Set thread ID */
        tid = nextThreadID();
    }

我们重点看一下和inheritThreadLocals相关的地方(含注释的地方)

  • 在进入init方法后,先获取了父线程,然后再下面判断了父线程的inheritThreadLocals 是否为空,不为空就调用ThreadLocal.createInheritedMap方法,参数就是父线程的inheritThreadLocals 。

再看下ThreadLocal.createInheritedMap方法:

  • 调用了自身的构造方法,将parentMap传入。
    static ThreadLocalMap createInheritedMap(ThreadLocalMap parentMap) {
        return new ThreadLocalMap(parentMap);
    }

看下这个构造方法:

  • 发现主要是用parentMap的所有entry初始化当前的map。
  • 在注释处,调用了inheritThreadLocals重写的childValue方法,而重写后,直接返回的是parentValue,也就是将父线程的inheritThreadLocal里面的entry完整的复制到了子线程中。
        private ThreadLocalMap(ThreadLocalMap parentMap) {
            Entry[] parentTable = parentMap.table;
            int len = parentTable.length;
            setThreshold(len);
            table = new Entry[len];

            for (int j = 0; j < len; j++) {
                Entry e = parentTable[j];
                if (e != null) {
                    @SuppressWarnings("unchecked")
                    ThreadLocal<Object> key = (ThreadLocal<Object>) e.get();
                    if (key != null) {
                        // 调用inheritThreadLocals重写的childValue方法
                        Object value = key.childValue(e.value);
                        Entry c = new Entry(key, value);
                        int h = key.threadLocalHashCode & (len - 1);
                        while (table[h] != null)
                            h = nextIndex(h, len);
                        table[h] = c;
                        size++;
                    }
                }
            }
        }

如何理解这个继承性

通过上面的源码分析,可以发现,InheritableThreadLocal的继承性主要体现在:创建子线程时,会将父线程的inheritThreadLocals里面所有entry拷贝一份给子进程。

那么当子进程被创建出来之后,父进程又修改了inheritThreadLocals里面的值,这个操作是否对子线程可见,通过上面的源码可知,这个操作明显是不可见的,下面有个demo可以证实。

  • sleep操作是为了控制两个线程的执行流程。
/**
 * @author ATFWUS
 * @version 1.0
 * @date 2021/11/9 14:29
 * @description
 */
@Slf4j
public class InheritableThreadLocalTest {

    static ThreadLocal<String> localStr = new ThreadLocal<>();
    static InheritableThreadLocal<String> inheritableLocalStr = new InheritableThreadLocal<>();

    public static void main(String[] args) throws InterruptedException {
        inheritableLocalStr.set("main线程第一次为inheritableLocalStr设置的值");
        new Thread(new Runnable() {
            @Override
            public void run() {
                log.debug("子线程第一次访问inheritableLocalStr : " + inheritableLocalStr.get());
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                log.debug("子线程第二次访问inheritableLocalStr : " + inheritableLocalStr.get());
            }
        }).start();
        Thread.sleep(500);
        inheritableLocalStr.set("main线程第二次为inheritableLocalStr设置的值");
        log.debug("main线程第二次为inheritableLocalStr赋值");
        Thread.sleep(1000);
    }
}

看下输出:

可以发现,子线程创建出来后,对父线程中inheritThreadLocals的修改操作,对子线程不可见。

总结

  • ThreadLocal不可继承,threadLocals是当前线程的成员变量,在子线程中不可见。
  • InheritableThreadLocal可继承,原理是:在新建子线程的时候,将父线程中inheritThreadLocals所有的entry拷贝给了子线程。
  • 子线程创建出来后,对父线程中inheritThreadLocals的修改操作,对子线程不可见。

4.存在的内存泄露问题

要充分理解ThreadLocal中存在的内存泄露问题,需要有以下JVM对内存管理的前置知识(这里篇幅问题就不补充了):

  • 什么是内存泄露?
  • 什么是强引用?
  • 什么是弱引用?
  • 何时GC?
  • 强引用和弱引用GC时的区别?

在分析上述ThreadLocalMap源码的时候,注意到有一个小细节,ThreadLocalMap的Entry继承了WeakReference<ThreadLocal<?>>,也就是说Entry的key是一个对ThreadLocal<?>的弱引用。问题来了,为什么这里要使用弱引用呢?

使用强引用会如何?

现在假设Entry的key是一个对ThreadLocal的强引用,当ThreadLocal对象使用完后,外部的强引用不存在,但是因为当前线程对象中的threadLocals还持有ThreadLocal的强引用,而threadLocals的生命周期是和线程一致的,这个时候,如果没有手动删除,整个Entry就发生了内存泄露。

使用弱引用会如何?

现在假设Entry的key是一个对ThreadLocal的弱引用,当ThreadLocal对象使用完后,外部的强引用不存在,此时ThreadLocal对象只存在Entry中key对它的弱引用,在下次GC的时候,这个ThreadLocal对象就会被回收,导致key为null,此时value的强引用还存在,但是value已经不会被使用了,如果没有手动删除,那么这个Entry中的key就会发生内存泄露。

使用弱引用还有一些好处,那就是,当key为null时, ThreadLocalMap中最多存在一个key为null,并且当调用set(),get(),remove()这些方法的时候,是会清除掉key为null的entry的。

set()、get()、remove() 方法中相关实现

  • 从下可以发现,set方法首先会进入一个循环。
  • 在这个循环中,会遍历整个Entry数组。直到遇到一个空的entry,退出循环。
  • 当遇到已存在的key'时,会直接替换value,然后返回。
  • 当遇到key为空的entry的时候,会直接将当前的entry存在这个过时的entry中,然后返回。

通过这个方法的源码可以看出,key为null的那个entry实际上迟早会被替换成新的entry。

        private void set(ThreadLocal<?> key, Object value) {

            // We don't use a fast path as with get() because it is at
            // least as common to use set() to create new entries as
            // it is to replace existing ones, in which case, a fast
            // path would fail more often than not.

            Entry[] tab = table;
            int len = tab.length;
            int i = key.threadLocalHashCode & (len-1);

            for (Entry e = tab[i];
                 e != null;
                 e = tab[i = nextIndex(i, len)]) {
                ThreadLocal<?> k = e.get();

                if (k == key) {
                    e.value = value;
                    return;
                }
							 // 发现key为空
                if (k == null) {
                    replaceStaleEntry(key, value, i);
                    return;
                }
            }

            tab[i] = new Entry(key, value);
            int sz = ++size;
            if (!cleanSomeSlots(i, sz) && sz >= threshold)
                rehash();
        }

        private static int nextIndex(int i, int len) {
            return ((i + 1 < len) ? i + 1 : 0);
        }

同理,可以看到在get方法中也存在:

        private Entry getEntry(ThreadLocal<?> key) {
            int i = key.threadLocalHashCode & (table.length - 1);
            Entry e = table[i];
            if (e != null && e.get() == key)
                return e;
            else
                return getEntryAfterMiss(key, i, e);
        }
         private Entry getEntryAfterMiss(ThreadLocal<?> key, int i, Entry e) {
            Entry[] tab = table;
            int len = tab.length;

            while (e != null) {
                ThreadLocal<?> k = e.get();
                if (k == key)
                    return e;
                // 替换过时的entry
                if (k == null)
                    expungeStaleEntry(i);
                else
                    i = nextIndex(i, len);
                e = tab[i];
            }
            return null;
        }

remove() 方法中也是一样:

        private void remove(ThreadLocal<?> key) {
            Entry[] tab = table;
            int len = tab.length;
            int i = key.threadLocalHashCode & (len-1);
            for (Entry e = tab[i];
                 e != null;
                 e = tab[i = nextIndex(i, len)]) {
                // 清除过时的key
                if (e.get() == key) {
                    e.clear();
                    expungeStaleEntry(i);
                    return;
                }
            }
        }

总结

  • ThreadLocal如果对ThreadLocalMap的key使用强引用,那么会存在整个entry发生内存泄露的问题,如果不手动清除,那么这个不被使用的entry会一直存在。
  • ThreadLocal如果对ThreadLocalMap的key使用弱引用,那么可能会存在一个entry的value发生内存泄露,但是在调用set(),get(),remove() 方法时,key为null的entry会被清除掉。
  • 发生内存泄露最根本的原因是:threadLocals的生命周期是和线程一致的。
  • 每次使用完ThreadLocal对象后,必须调用它的remove()方法清除数据。

5.ThreadLocal应用

ThreadLocal把数据存放到线程本地,解决了线程安全问题,没有使用锁,直接访问线程本地变量,效率较高(空间换时间。)
同时threadLocals的生命周期是和线程一致的,可以解决很多参数传递问题。

  • Session管理(Mabaties使用ThreadLocal存储session),数据库连接。
  • 如果需要跟踪请求的整个流程,可以使用ThreadLocal来传递参数。

ATFWUS 2021-11-11

以上就是深入浅出解析Java ThreadLocal原理的详细内容,更多关于Java ThreadLocal原理的资料请关注我们其它相关文章!

(0)

相关推荐

  • Java中的ThreadLocal详解

    目录 一.ThreadLocal简介 二.ThreadLocal简单使用 三.ThreadLocal的实现原理 1.set方法源码 2.get方法源码 3.remove方法的实现 四.ThreadLocal不支持继承性 五.InheritableThreadLocal类 六.从ThreadLocalMap看ThreadLocal使用不当的内存泄漏问题 1.基础概念 2.分析ThreadLocalMap内部实现 一.ThreadLocal简介 多线程访问同一个共享变量的时候容易出现并发问题,特别是

  • Java ThreadLocal原理解析以及应用场景分析案例详解

    目录 ThreadLocal的定义 ThreadLocal的应用场景 ThreadLocal的demo TheadLocal的源码解析 ThreadLocal的set方法 ThreadLocal的get方法 ThreadLocalMap的结构 ThreadLocalMap的set方法 ThreadLocalMap的getEntry方法 ThreadLocal的内存泄露 如何避免内存泄露呢 应用实例 实际应用二 总结 ThreadLocal的定义 JDK对ThreadLocal的定义如下: The

  • 详解Java中ThreadLocal类型及简单用法

    目录 1 基本概念 2 简单使用 3 应用场景 4 底层原理 4.1 set(Object) 4.2 get() 4.3 remove() 4.4 ThreadLocalMap 5 内存泄漏隐患和防止策略 5.1 为什么会发生内存泄漏? 5.2 怎样防止内存泄漏? 1 基本概念 ThreadLocal类提供了线程局部变量.这些变量与普通变量的不同之处在于,每个访问一个变量(通过其get或set方法)的线程都有自己的.独立初始化的变量副本.ThreadLocal实例通常是希望将状态与线程关联起来的

  • Java ThreadLocal有什么作用你知道吗

    目录 ThreadLocal有什么作用 测试代码 当前线程只能操作当前ThreadLocal定义的局部变量,其他线程是访问不了. 测试结果 总结 ThreadLocal有什么作用 ThreadLocal 的源码,可以看到源码注释中有很清楚的解释:它是线程的局部变量,这些变量只能在这个线程内被读写,在其他线程内是无法访问的. ThreadLocal 定义的通常是与线程关联的私有静态字段(例如,用户ID或事务ID). ThreadLocal 是线程本地存储,在每个线程中都创建了一个 ThreadLo

  • Java ThreadLocal的详细解释

    目录 一.ThreadLocal简介 二.ThreadLocal简单使用 三.ThreadLocal的实现原理 1.set方法源码 2.get方法源码 3.remove方法的实现 4.如下图所示: 四.ThreadLocal不支持继承性 五.InheritableThreadLocal类 六.从ThreadLocalMap看ThreadLocal使用不当的内存泄漏问题 1.基础概念 2.分析ThreadLocalMap内部实现 总结: 一.ThreadLocal简介 多线程访问同一个共享变量的时

  • Java面试必问之ThreadLocal终极篇分享

    目录 前言 ThreadLocal是什么 ThreadLoalMap hash冲突 内存泄露 如何避免内存泄露 总结 前言 在面试环节中,考察"ThreadLocal"也是面试官的家常便饭,所以对它理解透彻,是非常有必要的. 有些面试官会开门见山的提问: "知道ThreadLocal吗?" "讲讲你对ThreadLocal的理解" 当然了,也有面试官会慢慢引导到这个话题上,比如提问"在多线程环境下,如何防止自己的变量被其它线程篡改&qu

  • Java并发编程之threadLocal

    目录 1.ThreadLocal介绍 2.ThreadLocal使用实例 3.ThreadLocal实现原理 1.ThreadLocal介绍 多个线程访问同一个共享变量时特别容易出现并发问题,特别是多线程需要对共享变量进行写入时.为了保证线程安全,一般使用者在访问共享变量的时候需要进行适当的同步,如图 同步的一般措施是加锁,这就需要使用者对锁有一定的了解,这显然加重了使用者的负担,那么有没有一种方法可以做到,当创建一个变量后,每个线程对其进行访问的时候访问的是自己线程的变量呢?其实ThreadL

  • Java多线程 ThreadLocal原理解析

    目录 1.什么是ThreadLocal变量 2.ThreadLocal实现原理 3.内存泄漏问题 4.使用场景 1)存储用户Session 2)解决线程安全的问题 3)使用ThreadLocal重新设计一个上下文设计模式 4)ThreadLocal注意事项 脏数据 内存泄漏 父子线程共享线程变量 1.什么是ThreadLocal变量 ThreadLoal 变量,线程局部变量,同一个 ThreadLocal 所包含的对象,在不同的 Thread 中有不同的副本. 这里有几点需要注意: 因为每个 T

  • 深入浅出解析Java ThreadLocal原理

    目录 1.了解ThreadLocal 简介 使用 2.源码解析 – 探究实现思路 threadLocals变量与ThreadLocalMap set(T value) 方法 get() 方法 remove() 方法 实现思路总结 3.InheritableThreadLocal与继承性 ThreadLocal的不可继承性 InheritableThreadLocal实现继承性的源码剖析 如何理解这个继承性 总结 4.存在的内存泄露问题 使用强引用会如何? 使用弱引用会如何? set().get(

  • Java HashMap原理及实例解析

    这篇文章主要介绍了Java HashMap原理及实例解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 示例 1 : HashMap的键值对 HashMap储存数据的方式是-- 键值对 package collection; import java.util.HashMap; public class TestCollection { public static void main(String[] args) { HashMap<String

  • 一文助你搞懂参数传递原理解析(java、go、python、c++)

    前言 最近一年多的时间陆续接触了一些对我来说陌生的语言,主要就是 Python 和 Go,期间为了快速实现需求只是依葫芦画瓢的撸代码:并没有深究一些细节与原理. 就拿参数传递一事来说各个语言的实现细节各不相同,但又有类似之处:在许多新手入门时容易搞不清楚,导致犯一些低级错误. Java 基本类型传递 先拿我最熟悉的 Java 来说,我相信应该没人会写这样的代码: @Test public void testBasic() { int a = 10; modifyBasic(a); System.

  • 深度源码解析Java 线程池的实现原理

    java 系统的运行归根到底是程序的运行,程序的运行归根到底是代码的执行,代码的执行归根到底是虚拟机的执行,虚拟机的执行其实就是操作系统的线程在执行,并且会占用一定的系统资源,如CPU.内存.磁盘.网络等等.所以,如何高效的使用这些资源就是程序员在平时写代码时候的一个努力的方向.本文要说的线程池就是一种对 CPU 利用的优化手段. 线程池,百度百科是这么解释的: 线程池是一种多线程处理形式,处理过程中将任务添加到队列,然后在创建线程后自动启动这些任务.线程池线程都是后台线程.每个线程都使用默认的

  • 深入浅出探索Java分布式锁原理

    目录 什么是分布式锁?它能干什么? 分布式锁实现方案 基于数据库的分布式锁实现方案 实现原理 方案分析 基于Redis的分布式锁实现方案 基于sentnx命令的实现原理 方案分析 基于Redisson实现 RedLock 方案分析 基于Zookeeper的分布式锁实现方案 实现原理 方案分析 分布式锁方案到底选哪个? 总结 什么是分布式锁?它能干什么? 相信大家对于Java提供的synchronized关键字以及Lock锁都不陌生,在实际的项目中大家都使用过.如下图所示,在同一个JVM进程中,T

  • Java 深入浅出解析面向对象之抽象类和接口

    目录 抽象类 声明抽象类 声明抽象方法 案例 使用规则 接口 声明接口 案例 接口特性 抽象类和接口的区别 抽象类 java语言,声明类时 abstract class Db{} 说明Db类为抽象类. java语言中,抽象方法是说没有方法的实现(方法体)此方法为抽象方法,只有抽象类和接口中才可以有抽象方法. 声明抽象类 声明抽象类很简单,加个abstract关节字就行. public abstract class AA { } 声明抽象方法 在抽象类中声明一个抽象方法,抽象方法没有方法体,就是说

  • java ThreadLocal使用案例详解

    本文借由并发环境下使用线程不安全的SimpleDateFormat优化案例,帮助大家理解ThreadLocal. 最近整理公司项目,发现不少写的比较糟糕的地方,比如下面这个: public class DateUtil { private final static SimpleDateFormat sdfyhm = new SimpleDateFormat( "yyyyMMdd"); public synchronized static Date parseymdhms(String

  • ThreadLocal原理介绍及应用场景

    本次给大家介绍重要的工具ThreadLocal.讲解内容如下,同时介绍什么场景下发生内存泄漏,如何复现内存泄漏,如何正确使用它来避免内存泄漏. ThreadLocal是什么?有哪些用途? ThreadLocal如何使用 ThreadLocal原理 ThreadLocal使用有哪些坑及注意事项 1. ThreadLocal是什么?有哪些用途? 首先介绍Thread类中属性threadLocals: /* ThreadLocal values pertaining to this thread. T

随机推荐