java多线程提高笔记

java多线程提高笔记

读完java并发编程之美和java高并发编程详解后对多线程有了更深的理解故写一篇笔记来记录一下 本笔记不会太多涉及多线程基础语法运用的讲解 更注重其原理

1.线程的创建

​ 在java中只有一个Thread类可以代表线程,使用Runnable接口创建线程的说法并不太准确 Runnable只是提供了一个线程运行执行单元,这里使用的是模板方法设计模式

模板方法(Template Method)模式的定义如下:定义一个操作中的算法骨架,而将算法的一些步骤延迟到子类中,使得子类可以不改变该算法结构的情况下重定义该算法的某些特定步骤。它是一种类行为型模式。

我们先来捋一下Thread和Runnable的关系 Thread实现了 Runnable的 run() 方法 并且在Thread的构造方法中又可以传递一个Runnable类型的参数,我们待会说这样做的好处。现在我们思考Thread 构造中同时传递了一个Runnable的参数 和重写了run()方法会怎么样呢?会报错吗?答案先说 是不会的

new Thread(()->{
    System.out.println("我是Runnable");
}) {

    @Override
    public void run() {
        System.out.println("我是Thread");
    }
}.start();

执行如上代码会打印 :我是Thread 这是为什么呢? 我们进源码一看究竟

/* What will be run. */
private Runnable target;

在Thread的成员变量中有这样一个Runnable的属性 target 他就代表这我们的执行单元 在几个Thread的构造函数

在这里插入图片描述

我们发现都是通过init()初始化的 那我们在看一下init()源码

private void init(ThreadGroup g, Runnable target, String name,
                  long stackSize, AccessControlContext acc,
                  boolean inheritThreadLocals) {
 	/*其他操作*/
    this.target = target;
	/*其他操作*/
}

我选出了和我们要讲的相关的部分 就是这一行代码 有同学可能就要问了 那这个target有可能为null 啊 对滴 可以为null 我们再来看看Thread源码中实现Runnable中run()的源码

@Override  //Thread源码中的run()
public void run() {
    if (target != null) {
        target.run();
    }
}

于是我们就明白了 开始那个问题为什么会输出 我是Thread 因为 源码中的run 压根不会执行 直接被子类重写了 虽然结局是这样简单 但是通过上面的分析我们了解了Thread和Runnable的关系和我刚学线程时的疑问 既重写又传入会怎么样。

ThreadLocal

这里我们要解决一个疑问 ThreadLocal是怎么做到线程间隔离的

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BSlEnkxw-1653642881940)(E:\A_java代码\java并发编程\笔记\照片\1653640380133.png)]

以上是Thread的uml图

Thread 类中有一个 threadLocals 和一个 inheritableThreadLocals ,

/* ThreadLocal values pertaining to this thread. This map is maintained
 * by the ThreadLocal class. */
ThreadLocal.ThreadLocalMap threadLocals = null;

/*
 * InheritableThreadLocal values pertaining to this thread. This map is
 * maintained by the InheritableThreadLocal class.
 */
ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;

它们 都是 ThreadLocalMap 类型 的变量 , 而 ThreadLocalMap 是一个定制化的 Hashmap用来 。他是ThreadLocal类的静态内部类,其实每个线程的本地变量不是存放在 ThreadLocal 实例 里面,
而是存放在调用线程的 threadLocals 变量里面 。 也就是说 , ThreadLocal 类型的本地变量存
放在具体的线程内存空间中 。 ThreadLocal 就是一个工具壳,它通过 set 方法把 value 值放
入调用线程的 threadLocals 里面并存放起来 , 当调用 线程调用它的 get 方法时,再从当 前
线程的 threadLocals 变量里面将其拿出来使用 。Thread 里面 的 threadLocals 为何被设计为 map 结构?很明显是因为每个线程可以关联多个 ThreadLocal 变量。

当我们使用 ThreadLocal中set的时候

public void set(T value) {
    	//获取当前调用该方法的线程
        Thread t = Thread.currentThread();
    	//去当前线程去找 看看threadLocals是否被创建
        ThreadLocalMap map = getMap(t);
        if (map != null)
            //更新值 注意这里的 this指的是 该ThreadLocal 而不是线程
            map.set(this, value);
        else
            //创建出来
            createMap(t, value);
    }
}
ThreadLocalMap getMap(Thread t) {
        return t.threadLocals;
 }

void createMap(Thread t, T firstValue) {
        t.threadLocals = new ThreadLocalMap(this, firstValue);
}

在来看一下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;
        }
    }
    //当该线程中ThreadLocal没有初始化时对其初始化 并返回null 可见调用get也会使其初始化
    return setInitialValue();
}
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;
}

最后我们在来看一下 remove

public void remove() {
    ThreadLocalMap m = getMap(Thread.currentThread());
    if (m != null)
        m.remove(this);
}
		/**
         	* ThreadLoaclMap类中的 Remove the entry for key.
         */
        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)]) {
                if (e.get() == key) {
                    e.clear();
                    expungeStaleEntry(i);
                    return;
                }
            }
        }

如果调用线程一直不终止, 那么这个本地
变量会一直存放在调用线程的 threadLocals 变量里面 ,所以当不需要使用本地变量时可以
通过调用 ThreadLocal 变量的 remove 方法 ,从当前线程的 threadLocals 里面删除该本地变量 。但是不能直接使用将ThreadLocal赋值为null 这样虽然ThreadLocal被清空了 但是因为线程还在执行 其对 ThreadLocalMap的引用就还存在 即从根节点到该对象还是可达的这样gc就处理不了该对象

要使用本地变量时可以
通过调用 ThreadLocal 变量的 remove 方法 ,从当前线程的 threadLocals 里面删除该本地变量 。但是不能直接使用将ThreadLocal赋值为null 这样虽然ThreadLocal被清空了 但是因为线程还在执行 其对 ThreadLocalMap的引用就还存在 即从根节点到该对象还是可达的这样gc就处理不了该对象

今天就写到这 明天继续写

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值