Java-多线程-增强篇-锁强化第3篇

news/2024/4/26 17:20:10/文章来源:https://blog.csdn.net/qq_42897427/article/details/129135307

Java集合框架中的锁

在这里插入图片描述

今天我们继续来学习锁

字符串操作中的锁

  • String是线程安全的,因为使用final修饰
  • Stringbuilder 是线程不安全的,其方法没有使用synchronized修饰
  • StringBuffer 是线程安全的,其方法使用synchronized修饰

List集合中的锁

  • 不安全的
    • ArrayList
    • LinkedList
    • HashSet
    • TreeSet
    • HashMap
    • TreeMap
  • 安全的
    • Hashtable
    • Vector
    • Properties
    • ConcurrentHashMap
  • 但是Java也提供了很多线程安全的方式
    • 比如在java.util.concurrent 包下提供的类都是线程安全的类
    • 也可以使用Collections类提供的方法

Collections类源码解析

// 先从一段代码开始
public class MyList {public static void main(String[] args) {List<String> list = new ArrayList<>();LinkedList<String> strings = new LinkedList<>();List<String> list1 = Collections.synchronizedList(list);}
}// 点进去 Collections.synchronizedList(list); 方法 会跳到这个方法public static <T> List<T> synchronizedList(List<T> list) {return (list instanceof RandomAccess ?new SynchronizedRandomAccessList<>(list) :new SynchronizedList<>(list));
}// 因为我们使用的是 LinkedList 所以进入 SynchronizedList构造器
SynchronizedList(List<E> list) {super(list);this.list = list;
}// 此次又调用了父类构造器
// 然后进入了 Collections的SynchronizedCollection静态内部类
// 然后我们惊奇的发现,其中的所有的方法都是使用 synchronized 修饰的public class Collections {// Suppresses default constructor, ensuring non-instantiability.private Collections() {}static class SynchronizedCollection<E> implements Collection<E>, Serializable {private static final long serialVersionUID = 3053995032091335093L;final Collection<E> c;  // Backing Collectionfinal Object mutex;     // Object on which to synchronizeSynchronizedCollection(Collection<E> c) {this.c = Objects.requireNonNull(c);mutex = this;}SynchronizedCollection(Collection<E> c, Object mutex) {this.c = Objects.requireNonNull(c);this.mutex = Objects.requireNonNull(mutex);}public int size() {synchronized (mutex) {return c.size();}}public boolean isEmpty() {synchronized (mutex) {return c.isEmpty();}}public boolean contains(Object o) {synchronized (mutex) {return c.contains(o);}}public Object[] toArray() {synchronized (mutex) {return c.toArray();}}public <T> T[] toArray(T[] a) {synchronized (mutex) {return c.toArray(a);}}public Iterator<E> iterator() {return c.iterator(); // Must be manually synched by user!}public boolean add(E e) {synchronized (mutex) {return c.add(e);}}public boolean remove(Object o) {synchronized (mutex) {return c.remove(o);}}public boolean containsAll(Collection<?> coll) {synchronized (mutex) {return c.containsAll(coll);}}public boolean addAll(Collection<? extends E> coll) {synchronized (mutex) {return c.addAll(coll);}}public boolean removeAll(Collection<?> coll) {synchronized (mutex) {return c.removeAll(coll);}}public boolean retainAll(Collection<?> coll) {synchronized (mutex) {return c.retainAll(coll);}}public void clear() {synchronized (mutex) {c.clear();}}public String toString() {synchronized (mutex) {return c.toString();}}// Override default methods in Collection@Overridepublic void forEach(Consumer<? super E> consumer) {synchronized (mutex) {c.forEach(consumer);}}@Overridepublic boolean removeIf(Predicate<? super E> filter) {synchronized (mutex) {return c.removeIf(filter);}}@Overridepublic Spliterator<E> spliterator() {return c.spliterator(); // Must be manually synched by user!}@Overridepublic Stream<E> stream() {return c.stream(); // Must be manually synched by user!}@Overridepublic Stream<E> parallelStream() {return c.parallelStream(); // Must be manually synched by user!}private void writeObject(ObjectOutputStream s) throws IOException {synchronized (mutex) {s.defaultWriteObject();}}}
}

ConcurrentHashMap

  • 在JDK1.7之前,HashMap的底层是数组+链表。同样的,ConcurrentHashMap的底层树结构是数组+链表,但是和HashMap不一样的是,ConcurrentHashMap的中存放数据是一段一段的。即由很多个Segment(段)组成的,每个Segment中都有着类似于数组+链表的结构
  • 关于Segment
    • ConcurrentHashMap由三个参数
      • initalCapacity:初始化总容量,默认值为16
      • loadFactor:加载因子,默认0.75
      • concurrentLevel:并发级别,默认16
    • 其中的并发级别控制了Segment的个数。在y一个ConcurrentHashMap创建后Segment的个数是不能变的,扩容过程是改变每个Segment的大小
  • 关于分段锁
    • Segment继承了重入锁ReentrantLock,有了锁的的功能。每个锁控制的是一段,当每个Segment越来越大的时候,锁的粒度就越来越大了
      • 分段锁的优势是保证造操作不同段map的时候进行锁的竞争和等待。这相当于直接对整个map同步synchronized只是有优势的
      • 缺点在于分成很多段的时候会浪费很多的内存空间(不连续,碎片化),操作map的时候竞争一个分段锁概率狠小的时候,分段锁反而会造成更新等操作的长时间等待,分段锁的性能会下降
  • JDK1.8的map实现
    • JDK中的HashMap和ConcurrentHashMap。底层数据结构为数组+链表+红黑树。数组可以扩容,链表可以转化为红黑树(本篇文章不对红黑树做讲解,之前已经分析过, 请看Java 集合框架 - HashMap 底层 红黑树深度解读.md
  • 新版的ConcurrentHashMap为什么不使用ReentrantLock而使用synchronized?
    • 减少内存开销:如果使用ReenteantLock则需要节点继承AQS来获得同步支持,增加内存开销,而1.8中只有头节点需要同步
    • 内部优化:synchronized是JVM直接支持的,JVM能在运行时做出相应的优化措施:锁粗化、锁消除、锁自旋等

ConcurrentHashMap源码解析

// 此处只谈其分段所锁的实现 因为其本质就是HashMap// 构造方法
// 无参构造方法
public ConcurrentHashMap() {
}// map的初始容量为initialCapacity
public ConcurrentHashMap(int initialCapacity) {if (initialCapacity < 0)throw new IllegalArgumentException();int cap = ((initialCapacity >= (MAXIMUM_CAPACITY >>> 1)) ?MAXIMUM_CAPACITY :tableSizeFor(initialCapacity + (initialCapacity >>> 1) + 1));this.sizeCtl = cap;
}// 始化参数是一个map
public ConcurrentHashMap(Map<? extends K, ? extends V> m) {this.sizeCtl = DEFAULT_CAPACITY;putAll(m);
}// 初始值,和负载因子
public ConcurrentHashMap(int initialCapacity, float loadFactor) {this(initialCapacity, loadFactor, 1);
}// 初始值,负载因子,和并发等级
public ConcurrentHashMap(int initialCapacity,float loadFactor, int concurrencyLevel) {if (!(loadFactor > 0.0f) || initialCapacity < 0 || concurrencyLevel <= 0)throw new IllegalArgumentException();if (initialCapacity < concurrencyLevel)   // Use at least as many binsinitialCapacity = concurrencyLevel;   // as estimated threadslong size = (long)(1.0 + (long)initialCapacity / loadFactor);int cap = (size >= (long)MAXIMUM_CAPACITY) ?MAXIMUM_CAPACITY : tableSizeFor((int)size);this.sizeCtl = cap;
}// 核心分段 静态内部类
static class Segment<K,V> extends ReentrantLock implements Serializable {private static final long serialVersionUID = 2249069246763182397L;final float loadFactor;Segment(float lf) { this.loadFactor = lf; }
}// 如果指定的键已经不再与值相关联,尝试使用给定的映射函数计算其值并将其输入到该地图除非null 。 
// 法调用原子方式执行,因此至多每键一旦施加的功能。 而运算正在进行
// 计算应短而简单,而且不能尝试更新此地图的任何其他映射这个地图
// 线程上的一些尝试更新操作可能被阻止。
public V computeIfAbsent(K key, Function<? super K, ? extends V> mappingFunction) {if (key == null || mappingFunction == null)throw new NullPointerException();int h = spread(key.hashCode());V val = null;int binCount = 0;for (Node<K,V>[] tab = table;;) {Node<K,V> f; int n, i, fh;if (tab == null || (n = tab.length) == 0)tab = initTable();else if ((f = tabAt(tab, i = (n - 1) & h)) == null) {Node<K,V> r = new ReservationNode<K,V>();synchronized (r) {if (casTabAt(tab, i, null, r)) {binCount = 1;Node<K,V> node = null;try {if ((val = mappingFunction.apply(key)) != null)node = new Node<K,V>(h, key, val, null);} finally {setTabAt(tab, i, node);}}}if (binCount != 0)break;}else if ((fh = f.hash) == MOVED)tab = helpTransfer(tab, f);else {boolean added = false;synchronized (f) {if (tabAt(tab, i) == f) {if (fh >= 0) {binCount = 1;for (Node<K,V> e = f;; ++binCount) {K ek; V ev;if (e.hash == h &&((ek = e.key) == key ||(ek != null && key.equals(ek)))) {val = e.val;break;}Node<K,V> pred = e;if ((e = e.next) == null) {if ((val = mappingFunction.apply(key)) != null) {added = true;pred.next = new Node<K,V>(h, key, val, null);}break;}}}else if (f instanceof TreeBin) {binCount = 2;TreeBin<K,V> t = (TreeBin<K,V>)f;TreeNode<K,V> r, p;if ((r = t.root) != null &&(p = r.findTreeNode(h, key, null)) != null)val = p.val;else if ((val = mappingFunction.apply(key)) != null) {added = true;t.putTreeVal(h, key, val);}}}}if (binCount != 0) {if (binCount >= TREEIFY_THRESHOLD)treeifyBin(tab, i);if (!added)return val;break;}}}if (val != null)addCount(1L, binCount);return val;
}// 如果指定键的值存在,尝试来计算给定的密钥和它的当前映射值的新映射。 
// 法调用原子方式执行。 而运算正在进行,所以在计算应短而简单,
// 能尝试更新此地图的任何其他映射这个地图被其它线程上的一些尝试更新操作可能被阻止
public V computeIfPresent(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction) {if (key == null || remappingFunction == null)throw new NullPointerException();int h = spread(key.hashCode());V val = null;int delta = 0;int binCount = 0;for (Node<K,V>[] tab = table;;) {Node<K,V> f; int n, i, fh;if (tab == null || (n = tab.length) == 0)tab = initTable();else if ((f = tabAt(tab, i = (n - 1) & h)) == null)break;else if ((fh = f.hash) == MOVED)tab = helpTransfer(tab, f);else {synchronized (f) {if (tabAt(tab, i) == f) {if (fh >= 0) {binCount = 1;for (Node<K,V> e = f, pred = null;; ++binCount) {K ek;if (e.hash == h &&((ek = e.key) == key ||(ek != null && key.equals(ek)))) {val = remappingFunction.apply(key, e.val);if (val != null)e.val = val;else {delta = -1;Node<K,V> en = e.next;if (pred != null)pred.next = en;elsesetTabAt(tab, i, en);}break;}pred = e;if ((e = e.next) == null)break;}}else if (f instanceof TreeBin) {binCount = 2;TreeBin<K,V> t = (TreeBin<K,V>)f;TreeNode<K,V> r, p;if ((r = t.root) != null &&(p = r.findTreeNode(h, key, null)) != null) {val = remappingFunction.apply(key, p.val);if (val != null)p.val = val;else {delta = -1;if (t.removeTreeNode(p))setTabAt(tab, i, untreeify(t.first));}}}}}if (binCount != 0)break;}}if (delta != 0)addCount((long)delta, binCount);return val;
}// 尝试计算用于指定键和其当前映射的值的映射(或null ,如果没有当前映射)。 
// 法调用原子方式执行。 而运算正在进行,所以在计算应短而简单,
// 能尝试更新此地图的任何其他映射这个地图被其它线程上的一些尝试更新操作可能被阻止。
public V compute(K key,BiFunction<? super K, ? super V, ? extends V> remappingFunction) {if (key == null || remappingFunction == null)throw new NullPointerException();int h = spread(key.hashCode());V val = null;int delta = 0;int binCount = 0;for (Node<K,V>[] tab = table;;) {Node<K,V> f; int n, i, fh;if (tab == null || (n = tab.length) == 0)tab = initTable();else if ((f = tabAt(tab, i = (n - 1) & h)) == null) {Node<K,V> r = new ReservationNode<K,V>();synchronized (r) {if (casTabAt(tab, i, null, r)) {binCount = 1;Node<K,V> node = null;try {if ((val = remappingFunction.apply(key, null)) != null) {delta = 1;node = new Node<K,V>(h, key, val, null);}} finally {setTabAt(tab, i, node);}}}if (binCount != 0)break;}else if ((fh = f.hash) == MOVED)tab = helpTransfer(tab, f);else {synchronized (f) {if (tabAt(tab, i) == f) {if (fh >= 0) {binCount = 1;for (Node<K,V> e = f, pred = null;; ++binCount) {K ek;if (e.hash == h &&((ek = e.key) == key ||(ek != null && key.equals(ek)))) {val = remappingFunction.apply(key, e.val);if (val != null)e.val = val;else {delta = -1;Node<K,V> en = e.next;if (pred != null)pred.next = en;elsesetTabAt(tab, i, en);}break;}pred = e;if ((e = e.next) == null) {val = remappingFunction.apply(key, null);if (val != null) {delta = 1;pred.next =new Node<K,V>(h, key, val, null);}break;}}}else if (f instanceof TreeBin) {binCount = 1;TreeBin<K,V> t = (TreeBin<K,V>)f;TreeNode<K,V> r, p;if ((r = t.root) != null)p = r.findTreeNode(h, key, null);elsep = null;V pv = (p == null) ? null : p.val;val = remappingFunction.apply(key, pv);if (val != null) {if (p != null)p.val = val;else {delta = 1;t.putTreeVal(h, key, val);}}else if (p != null) {delta = -1;if (t.removeTreeNode(p))setTabAt(tab, i, untreeify(t.first));}}}}if (binCount != 0) {if (binCount >= TREEIFY_THRESHOLD)treeifyBin(tab, i);break;}}}if (delta != 0)addCount((long)delta, binCount);return val;
}// 如果指定键已经不再与一个(非空)值相关联,它与给定值关联。 
// 替换指定重映射函数的结果,或移除如果该值为null 。 整个方法调用原子方式执行。 
// 正在进行,所以在计算应短而简单,而且不能尝试更新此地图的任何其他映射这个地图
// 程上的一些尝试更新操作可能被阻止。
public V merge(K key, V value, BiFunction<? super V, ? super V, ? extends V> remappingFunction) {if (key == null || value == null || remappingFunction == null)throw new NullPointerException();int h = spread(key.hashCode());V val = null;int delta = 0;int binCount = 0;for (Node<K,V>[] tab = table;;) {Node<K,V> f; int n, i, fh;if (tab == null || (n = tab.length) == 0)tab = initTable();else if ((f = tabAt(tab, i = (n - 1) & h)) == null) {if (casTabAt(tab, i, null, new Node<K,V>(h, key, value, null))) {delta = 1;val = value;break;}}else if ((fh = f.hash) == MOVED)tab = helpTransfer(tab, f);else {synchronized (f) {if (tabAt(tab, i) == f) {if (fh >= 0) {binCount = 1;for (Node<K,V> e = f, pred = null;; ++binCount) {K ek;if (e.hash == h &&((ek = e.key) == key ||(ek != null && key.equals(ek)))) {val = remappingFunction.apply(e.val, value);if (val != null)e.val = val;else {delta = -1;Node<K,V> en = e.next;if (pred != null)pred.next = en;elsesetTabAt(tab, i, en);}break;}pred = e;if ((e = e.next) == null) {delta = 1;val = value;pred.next =new Node<K,V>(h, key, val, null);break;}}}else if (f instanceof TreeBin) {binCount = 2;TreeBin<K,V> t = (TreeBin<K,V>)f;TreeNode<K,V> r = t.root;TreeNode<K,V> p = (r == null) ? null :r.findTreeNode(h, key, null);val = (p == null) ? value :remappingFunction.apply(p.val, value);if (val != null) {if (p != null)p.val = val;else {delta = 1;t.putTreeVal(h, key, val);}}else if (p != null) {delta = -1;if (t.removeTreeNode(p))setTabAt(tab, i, untreeify(t.first));}}}}if (binCount != 0) {if (binCount >= TREEIFY_THRESHOLD)treeifyBin(tab, i);break;}}}if (delta != 0)addCount((long)delta, binCount);return val;
}// 移动或者复制每个仓库的节点
private final void transfer(Node<K,V>[] tab, Node<K,V>[] nextTab) {int n = tab.length, stride;if ((stride = (NCPU > 1) ? (n >>> 3) / NCPU : n) < MIN_TRANSFER_STRIDE)stride = MIN_TRANSFER_STRIDE; // subdivide rangeif (nextTab == null) {            // initiatingtry {@SuppressWarnings("unchecked")Node<K,V>[] nt = (Node<K,V>[])new Node<?,?>[n << 1];nextTab = nt;} catch (Throwable ex) {      // try to cope with OOMEsizeCtl = Integer.MAX_VALUE;return;}nextTable = nextTab;transferIndex = n;}int nextn = nextTab.length;ForwardingNode<K,V> fwd = new ForwardingNode<K,V>(nextTab);boolean advance = true;boolean finishing = false; // to ensure sweep before committing nextTabfor (int i = 0, bound = 0;;) {Node<K,V> f; int fh;while (advance) {int nextIndex, nextBound;if (--i >= bound || finishing)advance = false;else if ((nextIndex = transferIndex) <= 0) {i = -1;advance = false;}else if (U.compareAndSwapInt(this, TRANSFERINDEX, nextIndex,nextBound = (nextIndex > stride ?nextIndex - stride : 0))) {bound = nextBound;i = nextIndex - 1;advance = false;}}if (i < 0 || i >= n || i + n >= nextn) {int sc;if (finishing) {nextTable = null;table = nextTab;sizeCtl = (n << 1) - (n >>> 1);return;}if (U.compareAndSwapInt(this, SIZECTL, sc = sizeCtl, sc - 1)) {if ((sc - 2) != resizeStamp(n) << RESIZE_STAMP_SHIFT)return;finishing = advance = true;i = n; // recheck before commit}}else if ((f = tabAt(tab, i)) == null)advance = casTabAt(tab, i, null, fwd);else if ((fh = f.hash) == MOVED)advance = true; // already processedelse {synchronized (f) {if (tabAt(tab, i) == f) {Node<K,V> ln, hn;if (fh >= 0) {int runBit = fh & n;Node<K,V> lastRun = f;for (Node<K,V> p = f.next; p != null; p = p.next) {int b = p.hash & n;if (b != runBit) {runBit = b;lastRun = p;}}if (runBit == 0) {ln = lastRun;hn = null;}else {hn = lastRun;ln = null;}for (Node<K,V> p = f; p != lastRun; p = p.next) {int ph = p.hash; K pk = p.key; V pv = p.val;if ((ph & n) == 0)ln = new Node<K,V>(ph, pk, pv, ln);elsehn = new Node<K,V>(ph, pk, pv, hn);}setTabAt(nextTab, i, ln);setTabAt(nextTab, i + n, hn);setTabAt(tab, i, fwd);advance = true;}else if (f instanceof TreeBin) {TreeBin<K,V> t = (TreeBin<K,V>)f;TreeNode<K,V> lo = null, loTail = null;TreeNode<K,V> hi = null, hiTail = null;int lc = 0, hc = 0;for (Node<K,V> e = t.first; e != null; e = e.next) {int h = e.hash;TreeNode<K,V> p = new TreeNode<K,V>(h, e.key, e.val, null, null);if ((h & n) == 0) {if ((p.prev = loTail) == null)lo = p;elseloTail.next = p;loTail = p;++lc;}else {if ((p.prev = hiTail) == null)hi = p;elsehiTail.next = p;hiTail = p;++hc;}}ln = (lc <= UNTREEIFY_THRESHOLD) ? untreeify(lo) :(hc != 0) ? new TreeBin<K,V>(lo) : t;hn = (hc <= UNTREEIFY_THRESHOLD) ? untreeify(hi) :(lc != 0) ? new TreeBin<K,V>(hi) : t;setTabAt(nextTab, i, ln);setTabAt(nextTab, i + n, hn);setTabAt(tab, i, fwd);advance = true;}}}}}
}// 在替换所有斌链接节点在给定的索引,除非表是太小了,在这种情况下,调整大小来代替。
private final void treeifyBin(Node<K,V>[] tab, int index) {Node<K,V> b; int n, sc;if (tab != null) {if ((n = tab.length) < MIN_TREEIFY_CAPACITY)tryPresize(n << 1);else if ((b = tabAt(tab, index)) != null && b.hash >= 0) {synchronized (b) {if (tabAt(tab, index) == b) {TreeNode<K,V> hd = null, tl = null;for (Node<K,V> e = b; e != null; e = e.next) {TreeNode<K,V> p =new TreeNode<K,V>(e.hash, e.key, e.val,null, null);if ((p.prev = tl) == null)hd = p;elsetl.next = p;tl = p;}setTabAt(tab, index, new TreeBin<K,V>(hd));}}}}
}
  • ConcurrentHashMap取消了segment分段锁,而采用CAS和synchronized来保证并发安全。数据结构跟HashMap1.8的结构一样,数组+链表/红黑二叉树
  • synchronized只锁定当前链表或红黑二叉树的首节点,这样只要hash不冲突,就不会产生并发,效率又提升N倍。
  • 推荐文章
    • https://www.jianshu.com/p/d10256f0ebea
    • http://www.jasongj.com/java/concurrenthashmap/
    • https://www.itqiankun.com/article/concurrenthashmap-principle
    • http://www.codeceo.com/article/java-hashmap-concurrenthashmap.html
    • https://www.cnblogs.com/shan1393/p/9020564.html
    • Java并发包concurrent——ConcurrentHashMap
    • Java8 ConcurrentHashMap详解
  • 一些ConcurrentHashMap的问题
    • 扩容过程中,读访问能够访问到数据,怎么实现的?
      • volatile关键字修饰数据,保证了内存一致性
    • 扩容机制中,写访问如何处理
    • 假设指定桶为形成红黑树,且当前红黑树正在自平衡,那此时的读线程是被阻塞等待还是有其他的方案?详细说一说
    • JDK8中统计当前散列表的元素个数是如何实现的?为什么没有使用AtomicLong?
    • 简单说一下,LastRun机制

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.luyixian.cn/news_show_73979.aspx

如若内容造成侵权/违法违规/事实不符,请联系dt猫网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

内核并发消杀器(KCSAN)技术分析

一、KCSAN介绍KCSAN(Kernel Concurrency Sanitizer)是一种动态竞态检测器&#xff0c;它依赖于编译时插装&#xff0c;并使用基于观察点的采样方法来检测竞态&#xff0c;其主要目的是检测数据竞争。KCSAN是一种检测LKMM(Linux内核内存一致性模型)定义的数据竞争(data race)的工…

【网络原理8】HTTP请求篇

在上一篇文章当中&#xff0c;我们也提到了什么是HTTP。 每一个HTTP请求&#xff0c;都会对应一个HTTP响应。 下面这一篇文章&#xff0c;将聊一下HTTP请求的一些内容 目录 一、URL 第一部分&#xff1a;协议名称 第二部分:认证信息(新的版本已经没有了) 第三部分&#xf…

【数通网络交换基础梳理1】二层交换机、以太网帧、MAC地址详解及数据帧转发原理(爆炸细)

一、网络模型 万年不变&#xff0c;先从模型结构分析&#xff0c;现在大家熟知的网络模型有两种。第一种是&#xff0c;OSI七层模型&#xff0c;第二种是TCP/IP模型。在实际运用中&#xff0c;参考更多的是TCP/IP模型。 OSI七层模型 TCP/IP模型 不需要全部理解&#xff0c;…

Spring MVC 源码- HandlerExceptionResolver 组件

HandlerExceptionResolver 组件HandlerExceptionResolver 组件&#xff0c;处理器异常解析器&#xff0c;将处理器&#xff08; handler &#xff09;执行时发生的异常&#xff08;也就是处理请求&#xff0c;执行方法的过程中&#xff09;解析&#xff08;转换&#xff09;成对…

Python变量的定义和使用

定义&#xff1a;变量就是计算机内存中存储某些数据的位置的名称 形象理解变量就是一个存放东西的容器&#xff0c;该容器的名字就叫做变量&#xff0c;容器存放的东西就是变量的值 变量的组成&#xff1a; 标识&#xff1a;标识对象所储存的内存地址&#xff0c;使用内置函数i…

六千字让你明白什么是数字孪生?

文章目录1. 背景2. 数字孪生基础2.1 概念2.2 价值3. 技术生态3.1 技术体系3.2 核心技术3.2.1 多领域、多尺度融合建模3.2.2 数据驱动与物理模型融合的状态评估3.2.3 数据采集和传输3.2.4 全生命周期数据管理3.2.5 虚拟现实呈现3.2.6 高性能计算3.3 建设3.3.1 重点3.3.1.1 数字孪…

SEATA是什么?它的四种分布式事务模式

一、SEATA是什么&#xff1f; Seata 是一款开源的分布式事务解决方案&#xff0c;致力于提供高性能和简单易用的分布式事务服务。Seata 将为用户提供了 AT、TCC、SAGA 和 XA 事务模式&#xff0c;为用户打造一站式的分布式解决方案。 在继续学习使用SEATA之前&#xff0c;对s…

数据结构栈的经典OJ题【leetcode最小栈问题大剖析】【leetcode有效的括号问题大剖析】

目录 0.前言 1.最小栈 1.1 原题展示 1.2 思路分析 1.2.1 场景引入 1.2.2 思路 1.3 代码实现 1.3.1 最小栈的删除 1.3.2 最小栈的插入 1.3.3 获取栈顶元素 1.3.4 获取当前栈的最小值 2. 有效的括号 0.前言 本篇博客已经把两个关于栈的OJ题分块&#xff0c;可以根据目…

【华为OD机试模拟题】用 C++ 实现 - 分糖果(2023.Q1)

最近更新的博客 华为OD机试 - 入栈出栈(C++) | 附带编码思路 【2023】 华为OD机试 - 箱子之形摆放(C++) | 附带编码思路 【2023】 华为OD机试 - 简易内存池 2(C++) | 附带编码思路 【2023】 华为OD机试 - 第 N 个排列(C++) | 附带编码思路 【2023】 华为OD机试 - 考古…

Jina 3.14 版本发布!支持独立部署Executor

Jina 是一个 MLOps 框架&#xff0c;赋能开发者在云上构建多模态、跨模态的应用程序。Jina 能够将 PoC 提升为生产就绪服务。基础设施的复杂性交给 Jina&#xff0c;开发者能够直接轻松使用高级解决方案和云原生技术。&#x1f31f; GitHubhttps://github.com/jina-ai/jina/rel…

基于博客系统的测试用例

登陆界面博客预览页博客详情页博客编辑页

【华为OD机试模拟题】用 C++ 实现 - 时间格式化(2023.Q1)

最近更新的博客 华为OD机试 - 入栈出栈(C++) | 附带编码思路 【2023】 华为OD机试 - 箱子之形摆放(C++) | 附带编码思路 【2023】 华为OD机试 - 简易内存池 2(C++) | 附带编码思路 【2023】 华为OD机试 - 第 N 个排列(C++) | 附带编码思路 【2023】 华为OD机试 - 考古…

【华为OD机试模拟题】用 C++ 实现 - 最多获得的短信条数(2023.Q1)

最近更新的博客 【华为OD机试模拟题】用 C++ 实现 - 分积木(2023.Q1) 【华为OD机试模拟题】用 C++ 实现 - 吃火锅(2023.Q1) 【华为OD机试模拟题】用 C++ 实现 - RSA 加密算法(2023.Q1) 【华为OD机试模拟题】用 C++ 实现 - 构成的正方形数量(2023.Q1) 【华为OD机试模拟…

MATLAB绘制雷达图/蜘蛛图

雷达图/蜘蛛图 1 方法一 函数来源为MATLAB | 如何使用MATLAB绘制雷达图(蜘蛛图) 1.1 调用函数 1.2 案例 2 方法二 函数来源为MATLAB帮助-spider_plot 2.1 调用函数 语法&#xff08;Syntax&#xff09;&#xff1a; spider_plot(P)spider_plot(P, Name, Value, ...)h …

内网穿透常用方法系列总结

前言在内网渗透时&#xff0c;一个WebShell或CobaltStrike、Metasploit上线等&#xff0c;只是开端&#xff0c;更多是要内网横向移动&#xff0c;扩大战果&#xff0c;打到核心区域。但后渗透的前提是需要搭建一条通向内网的“专属通道”&#xff0c;才能进一步攻击。可实战中…

中国ETC行业市场规模及未来发展趋势

中国ETC行业市场规模及未来发展趋势编辑根据市场调研在线网发布的2023-2029年中国ETC行业发展策略分析及战略咨询研究报告分析&#xff1a;随着政府坚持实施绿色出行政策&#xff0c;ETC行业也受到了极大的支持。根据中国智能交通协会统计&#xff0c;2017年中国ETC行业市场规模…

高端装备的AC主轴头结构

加工机器人的AC主轴头和位置相关动力学特性1. 位置依赖动态特性及其复杂性2. AC主轴头2.1 常见主轴头摆角结构2.2 摆动机构3. 加装AC主轴头的作用和局限性4. 切削机器人的减速器类型5. 其他并联结构形式参考文献资料1. 位置依赖动态特性及其复杂性 However, FRF measurements …

【华为OD机试模拟题】用 C++ 实现 - 求解连续数列(2023.Q1)

最近更新的博客 【华为OD机试模拟题】用 C++ 实现 - 分积木(2023.Q1) 【华为OD机试模拟题】用 C++ 实现 - 吃火锅(2023.Q1) 【华为OD机试模拟题】用 C++ 实现 - RSA 加密算法(2023.Q1) 【华为OD机试模拟题】用 C++ 实现 - 构成的正方形数量(2023.Q1) 【华为OD机试模拟…

【异常】导出Excel异常This archive contains unclosed entries.

一、异常说明 二、定位问题代码 一看问题&#xff0c; 上下文都是与订单相关的内容。 查询代码的使用地方&#xff0c;发现出现在这个Mybatis的select语句中 查看备注&#xff0c;发现是订单物流&#xff0c;那就没跑了&#xff0c; 肯定是商城的物流模块出了问题 那是什么地…

Gin获取Response Body引发的OOM

有轮子尽量用轮子 &#x1f62d; &#x1f62d; &#x1f62d; &#x1f62d; &#x1f62d; &#x1f62d; 我们在开发中基于Gin开发了一个Api网关&#xff0c;但上线后发现内存会在短时间内暴涨&#xff0c;然后被OOM kill掉。具体内存走势如下图&#xff1a; 放大其中一次 在…