并发编程由浅及深

并发编程重要吗?当然重要,因为并发在我们的项目中真实存在,如果你不能充分了解它那么很可能造成严重的生产事故。最近隔壁项目组出了一个问题,每次请求接口之后都发现线程固定增加了5个,而且线程数一直增加没有减少,他们怀疑是中间件的问题,但实际上是因为他们的代码中线程池使用不当造成。所以了解并发是很有必要的。

并发编程由浅及深

1.基础概念

  1. 并行:两台饮水机,两个人去接水一人使用一台
  2. 并发:一台饮水机,两个人去接水,需要等待一个人接完才行
  3. 异步:不需要等待结果返回
  4. 同步:需要等待结果返回
  5. 线程:是运行在进程内:比如音乐播放器打开之后,在随机播放着音乐 的同时,还可以继续搜索我们想听的音乐。可以理解有两个线程分别处理着音乐播放和音乐搜索
  6. 进程:是一个具体的应用示例:比如打开音乐播放器就是运行了一个进程
  7. 线程上下文切换:cpu调度线程时切换线程同时需要保存线程相关数据,cpu和内存之间

2.Thread

2.1 线程的创建

线程的创建到底有几种?这个答案其实不是固定。要看回答的方向

There are two ways to create a new thread of execution
eg1:new Thread().start()
eg2:R implements Runnable

上面这句话是Thread类上面的注释,也就是官方说是两种,但是实际使用过程中根据根据这两种又有衍生和变化。概括有一下几种

1. new Thread().start();
2. R implements Runnable
	new Thread(R).start();
3. C implements Callable
	new Thread(new FutureTask<>(C)).start();
4. R implements Runnable
	new ThreadPoolExecutor().execute(R);

2.2 线程的中断方法

1. Thread.interrupt() 给线程加一个中断标记
2. Thread.interrupted() 判断线程是否中断,会清除标记
3. Thread.isInterrupted() 判断线程是否中断,不会清除标记

2.3 线程的等待唤醒机制

1. synchronizedwait(),notify()
2. locksupport 的 park(),unpark()
3. condition wait() signal()

3.ThreadLocal(源码)

下面这段代码大概就是我们使用ThreadLocal的方式,接下来将根据这段代码中的方法对ThreadLocal原理进行剖析。

    // 1.new
    ThreadLocal<Map<String,String>> myThreadLocal = new ThreadLocal<>();

    // 2.存值
    Map<String,String> map = new HashMap<>();
    map.put("org","0001");
    myThreadLocal.set(map);

    // 3.获取值
    map = myThreadLocal.get();
    String org = map.get("org");
    System.out.println(org);

    // 4.清空
    myThreadLocal.remove();

3.1 new ThreadLocal<>()

    public ThreadLocal() {
   
    }

3.2 ThreadLocal#set()

    public void set(T value) {
   
    	// 获取当前线程
        Thread t = Thread.currentThread();
        // 获取thread对应的map
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
        	// 创建map
            createMap(t, value);
    }

3.2.1ThreadLocal#createMap

    void createMap(Thread t, T firstValue) {
   
        // 给thread的threadLocals属性赋值
        t.threadLocals = new ThreadLocalMap(this, firstValue);
    }
3.2.1.1 new ThreadLocalMap()
ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
   
			// 创建初始长度为16的数组
            table = new Entry[INITIAL_CAPACITY];
            // firstKey传的是this 即myThreadLocal对象本身
            int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
            // 数组中存入值:key=myThreadLocal,firstValue=map
            table[i] = new Entry(firstKey, firstValue);
            size = 1;
            setThreshold(INITIAL_CAPACITY);
        }
3.2.1.2 ThreadLocal#getMap
    ThreadLocalMap getMap(Thread t) {
   
    	// 获取thread中的ThreadLocalMap属性
        return t.threadLocals;
    }

执行完myThreadLocal.set(map),最终结构如下图:
在这里插入图片描述

3.3 myThreadLocal.get() 方法

    public T get() {
   
        Thread t = Thread.currentThread();
        // 获取thread的属性ThreadLocalMap
        ThreadLocalMap map = getMap(t);
        if (map != null) {
   
        	// 获取key为myThreadLocal的Entry 
            ThreadLocalMap.Entry e = map.getEntry(this);
            if (e != null) {
   
                @SuppressWarnings("unchecked")
                T result = (T)e.value;
                // 返回value即为set存储的map
                return result;
            }
        }
        // 如果当前线程未set值,默认返回null
        return setInitialValue();
    }

3.3.1 map.getEntry(this) 方法

       private Entry getEntry(ThreadLocal<?> key) {
   
       	   // 和set时一样,使用myThreadLocal对象获取数组的下标
           int i = key.threadLocalHashCode & (table.length - 1);
           Entry e = table[i];
           // 当前下标位置数组值不为null,且key值相等。即为get返回值
           if (e != null && e.get() == key)
               return e;
           else
               return getEntryAfterMiss(key, i, e);
       }

3.4 myThreadLocal.remove() 方法

     public void remove() {
   
     	// 获取当前程的属性ThreadLocalMap
         ThreadLocalMap m = getMap(Thread.currentThread());
         if (m != null)
         	// 移除当前myThreadLocal对象所在下标的数组的值
             m.remove(this);
     }

3.4.1 m.remove(this)

        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;
                }
            }
        }

4.ReentrantLock(非公平锁实现源码)

下面的源码实现主要列的是非公平锁,公平锁代码和非公平锁差不多所以鼓励大家自己看看。看源码前请大家想象一个场景,现在有两个线程,第一个线程获取锁成功了在执行业务逻辑时,此时第二个线程过来加锁。下面的源码分析的就是上面这样一个场景。

常规使用如下:

 // 1.创建 ReentrantLock 对象
        ReentrantLock lock = new ReentrantLock(); 
        // 2.获取锁
        lock.lock(); 
        try {
   
           // 业务逻辑
        } finally {
   
            // 3.释放锁
            lock.unlock(); 
        }

4.1 new ReentrantLock()

    public ReentrantLock() {
   
    	// 给ReentrantLock属性赋值;NonfairSync实际上就是抽象的队列同步器
        sync = new NonfairSync();
    }

NonfairSync类的继承关系
在这里插入图片描述

4.2 lock.lock()

    public void lock() {
   
        sync.lock();
    }

4.2.1 sync.lock()

   final void lock() {
   
   		// 使用CAS的方式尝试将同步等待队列中的state由0改为1
       if (compareAndSetState(0, 1))
       		// 修改成功代表获取锁成功,将独占线程赋值为当前线程
           setExclusiveOwnerThread(Thread.currentThread());
       else
       		// 1.获取锁
           acquire(1);
   }
4.2.1.1 acquire(1)
    public final void acquire(int arg) {
   
    	// tryAcquire尝试获取一次锁,如果失败了addWaiter进入队列,然后再次获取锁acquireQueued
        if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            selfInterrupt();
    }
4.2.1.1.1 tryAcquire
	protected final boolean tryAcquire(int acquires) {
   
			// 非公平获取锁
            return nonfairTryAcquire(acquires);
        }
4.2.1.1.1.1 nonfairTryAcquire()方法
	// acquires从2.1方法中传过来 固定为1
    final boolean nonfairTryAcquire(int acquires) {
   
        final Thread current = Thread.currentThread();
        // 获取同步等待队列的state属性,默认值为0,如果不为0代表有线程获取锁成功
        int c = getState();
        if (c == 0) {
   
        	// 如果当前没有线程获取到锁,就直接cas加锁
            if (compareAndSetState(0, acquires)) {
   
                setExclusiveOwnerThread(current);
                return true;
            }
        }
        // c!=0,当前已有线程获取了锁。判断是不是同一个线程
        else if (current == getExclusiveOwnerThread()) {
   
        	// 可重入锁实现 通过state值自增判断重入几次
            int nextc = c + acquires;
            if (nextc < 0) // overflow
                throw new Error("Maximum lock count exceeded");
            setState(nextc);
            return true;
        }
        return false;
    }
4.2.1.1.2 addWaiter
	// node从4.2.1.1传递的是null
    private Node addWaiter(Node mode) {
   
    	// 构建node节点
        Node node = new Node(Thread.currentThread(), mode);
        // Try the fast path of enq; backup to full enq on failure
        // 1.将尾节点赋值给变量pred
        Node pred = tail;
        // 2.判断尾节点是否为空,为空则代表链表为空未进行初始化(双向指针构造的)
        if (pred != null) {
   
        	// 2.1 尾节点不为空,则将当前node节点添加到链表尾部
            node.prev = pred;
            if (compareAndSetTail(pred, node)) {
   
                pred.next =
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值