java多线程部分

java阻塞队列

1、arrayBlockingQueue数组结构、有界阻塞队列

公平非公平

先进先出对元素排序, 默认不保证访问者公平的访问队列

    公平访问队列:阻塞all生产者/消费者线程,当队列可用,按阻塞顺序访问队列,先阻塞的生产者线程先往队列插入,先阻塞的消费者线程先从队列获取

    公平阻塞队列ArrayBlockingQueue fairQueue = new ArrayBlockingQueue(1000,true)

    

2、linkedBlockingQueue链表结构、有界阻塞队列

两个独立锁提高并发

先进先出fifo对元素排序,生产者端和消费者端分别采用独立的锁控制数据同步;默认一个类似无限大小的容量

3、priorityBlockingQueue支持优先级排序、无界阻塞队列

compareTo排序实现优先

默认是自然顺序,可自定义实现compareTo来指定元素进行排序规则,或初始化时指定构造参数Comparator对元素进行排序,不能保证同优先级元素的顺序,像redis的zset同分数一样

4、delayQueue优先级队列实现的无界阻塞队列

缓存失效,定时任务,支持延时获取元素

使用priorityQueue实现,队列中元素要实现delayed接口,创建元素可指定多久才能从队列获取当前元素

5、synchronousQueue不存储元素的阻塞队列

不存储数据,传递数据,吞吐量高于前两者

每个put要等待一个take操作否则不能添加元素,把生产者线程处理的数据直接传递给消费者线程

6、linkedTransferQueue链表结构组成的无界阻塞队列

transferQueue队列,多了tryTransfer和transfer方法

    transfer如当前消费者正在等待接收元素(消费者使用take或时间限制poll),该方法把生产者传入的消息立刻transfer(船速)给消费者,如果没有消费者在等待接收则将元素放在队列tail尾节点并等该元素被消费才返回

   tryTransfer试探生产者传入的元素是否能直接传给消费者,如无消费者等待接收则返false

   tryTransfer(E e,long timeout,TimeUnit unit)试图把生产者传入的元素直接传给消费者,如没有消费者消费该元素则等待指定的时间再返回,如果超时返回false,未超时消费了返回true

7、linkedBlockingDeque链表结构组成的双向阻塞队列

两端插入和移除元素,addFirst addLast offerFirst offerLast peekFirst peekLast,first结尾的表示插入、获取peek或移除双端队列的第一个元素,last则是插入获取移除最后一个元素

countDownLatch线程计数器

 位于java.util.concurrent包下,实现类似计数器的功能

CyclicBarrier:一组线程等待至某个状态后同时执行

   await挂起当前线程,直到all线程到达barrier状态再同时执行后继任务

   await(long timeout,TimeUnit unit)等待至指定时间还让线程没有到达barrier状态就直接让到达barrier的线程执行后继任务

semaphore信号量

控制同时访问的线程个数,acquire获取许可无许可则等待,release释放许可

   acquire(int permits)获取许可,acquire,release释放许可,release(int permites)释放n个,这四个方法都会被阻塞

   boolean tryAcquire尝试获取若成功返true,失败返false

   boolean tryAcquire(long timeout,TimeUnit unit)尝试指定时间获取许可,成功返true失败false

   boolean tryAcquire(int permits)、boolean tryAcquire(int permits,long timeout,TimeUnit unit)

   availablePermits得到可用的许可数目

countDownLatch和CyclicBarrier实现线程间等待,侧重点不同,countDownLatch一般某个线程等待若干其他线程执行完任务之后才执行;cyclicBarrier用于一组线程互相等待至某个状态,然后同时执行,cyclicBarrier可重用

volatile变量可见性、禁止重排序,不会被缓存在寄存器或者其他处理器不可见的地方

    所有线程可见,当一个线程修改了变量的值,新值对于其他线程可立即获取

    比synchronize更轻量级的同步锁,访问volatile变量时不会加锁

    非volatile变量读写,每个线程先从内内存拷贝变量到cpu缓存,如果有多个cpu每个线程可能在不同cpu上被处理,每个线程拷贝到不同cpu cache中,而声明变量的volatile,jvm每次读取从内存中读、跳过cpu cache

threadLocal

threadlocal线程本地储存,提供线程内部的局部变量,在线程生命周期内起作用,减少同一个线程内多个函数或组件间公共变量传递的复杂度

    threadlocalMap每个线程都有一个自己的threadLocalMap类对象,可将线程自己的对象保持到其中

   将公用的threadLock静态实例作为key,不同对象的引用保存到不同线程的threadLocalMap中,然后在线程执行的各处通过这个静态threadLocal实例的get取得自己线程保存的那个对象

get获取threadLocal在当前线程保存的变量副本,set设置当前线程中变量的副本

remove移除当前线程中变量的副本,initialValue延迟加载,在使用时进行重写

https://www.jianshu.com/p/98b68c97df9b

https://www.jianshu.com/p/6fc3bba12f38

https://www.jianshu.com/p/3c5d7f09dfbd

上面三篇博客大概的介绍很可以,我把我的重点展示一下

//ThreadLocalMap构造方法
ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
        //内部成员数组,INITIAL_CAPACITY值为16的常量
        table = new Entry[INITIAL_CAPACITY];
        //位运算,结果与取模相同,计算出需要存放的位置
        //threadLocalHashCode比较有趣
        int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
        table[i] = new Entry(firstKey, firstValue);
        size = 1;
        setThreshold(INITIAL_CAPACITY);
}

  //ThreadLocalMap中set方法。
  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);

            //遍历tab如果已经存在则更新值
            for (Entry e = tab[i];
                 e != null;
                 e = tab[i = nextIndex(i, len)]) {
                ThreadLocal<?> k = e.get();

                if (k == key) {
                    e.value = value;
                    return;
                }

                if (k == null) {
                    replaceStaleEntry(key, value, i);
                    return;
                }
            }
            
            //如果上面没有遍历成功则创建新值
            tab[i] = new Entry(key, value);
            int sz = ++size;
            //满足条件数组扩容x2
            if (!cleanSomeSlots(i, sz) && sz >= threshold)
                rehash();
        }


//ThreadLocal中get方法
public T get() {
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if (map != null) {
        ThreadLocalMap.Entry e = map.getEntry(this);//this不是t
        if (e != null) {
            @SuppressWarnings("unchecked")
            T result = (T)e.value;
            return result;
        }
    }
    return setInitialValue();
}
    
//ThreadLocalMap中getEntry方法
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);
   }

consurrentHashMap并发

分段锁,减小锁粒度:缩小锁定对象的范围,提高并发

   由segment数组结构和hashentry数组结构组成,segment是可重入锁,hashentry储存键值对;segment类似hashmap数组链表结构,一个segment含一个hashentry数组,每个hashentry是一个链表结构的元素

640?wx_fmt=jpeg

1.8改进:

640?wx_fmt=png

https://blog.csdn.net/weixin_44460333/article/details/86770169

https://blog.csdn.net/bill_xiang_/article/details/81122044

   添加元素首先据hashcode得到该表应放在哪个段中,该段加锁完成put

抢占式调度

   每条线程执行的时间,线程切换由系统控制

   jvm线程调度:按照优先级分配CPU时间片运行

协同式调度

   某一线程执行完成后主动通知系统切换到另一线程上执行,执行时间由线程本身控制,线程切换可预知,不存在多线程同步问题;如果一个线程编写有问题则堵塞可能导致系统崩溃

线程让出cpu

  1. 当前运行线程主动放弃cpu、jvm暂时放弃cpu
  2. 当前运行线程因为某些原因进入阻塞状态:阻塞在I/O上
  3. 当前运行线程结束

进程调度算法

优先调度

1、FCFS先来先服务:算法简单,基本上公平

   作业调度:每次调度从后备作业队列选择一个或多个最先进入该队列的作业,调入内存分配资源创建进程,放入就绪队列

   进程调度采用fcfs,每次调度从就绪队列选择一个最先进入的进程,分配处理机,运行到完成或因某事件阻塞放弃处理机

2、短作业优先调度算法

   短作业优先SJF从后备队列选*个估计运行时间最短的作业,调入内存运行

发布了437 篇原创文章 · 获赞 162 · 访问量 46万+

猜你喜欢

转载自blog.csdn.net/ma15732625261/article/details/105000754