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

	}

}


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());

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

List<Integer> list = 
    new CopyOnWriteArrayList<Integer>();
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的判定写会的原子性从机器指令上进行保证
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值