• 0903(046天 线程集合总结01)


    0903(046天 线程集合01)

    每日一狗(田园犬西瓜瓜

    线程集合01

    1. 迭代器

    **意图:**提供一种方法顺序访问一个聚合对象中各个元素, 而又无须暴露该对象的内部表示。

    **主要解决:**不同的方式来遍历整个整合对象。

    **何时使用:**遍历一个聚合对象。

    实现

    package com.yang1;
    
    import java.util.ArrayList;
    import java.util.Arrays;
    
    public class Test03 {
    	public static void main(String[] args) {
    		MyContaniner mc = new MyContaniner();
    		Iterator it = mc.getIterator();
    		while (it.hasNext()) {
    			System.out.println(it.next());
    		}
    	}
    }
    /*
    1
    2
    3
    5
    6
    8
    
    */
    // 定义迭代器接口
    interface Iterator {
    	public boolean hasNext();
    
    	public Object next();
    }
    
    // 定义集合容器接口
    interface Contaniner {
    	public Iterator getIterator();
    }
    
    // 构建容器实现类
    class MyContaniner implements Contaniner {
    	private ArrayList<Integer> data;
    
    	public MyContaniner() {
    		data = new ArrayList<>(Arrays.asList(1, 2, 3, 5, 6, 8));
    	}
    
    	@Override
    	public Iterator getIterator() {
    		return new Iter();
    	}
    
    	private class Iter implements Iterator {
    		private int index;
    
    		@Override
    		public boolean hasNext() {
    			return index < data.size();
    		}
    
    		@Override
    		public Object next() {
    			return data.get(index++);
    		}
    
    	}
    
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65

    2. 并发编程和集合框架 总结 问:

    2.1 并发编程

    并发编程的三种性质
    • 可见性
    • 原子性
    • 有序性
    并发编程的三个问题
    • 线程安全安全
    • 活跃性:死锁、活锁、饥饿
    • 性能
      • 无锁
      • 降低锁颗粒度
    volatile

    Java中会第一时间将自己的数据同步到内存中去,而且还会防止Java编译器的重排优化,保证有序性。

    • 可见性

    • 有序性

    • 无法保证原子性

    预防死锁

    死锁的必要条件有四个,只需要破坏其中一项就可以解决死锁

    • 互斥:synchronized就互斥呀,没法改,
    • 占有且等待:同时申请多把锁
    • 不可抢占:synchronized没辙,Lock可以
    • 循环等待:为资源设定id,你得有这个才能申请那个,按照顺序申请

    同步方法锁使用对象头实现,被synchronized修饰的方法会在方法区有一个标识ACC_SYCHRONIZED来进行标识;同步代码块使用了两个机器码来进行进入monitorenter和退出:monitorexit(有俩退出,一个异常一个正常)。

    2.2 锁的分类

    常见的锁
    • 阻塞:需要进行状态转换

    • 自旋:轻量级锁,忙等锁

    • 可重入锁:自己拿着锁,自己再去申请锁直接拿到。(一定程度避免死锁)

    • 读写锁:主要提高数据读写的并发性。

      • 读读不互斥
      • 读被申请走了,写锁无法被其他线程申请;写锁被申请走了,读锁无法被其他线程申请。
      • 有读拿写锁不行;有写拿读锁可以
    • 独享锁、共享锁 通过AQS来进行实现

      • 独享锁。就是排它锁
      • 共享锁。读锁就是共享锁
    • 乐观、悲观:是面对线程安全问题的一种态度

      • 乐观:轻易不出现安全问题,出现了我自己补偿(CAS),适合大量的读操作

        Java搞了很多的原子类来应对CAS操作

      • 悲观:肯定有问题,先上互斥锁了再说,适合大量的写操作

    • 无锁、偏向锁、轻量级锁、重量级锁(会升级,但是不会退化)

    • 自旋锁:忙等状态。

    • 自适应自旋锁:忙等次数过多会被阻塞。由前一个线程阻塞的次数和锁的拥有者的状态进行动态

    锁消除:1.6+引入的逃逸分析

    就是我拿着锁但是就根本没去操作过公共数据

    操作系统会影响点东西
    • 线程加锁和释放锁,由于操作系统的原因,这中间需要在用户态和核心态之间来回切换,消耗巨大
    • 线程优先级,尽可能拉大点,不同系统拥有的优先级等级不一定可以一一对应。
    公平锁与非公平锁

    公平:会比非公平锁多判定一个阻塞队列中是否有被阻塞的队列,队列有线程阻塞这就会把自己加到队列中

    非公平锁:看锁没人用,没人就加锁,有才会去阻塞。

    2.3 并发集合

    两种解决并发集合安全方法

    加锁:给这几个桶加一把锁,另几个桶加一把锁,不同桶之间可以并发,桶内互斥。可以降低锁的颗粒度。

    List<Integet> list = 
        Collections.synchronizedList(new ArrayList());
    
    • 1
    • 2

    拷贝后写入:搞一份备份,改的时候改这个备份,改完了改一个引用就行。

    List<Integer> list = 
        new CopyOnWriteArrayList<Integer>();
    
    • 1
    • 2
    ConcurrentHashMap

    一开始的锁数组无法扩容,然后这个锁数组中存储的那个引用数组,这个数组才是可以扩容的。

    1.7+:数组+Segment分段锁+单项链表。这种颗粒度太大了。

    在这里插入图片描述

    1.8+:数组+链表+红黑树,不分段了,直接给桶加锁,频繁加锁解锁影响性能,这里使用CAS保证数据的安全性。 Node:保存key,value及key的hash值的数据结构。其中value和next都用volatile修饰,保证并发的可见性。

    JDK8中放弃了分段锁。

    键值都不允许为null

    扰动:(h ^ (h >>> 16)) & 0x7fffffff,HashMap中只有(h ^ (h >>> 16))

    在操作节点的时候会使用头节点为锁对象进入同步代码块。

    2.4 编程模型CAS

    为了以不加锁的方式实现加锁的效果。

    CAS,是Compare and Swap(对比和交换)的简称,在这个机制中有三个核心的参数:

    • 主内存中存放的共享变量:V
    • 工作内存中共享变量的备份值,也叫预期值:A
    • 需要将共享变量更新到的最新值:B

    工作流程就是,先把主内存中的V拷贝到工作内存中的A中,然后再算出自己想要更新的最新值B,在写回到主内存中时,使用A和V进行对比,如果不同那就说明在这期间值被别的线程修改了,就不能在覆盖写回了。

    但是在这期间的对比写回不也不是原子的嘛,但其实这个对比与写回并不是使用Java程序保证的,而是从指令层面保证的CAS这一步就是原子的。

    优点

    • 保证变量操作的原子性
    • 在并发量不是很多的情况下,在保证线程安全的前提CAS要比使用锁机制效率更高
    • 在线程对共享资源占用时间较短的情况下,使用CAS效率也会更高

    缺点

    • ABA问题:三个线程,其中两个线程改来改去没改,第三个线程无法判定到底改没改
    • 在并发量比较高的时候,使用CAS能成功修改的可能性比较低,那你修改失败了就不改了嘛,不能够呀,该干的事还是得干呀,不一样那我就把值拿出来再算一遍被,那你修改的次数=1/p。显然p越小越修改次数越多,这中间就浪费了好多CPU资源

    扩展小芝士

    • 多线程常用于频繁进行用户交互的时候;计算密集型不适合用多线程
    • CAS的判定写会的原子性从机器指令上进行保证
  • 相关阅读:
    深入浅出理解CSS中的3D变换:踏上立体视觉之旅
    汇编攻城记-LDR/STR/LDM/STM数据传输
    mkfifo函数 及 解决Linux下 “mkfifo: no such file or directory”
    如何轻松学习Java
    Python学习笔记:Jupyter Notebook快速入门案例:学习时间与成绩的关系
    API接口名称(item_get - 获得dangdang商品详情)[item_search,item_get,item_search_shop等]
    【C++】类和对象(上)
    CANoe-vTESTstudio之State Diagram编辑器(元素介绍)
    AWS、微软云、谷歌云背后的格局焦虑
    欧莱雅SAP系统成功合并
  • 原文地址:https://blog.csdn.net/weixin_48015656/article/details/126681066