首页
友情链接
精美壁纸
给我留言
更多
关于我
Search
1
uniapp Click点击事件冲突解决
4,558 阅读
2
【插件】UNI APP 实现商米打印机功能支持T1,T2,V2机型
3,871 阅读
3
【测试可用】个人码免签支付系统源码/免签支付系统/微信支付平台
1,974 阅读
4
Java Validation参数校验注解使用
1,230 阅读
5
windows10下docker:给已存在的容器添加端口映射的方法
1,219 阅读
Java
Spring Boot
Spring Mvc
Java基础
进阶知识
前端
uniapp
小程序/公众号
JavaScript
HTML/CSS
Vue
PHP
开源软件
商城
营销工具
开发工具
视频/教程
Discuz主题/插件
typecho主题/插件
SEO杂谈
数据库
MongoDB
MySQL
Redis
单片机
概念说明
电路相关
Python
devops
docker
k8s
linux
职场杂谈
登录
/
注册
Search
标签搜索
python
mysql
人人商城
php
java
docker
typecho
插件
微擎
seo
spring boot
discuz
队列
uni-app
phpcms
教程视频
开源系统
源码
工具
css
哈根达斯
累计撰写
100
篇文章
累计收到
154
条评论
首页
栏目
Java
Spring Boot
Spring Mvc
Java基础
进阶知识
前端
uniapp
小程序/公众号
JavaScript
HTML/CSS
Vue
PHP
开源软件
商城
营销工具
开发工具
视频/教程
Discuz主题/插件
typecho主题/插件
SEO杂谈
数据库
MongoDB
MySQL
Redis
单片机
概念说明
电路相关
Python
devops
docker
k8s
linux
职场杂谈
页面
友情链接
精美壁纸
给我留言
关于我
搜索到
6
篇与
java
的结果
2023-01-31
并发编程-Java jdk中队列Queue的详解与应用
一、什么是队列,队列有什么用?假设春运火车站只有一个售票窗口,但是买火车票的人非常多,如果大家一哄而上到窗口进行买票,估计售票员会发疯,因此聪明的人类发明了排队这种游戏规则,则先到先得的机制,在不断的发展过程中,排队的规则也慢慢完善,比如有些例外比如军人优先或者老弱病残优先则可以进行插队,通过这样的规则可以让面对人流高峰也可以平稳进行工作。这个规则运用到实际开发中也是如此,在程序中队列是一种特殊的线性表,遵循先入先出、后入后出的基本原则,一般来说,它只允许在队列的前端进行删除操作,而在队列的后端进行插入操作。但是java的某些队列运行在任何地方插入删除(如军人插队)在java中根据队列特性,队列可分为阻塞队列和非阻塞队列,有界队列和无界队列、单向链表队列和双向链表队列之分,二、阻塞/非阻塞队列2.1 阻塞队列入列(添加元素)时,如果元素数量超过队列总数,会进行等待(阻塞),待队列的中的元素出列后,元素数量未超过队列总数时,就会解除阻塞状态,进而可以继续入列;出列(删除元素)时,如果队列为空的情况下,也会进行等待(阻塞),待队列有值的时候即会解除阻塞状态,进而继续出列;阻塞队列优点 :阻塞队列的好处是可以防止队列容器溢出;只要满了就会进行阻塞等待;也就不存在溢出的情况,只要是阻塞队列,都是线程安全的;实现类DelayQueue:一个支持延时获取元素的无界阻塞队列LinkedTransferQueue:一个由链表结构组成的无界阻塞队列。ArrayBlockingQueue:有界队列,阻塞式,初始化时必须指定队列大小,且不可改变;,底层由数组实现;SynchronousQueue:最多只能存储一个元素,每一个put操作必须等待一个take操作,否则不能继续添加元素PriorityBlockingQueue:一个带优先级的队列,而不是先进先出队列。元素按优先级顺序被移除,而且它也是无界的,也就是没有容量上限,虽然此队列逻辑上是无界的,但是由于资源被耗尽,所以试图执行添加操作可能会导致 OutOfMemoryError 错误;2.2 非阻塞队列不管出列还是入列,都不会进行阻塞,入列时,如果元素数量超过队列总数,则会抛出异常,出列时,如果队列为空,则取出空值;一般情况下,非阻塞式队列使用的比较少,一般都用阻塞式的对象比较多;阻塞和非阻塞队列在使用上的最大区别就是阻塞队列提供了以下2个方法:出队阻塞方法 : take()入队阻塞方法 : put()实现类ConcurrentLinkedQueue:单向链表结构的无界并发队列, 非阻塞队列,由CAS实现线程安全,内部基于节点实现ConcurrentLinkedDeque:双向链表结构的无界并发队列, 非阻塞队列,由CAS实现线程安全PriorityQueue: 内部基于数组实现,线程不安全的队列三、有界/无界队列有界和无界有界:有界限,大小长度受限制无界:无限大小,其实说是无限大小,其实是有界限的,只不过超过界限时就会进行扩容,就想ArrayList 一样,在内部动态扩容,最大值为Integer.MAX_VALUE四、单向/双向链表队列4.1 单向链表每个元素中除了元素本身之外,还存储一个指针,这个指针指向下一个元素;4.2 双向链表除了元素本身之外,还有两个指针,一个指针指向前一个元素的地址,另一个指针指向后一个元素的地址;四、 Java中队列的继承关系五、队列常用方法队列常用方法函数名函数介绍特殊说明add增加一个元索如果队列已满,则抛出一个IIIegaISlabEepeplian异常remove移除并返回队列头部的元素如果队列为空,则抛出一个NoSuchElementException异常element返回队列头部的元素如果队列为空,则抛出一个NoSuchElementException异常offer添加一个元素并返回true如果队列已满,则返回falsepoll移除并返问队列头部的元素如果队列为空,则返回nullpeek返回队列头部的元素如果队列为空,则返回nullput添加一个元素如果队列满,则阻塞take移除并返回队列头部的元素如果队列为空,则阻塞drainTo一次性取出队列所有元素)-PS知识点: remove、element、offer 、poll、peek 属于Queue接口的定义。六、代码示例假设现在需要售卖一趟车票共100张,并且有3个窗口开放售票,当有大量人购买时如何保障出票安全,我们使用ArrayBlockingQueue阻塞式有界队列来模拟常见代码。package com.queue; import cn.hutool.core.util.StrUtil; import java.util.concurrent.*; import java.util.concurrent.atomic.AtomicInteger; public class BlockingQueueTest { //售票员窗口 static final int SALESPERSON=3; //总票数100张 static AtomicInteger ticketTotal=new AtomicInteger(100); //假设大厅总排队人数最大可排1000人 static final int MAX_COUNT=1000; public static void main(String[] args) { // 阻塞-有界队列 BlockingQueue<String> queue=new ArrayBlockingQueue<String>(MAX_COUNT); for (int i = 0; i < SALESPERSON; i++) { //模拟启动三个工作窗口工作排队 new Thread(new Producer(queue)).start(); //模拟启动三个工作窗口出票 new Thread(new Consumer(queue)).start(); } } /** * 模拟系统排队出票 */ static class Consumer extends Thread{ BlockingQueue<String> queue; public Consumer(BlockingQueue<String> queue) { this.queue = queue; } @Override public void run() { while (true){ try { if(ticketTotal.get()==0){ System.out.println("结束售票啦,收工下班"); return; } System.out.println(StrUtil.format("{},出票号:{},大厅排队总人数:{}",queue.take(),ticketTotal.getAndDecrement(),queue.size())); } catch (InterruptedException e) { e.printStackTrace(); } } } } /** * 模拟窗口并发出票发起请求 */ static class Producer implements Runnable{ BlockingQueue<String> queue; public Producer(BlockingQueue<String> queue) { this.queue = queue; } @Override public void run() { while (true){ try { queue.put(StrUtil.format("出票窗口{}号,购票请求:{}",Thread.currentThread().getId(),ticketTotal.get())); } catch (InterruptedException e) { e.printStackTrace(); } } } } } 通过上述代码运行,我看可以看出,当26,23,20同时请求同一个100票号时,系统也可以再多线程下正常出票,有效解决并发和出错票等问题出票窗口26号,购票请求:100,出票号:100,大厅排队总人数:7 出票窗口23号,购票请求:100,出票号:98,大厅排队总人数:6 出票窗口23号,购票请求:100,出票号:97,大厅排队总人数:217 出票窗口20号,购票请求:100,出票号:99,大厅排队总人数:8 出票窗口23号,购票请求:100,出票号:96,大厅排队总人数:226 出票窗口23号,购票请求:100,出票号:94,大厅排队总人数:237 出票窗口20号,购票请求:100,出票号:95,大厅排队总人数:227 出票窗口26号,购票请求:100,出票号:92,大厅排队总人数:264 出票窗口20号,购票请求:100,出票号:93,大厅排队总人数:246 出票窗口20号,购票请求:100,出票号:90,大厅排队总人数:274 出票窗口23号,购票请求:100,出票号:91,大厅排队总人数:267 出票窗口20号,购票请求:97,出票号:88,大厅排队总人数:292 出票窗口20号,购票请求:97,出票号:89,大厅排队总人数:284 出票窗口20号,购票请求:97,出票号:86,大厅排队总人数:327 出票窗口26号,购票请求:98,出票号:87,大厅排队总人数:298 出票窗口23号,购票请求:97,出票号:85,大厅排队总人数:341 出票窗口20号,购票请求:97,出票号:83,大厅排队总人数:339 出票窗口20号,购票请求:97,出票号:84,大厅排队总人数:340 出票窗口26号,购票请求:97,出票号:81,大厅排队总人数:352 出票窗口23号,购票请求:97,出票号:82,大厅排队总人数:345 出票窗口23号,购票请求:97,出票号:79,大厅排队总人数:362 出票窗口20号,购票请求:97,出票号:80,大厅排队总人数:360 出票窗口20号,购票请求:97,出票号:77,大厅排队总人数:366 出票窗口26号,购票请求:97,出票号:78,大厅排队总人数:366 出票窗口26号,购票请求:97,出票号:75,大厅排队总人数:380 出票窗口20号,购票请求:97,出票号:76,大厅排队总人数:370 出票窗口20号,购票请求:97,出票号:73,大厅排队总人数:403 出票窗口23号,购票请求:97,出票号:74,大厅排队总人数:391 出票窗口20号,购票请求:97,出票号:72,大厅排队总人数:408 出票窗口23号,购票请求:97,出票号:70,大厅排队总人数:406 出票窗口26号,购票请求:97,出票号:71,大厅排队总人数:407 出票窗口20号,购票请求:97,出票号:69,大厅排队总人数:420 出票窗口20号,购票请求:97,出票号:68,大厅排队总人数:422 出票窗口20号,购票请求:97,出票号:66,大厅排队总人数:433 出票窗口26号,购票请求:97,出票号:65,大厅排队总人数:439 出票窗口23号,购票请求:97,出票号:67,大厅排队总人数:429 出票窗口23号,购票请求:97,出票号:63,大厅排队总人数:442 出票窗口20号,购票请求:97,出票号:64,大厅排队总人数:442 出票窗口26号,购票请求:97,出票号:61,大厅排队总人数:457 出票窗口20号,购票请求:97,出票号:62,大厅排队总人数:447 出票窗口23号,购票请求:97,出票号:59,大厅排队总人数:470 出票窗口20号,购票请求:97,出票号:60,大厅排队总人数:469 出票窗口26号,购票请求:97,出票号:57,大厅排队总人数:489 出票窗口20号,购票请求:97,出票号:58,大厅排队总人数:481 出票窗口23号,购票请求:97,出票号:55,大厅排队总人数:509 出票窗口20号,购票请求:97,出票号:56,大厅排队总人数:494 出票窗口23号,购票请求:97,出票号:53,大厅排队总人数:541 出票窗口26号,购票请求:97,出票号:52,大厅排队总人数:547 出票窗口26号,购票请求:97,出票号:54,大厅排队总人数:522 出票窗口26号,购票请求:97,出票号:50,大厅排队总人数:568 出票窗口23号,购票请求:97,出票号:51,大厅排队总人数:557 出票窗口23号,购票请求:97,出票号:49,大厅排队总人数:587 出票窗口23号,购票请求:97,出票号:47,大厅排队总人数:602 出票窗口26号,购票请求:97,出票号:48,大厅排队总人数:596 出票窗口23号,购票请求:97,出票号:45,大厅排队总人数:631 出票窗口26号,购票请求:97,出票号:46,大厅排队总人数:613 出票窗口23号,购票请求:97,出票号:43,大厅排队总人数:656 出票窗口26号,购票请求:97,出票号:44,大厅排队总人数:649 出票窗口23号,购票请求:97,出票号:41,大厅排队总人数:705 出票窗口26号,购票请求:97,出票号:42,大厅排队总人数:679 出票窗口23号,购票请求:97,出票号:39,大厅排队总人数:725 出票窗口26号,购票请求:97,出票号:40,大厅排队总人数:716 出票窗口26号,购票请求:97,出票号:37,大厅排队总人数:756 出票窗口26号,购票请求:97,出票号:38,大厅排队总人数:740 出票窗口26号,购票请求:97,出票号:35,大厅排队总人数:797 出票窗口23号,购票请求:97,出票号:36,大厅排队总人数:771 出票窗口23号,购票请求:97,出票号:33,大厅排队总人数:850 出票窗口26号,购票请求:97,出票号:34,大厅排队总人数:832 出票窗口26号,购票请求:97,出票号:31,大厅排队总人数:887 出票窗口26号,购票请求:97,出票号:32,大厅排队总人数:867 出票窗口26号,购票请求:97,出票号:29,大厅排队总人数:913 出票窗口23号,购票请求:97,出票号:30,大厅排队总人数:898 出票窗口23号,购票请求:97,出票号:27,大厅排队总人数:939 出票窗口26号,购票请求:97,出票号:28,大厅排队总人数:924 出票窗口26号,购票请求:97,出票号:26,大厅排队总人数:953 出票窗口26号,购票请求:97,出票号:25,大厅排队总人数:953 出票窗口26号,购票请求:97,出票号:22,大厅排队总人数:999 出票窗口26号,购票请求:97,出票号:23,大厅排队总人数:998 出票窗口23号,购票请求:97,出票号:24,大厅排队总人数:972 出票窗口23号,购票请求:97,出票号:20,大厅排队总人数:999 出票窗口23号,购票请求:97,出票号:21,大厅排队总人数:999 出票窗口23号,购票请求:97,出票号:18,大厅排队总人数:999 出票窗口26号,购票请求:97,出票号:19,大厅排队总人数:999 出票窗口23号,购票请求:97,出票号:16,大厅排队总人数:999 出票窗口26号,购票请求:97,出票号:17,大厅排队总人数:999 出票窗口23号,购票请求:97,出票号:14,大厅排队总人数:999 出票窗口26号,购票请求:97,出票号:15,大厅排队总人数:999 出票窗口26号,购票请求:97,出票号:12,大厅排队总人数:999 出票窗口23号,购票请求:97,出票号:13,大厅排队总人数:999 出票窗口26号,购票请求:97,出票号:10,大厅排队总人数:999 出票窗口23号,购票请求:97,出票号:9,大厅排队总人数:999 出票窗口23号,购票请求:97,出票号:11,大厅排队总人数:999 出票窗口26号,购票请求:97,出票号:7,大厅排队总人数:999 出票窗口23号,购票请求:97,出票号:8,大厅排队总人数:1000 出票窗口26号,购票请求:97,出票号:5,大厅排队总人数:999 出票窗口23号,购票请求:97,出票号:6,大厅排队总人数:999 出票窗口23号,购票请求:97,出票号:3,大厅排队总人数:999 出票窗口23号,购票请求:97,出票号:4,大厅排队总人数:999 结束售票啦,收工下班 出票窗口23号,购票请求:97,出票号:1,大厅排队总人数:999 结束售票啦,收工下班 出票窗口26号,购票请求:97,出票号:2,大厅排队总人数:999 结束售票啦,收工下班
2023年01月31日
144 阅读
0 评论
0 点赞
2023-01-16
Java中基本数据类型与对应的包装类型介绍
目录一. Java基本类型与包装类型介绍Java中有8种基本数据类型,分别为:数字类型(6种) :byte、short、int、long、float、double字符类型(1种) :char布尔型(1种) :boolean。这八种基本类型都有对应的包装类分别为:Byte、Short、Integer、Long、Float、DoubleCharacterBoolean基本类型位数字节默认值int3240short1620long6480Lbyte810char162'u0000'float3240fdouble6480dboolean1官方文档未明确定义,它依赖于 JVM 厂商的具体实现。逻辑上理解是占用 1位,但是实际中会考虑计算机高效存储因素。false二 类型的自动装箱与拆箱装箱与拆箱的含义:装箱:将基本类型用它们对应的引用类型包装起来;拆箱:将包装类型转换为基本数据类型;Java基本类型的包装类的大部分实现了常量池技术,即Byte,Short,Integer,Long,Character,Boolean;前面4种包装类默认创建了数值[-128,127]的相应类型的缓存数据,Character创建了数值在[0,127]范围的缓存数据,Boolean 直接返回True Or False。如果超出对应范围仍然会去创建新的对象。其中仅浮点数类型的包装类Float与Double 没有实现常量池技术。Boolean常量池public static Boolean valueOf(boolean b) { return (b ? TRUE : FALSE); // 其中TRUE与FALSE为定义的静态常量 }CharacterCache 缓存常量池private static class CharacterCache { private CharacterCache(){} static final Character cache[] = new Character[127 + 1]; static { for (int i = 0; i < cache.length; i++) cache[i] = new Character((char)i); } }/** *此方法将始终缓存-128 到 127(包括端点)范围内的值,并可以缓存此范围之外的其他值。 */ public static Integer valueOf(int i) { if (i >= IntegerCache.low && i <= IntegerCache.high) //如果在缓存范围值内 return IntegerCache.cache[i + (-IntegerCache.low)]; return new Integer(i); // 不如不在范围内者使用new运算符创建一个堆内存返回新的对象 }举例说明例子1 Integer i1 = 33; Integer i2 = 33; System.out.println(i1 == i2);// 输出 true, 因为33在-128-127范围内使用的是缓存常量,即两个数值的内存地址一致 Integer i11 = 333; Integer i22 = 333; System.out.println(i11 == i22);// 输出 false // 超出范围,返回的都是new的新对象 Double i3 = 1.2; Double i4 = 1.2; System.out.println(i3 == i4);// 输出 false // Double未实现缓存常量池 例子2需要注意一旦使用new运算符创建对象,则将创建新的数据,不会使用缓存常量值,如以下Integer i1 = 40; Integer i2 = new Integer(40); System.out.println(i1==i2);//输出 falseInteger i1=40;Java 在编译的时候会直接将代码封装成 Integer i1=Integer.valueOf(40);,从而使用常量池中的对象。Integer i1 = new Integer(40);这种情况下会创建新的对象。例子3Integer i1 = 40; Integer i2 = 40; Integer i3 = 0; Integer i4 = new Integer(40); Integer i5 = new Integer(40); Integer i6 = new Integer(0); System.out.println("i1=i2 : " + (i1 == i2)); // true,两个变量使用的是同一个常量值 System.out.println("i1=i2+i3 : " + (i1 == i2 + i3)); // true, 因为"+"加号运算符不能使用在Integer上,所以会自动拆箱为基础类型int:40+0=40, 同时"="等号也无法使用在Integer上,则最终比较为40=40 System.out.println("i1=i4 : " + (i1 == i4));// false System.out.println("i4=i5 : " + (i4 == i5)); // false System.out.println("i4=i5+i6 : " + (i4 == i5 + i6)); //true,加法运算发自动拆箱后进行加法运行并进行比较 System.out.println("40=i5+i6 : " + (40 == i5 + i6));// true,加法运算发自动拆箱后进行加法运行并进行比较
2023年01月16日
81 阅读
0 评论
0 点赞
2023-01-16
Java中双等号比较运算"=="和"equals"方法的区别
目录两个等于号 ==它的作用是判断两个对象的内存地址是不是相等,即判断两个对象是不是同一个对象。基本数据类型比较的是值,引用数据类型==比较的是内存地址equals()比较它的作用也是判断两个对象是否相等,它不能用于比较基本数据类型的变量。 equals()方法存在于 Object 类中,而 Object 类是所有类的直接或间接父类。Object 类 equals() 方法:public boolean equals(Object obj) { return (this == obj); }equals() 方法存在两种使用情况:情况 1:类没有覆盖 equals() 方法, 则通过 equals() 比较该类的两个对象时,等价于通过“==”比较这两个对象。使用的默认是 Object 类 equals() 方法。情况 2:类覆盖 equals() 方法。一般覆盖 equals() 方法用来比对两个对象的 内容 相等;若它们的内容相等,则返回 true(即认为这两个对象相等),比如字符串的String类比较则是覆盖重写equals方法。实例代码:public class test1 { public static void main(String[] args) { String str1 = new String("test"); // a 为一个引用 String str2 = new String("test"); // b为另一个引用,对象的内容一样 String str3 = "test"; // 放在常量池中 String str4 = "test"; // 从常量池中查找 if (str3 == str3) // true System.out.println("aa==bb"); if (str1 == str2) // false,非同一对象 System.out.println("a==b"); if (str1.equals(str2)) // true System.out.println("equals比较内容相等"); if (42 == 42.0) { // true System.out.println("true"); } } }特别说明String 中的 equals 方法是被重写过的,因为 Object 的 equals 方法是比较的对象的内存地址,而 String 的 equals 方法比较的是对象的值。当创建 String 类型的对象时,虚拟机会在常量池中查找有没有已经存在的值和要创建的值相同的对象,如果有就把它赋给当前引用。如果没有就在常量池中重新创建一个 String 对象。String 类 equals()的重写方法:public boolean equals(Object anObject) { if (this == anObject) { return true; } if (anObject instanceof String) { String anotherString = (String)anObject; int n = value.length; if (n == anotherString.value.length) { char v1[] = value; char v2[] = anotherString.value; int i = 0; while (n-- != 0) { if (v1[i] != v2[i]) return false; i++; } return true; } } return false; }
2023年01月16日
97 阅读
0 评论
0 点赞
2021-08-31
Java中ThreadLocal简单学习使用
文章目录索引ThreadLocal定义该类提供了线程局部 (thread-local) 变量。这些变量不同于它们的普通对应物,因为访问某个变量(通过其 get 或 set 方法)的每个线程都有自己的局部变量,它独立于变量的初始化副本。ThreadLocal 实例通常是类中的 private static 字段,它们希望将状态与某一个线程(例如,用户 ID 或事务 ID)相关联。以上来源官方API。大概可以总结为两点:ThreadLocal提供get/set方法,可以访问属于当前线程的变量,也就是可以保证每个线程的变量不一样。ThreadLocal使用时通常定义为private static的。从字面意思理解,可能会将ThreadLocal认为是本地线程,其实ThreadLocal并不是线程,而是线程Thread的局部变量。1、如何使用首先,定义一个private static的ThreadLocal对象。private static ThreadLocal<String> threadLocal = new ThreadLocal<>();然后每个线程可以将当前线程需要存放在局部变量中,并且可以从中获取。public void setAndGet(String name) { threadLocal.set(name); String s = threadLocal.get(); }最后在使用完之后,需要将ThreadLocal中的值移除。public void remove() { threadLocal.remove(); }2.原理那么ThreadLocal是如何做到保证每个线程get出来的数据不一样的呢?我们通过源码来看一下。public void set(T value) { Thread t = Thread.currentThread(); // 通过当前线程获取出来一个ThreadLocalMap ThreadLocalMap map = getMap(t); if (map != null) map.set(this, value); else createMap(t, value); } void createMap(Thread t, T firstValue) { t.threadLocals = new ThreadLocalMap(this, firstValue); }我们发现在set方法中,会创建一个ThreadLocalMap,然后将要设置的值放在这个Map中,而当前这个ThreadLocal对象作为key;然后再将这个ThreadLocalMap赋值给Thread的threadLocals里。如果去看Thread类的代码会发现,在Thread类中存在两个变量threadLocals和inheritableThreadLocals,它们的类型就是ThreadLocal.ThreadLocalMap。通过下图可以看出Thread,ThreadLocal,ThreadLocalMap三者的关系。这时候我们再看看get()和remove()方法的代码。public T get() { Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) { ThreadLocalMap.Entry e = map.getEntry(this); if (e != null) { @SuppressWarnings("unchecked") T result = (T)e.value; return result; } } return setInitialValue(); } ThreadLocalMap getMap(Thread t) { return t.threadLocals; } public void remove() { ThreadLocalMap m = getMap(Thread.currentThread()); if (m != null) m.remove(this); }可以看出来,和我们上面图中的结构一样,get()方法就是从Thread中取出来ThreadLocalMap,然后通过ThreadLocal对象作为Key取出值;remove()方法则是取出ThreadLocalMap将ThreadLocal对应的数据移除。那么ThreadLocalMap到底是什么呢?是java.util.Map接口的子类吗?我们来看源码。static class ThreadLocalMap { static class Entry extends WeakReference<ThreadLocal<?>> { /** The value associated with this ThreadLocal. */ Object value; Entry(ThreadLocal<?> k, Object v) { super(k); value = v; } } private static final int INITIAL_CAPACITY = 16; private Entry[] table; private int size = 0; private int threshold; // Default to 0 // 省略其他代码 }通过源码发现,ThreadLocalMap并没有实现Map接口,也没有集成其他任何的Map类。是定义在ThreadLocal类中的一个静态内部类。而它的结构和HashMap结构极其相似。有一个Entry[]数组存放数据。而这个Entry类是继承自WeakReference类的子类,这一点和HashMap有所不同。ThreadLocalMap和HashMap结构有很多不同,但是还有一点和HashMap不一样,我们来看一下ThreadLocalMap的set方法。private void set(ThreadLocal<?> key, Object value) { Entry[] tab = table; int len = tab.length; // 计算key对应放在tab中的下标 int i = key.threadLocalHashCode & (len-1); //循环遍历tab,首先获取到下标i对应的对象,如果不为空,则执行循环体 // 如果不是相同的threadLocal或者位置失效,则要寻找下一个位置。 for (Entry e = tab[i]; e != null; e = tab[i = nextIndex(i, len)]) { ThreadLocal<?> k = e.get(); // 判断与当前ThreadLocal是否是同一个对象,如果是则进行值替换,结束 if (k == key) { e.value = value; return; } // 如果k==null代表threadLocal的key失效了,将失效的key进行替换 if (k == null) { replaceStaleEntry(key, value, i); return; } } tab[i] = new Entry(key, value); int sz = ++size; if (!cleanSomeSlots(i, sz) && sz >= threshold) rehash(); }代码中循环遍历tab,首先获取到下标i对应的对象,如果不为空,则执行循环体,如果在循环体中的两个条件都不满足,则会执行nextIndex()方法,这个方法的代码如下:private static int nextIndex(int i, int len) { return ((i + 1 < len) ? i + 1 : 0); }这个方法的逻辑就是 开放寻址法 。而HashMap则是通过 拉链法 和 rehash 来做的。3.使用场景通过上面的内容基本可以掌握ThreadLocal的基本用法,那么ThreadLocal主要在什么场景中使用呢。ThreadLocal的作用通过以上了解我们知道主要是用来做线程间数据隔离。那么在什么场景下能用到线程隔离呢?首先想到的就是SimpleDateFormat这个工具类,它不是线程安全的,可以通过ThreadLocal在每个线程中放一份,保证线程安全。还有比如说用户登录的session,或者token数据,只数据当前会话线程,也可以通过ThreadLocal存储。再比如在某些场景下,上下文数据在不同方法之间调用,传递起来非常麻烦,可以通过ThreadLocal存放,只需要在需要用到的地方获取就可以。除了这些场景,在某些框架源码中也会使用到,比如Spring中的事务也主要是通过ThreadLocal和面向切面编程AOP实现的,感兴趣的同学可以查看源码了解。4.避免踩坑内存泄漏ThreadLocalMap中的Entry的Key是一个弱引用,因此如果在使用后不调用remove方法清除掉会导致对应的value内存泄漏。所以在使用完以后一定要记得调用remove方法清除数据。
2021年08月31日
136 阅读
0 评论
0 点赞
2021-08-11
Java 书单:比较全的一篇
健康《程序员健康指南》- 链接:https://pan.baidu.com/s/1hb8xQCE3TrEjazF5ZCe6-w ,提取码:h6fh本书是为程序员量身制作的健康指南,针对头痛、眼部疲劳、背部疼痛和手腕疼痛等常见的问题,简要介绍了其成因、测试方法,并列出了每天的行动计划,从运动、饮食等方面给出详细指导,帮助程序员在不改变工作方式的情况下轻松拥有健康。之所以把这个放到第一位,是因为许多健康问题都是知道的越早越好,这样才能更好地预防Java 基础《Head First Java(第二版)》- 链接:https://pan.baidu.com/s/17LNLb-1s_k9wR1kK__s1eQ ,提取码:v3ae《Java核心技术卷一(第十版)》- 链接:https://pan.baidu.com/s/1l-pDMOkR832ceA5q2CVZ7g ,提取码:v88g《Java编程思想(第四版)》 - 链接:https://pan.baidu.com/s/1NwOrkSadZuL64DH43_5rKA ,提取码:jgorJava 进阶偏实战,需要打好上面的基础《Java8实战》- 链接:https://pan.baidu.com/s/1P6RBXi5PzRm4zuKgUr14sg ,提取码:q3lo《Java 并发编程实战》- 链接:https://pan.baidu.com/s/1m6ts-HSu2SgFVRgJKKPl6Q ,提取码:i7nz《实战Java高并发程序设计(第2版) 》- 链接:https://pan.baidu.com/s/1tSPNtR1CNGea6gKfXnBklA ,提取码:dad3《Java性能权威指南》- 链接:https://pan.baidu.com/s/1OlHlJ4qyPHQe5QMB6qkLmw ,提取码:vwca《深入理解Java虚拟机:JVM高级特性与最佳实践(第3版)》- 链接:https://pan.baidu.com/s/1ZQi72jiqQzHlFhdkMqJtXg ,提取码:h7n7Java 优化下面主要讲解了Java开发常见的一些规范,使代码更加整洁高效《码出高效:Java开发手册》- 链接:https://pan.baidu.com/s/19sttAcC01G0wg1nqkfmprA ,提取码:zemp《Effective Java(第三版)》- 链接:https://pan.baidu.com/s/1RDaiVNViHvTVVunMkpFecg ,提取码:m9qe《嵩山版 Java 开发手册》- 链接:https://pan.baidu.com/s/1Rgh5tA_yPE-nV9MjPE572w ,提取码:62iw《代码整洁之道》《程序员修炼之道 从小工到专家》- 链接:https://pan.baidu.com/s/1lVoRCEr9NJR2I5rPneXK0g ,提取码:fxwe计算机《TCP-IP详解(卷一、二、三)》- 链接:https://pan.baidu.com/s/1D7luuYvEfTjaC7bh8Tm1Hg ,提取码:pmcj《深入理解计算机系统(原书第3版)》- 链接:https://pan.baidu.com/s/15F6MOyryXUI_l7DLoNbgiA ,提取码:7x0u算法《算法图解》- 链接:https://pan.baidu.com/s/1qbJAJbLInXo-rlPBGLOFUA ,提取码:k9nj《算法珠玑(Java版)》- 链接:https://pan.baidu.com/s/1EQj2sQk1Ys-EU8mwHho_MA ,提取码:7fxt《Leetcode 前 300 题算法题解析(Java)》- 链接:https://pan.baidu.com/s/1BGuMZW2ec4P_cyiv6sicww ,提取码:2jty设计模式《Head First 设计模式》- 链接:https://pan.baidu.com/s/1rxH5Ll38VpHl_j6ts6Q-oQ ,提取码:z6xg《Design-Pattern包教不包会(设计模式包教不包会)》- 链接:https://pan.baidu.com/s/1SDIsamZCv7JKY1zTuNLKtg ,提取码:81ci《设计模式 Java版本》- 链接:https://pan.baidu.com/s/1ikneBCgvAoIk5A-M6snIPg ,提取码:7b98《设计模式:可复用面向对象软件的基础》- 链接:https://pan.baidu.com/s/1CPJUAJp_oryQnKxWCbn8tA ,提取码:7qtiSpring 全家桶《Spring实战(第4版)》- 链接:https://pan.baidu.com/s/1CC1mAD4kZ57SmQRA8NEDXg ,提取码:ei6v《Spring Boot实战 》- 链接:https://pan.baidu.com/s/1QPfW43uN1uTJnsdKzUBDog ,提取码:aukv《Spring Boot 2企业应用实战》- 链接:https://pan.baidu.com/s/1s31xMCJ3WIjN2Koc-hSlyg ,提取码:3apd《JavaEE开发的颠覆者 Spring Boot实战》- 链接:https://pan.baidu.com/s/1TOmImwqszUDYmXc9uALaWg ,提取码:blpp《SpringBoot实战系列》- 链接:https://pan.baidu.com/s/1PPvY5goAI8L3SChfYSaAiw ,提取码:j26wMaven 依赖管理《Maven实战 高清》- 链接:https://pan.baidu.com/s/1MUZOKKjv5F7XY32aUIDaog ,提取码:xj6wMySQL 数据库《MySQL技术内幕(第5版)》- 链接:https://pan.baidu.com/s/1Dv6HXvhmyiGA2Gsn7NBo3w ,提取码:d7pj《MySQL技术内幕:InnoDB存储引擎(第2版)》- 链接:https://pan.baidu.com/s/1-dRbRaERiEJGIg3tgg00_A ,提取码:t4rh《高性能MySQL(第3版)》- 链接:https://pan.baidu.com/s/1yjHTlNcJLTaXjUAv2al0HA ,提取码:v037《Effective MySQL之SQL语句最优化》- 链接:https://pan.baidu.com/s/1IVWZw_JfL8y0KQ290gHT6w ,提取码:cbiy《SQL反模式》- 链接:https://pan.baidu.com/s/1C_jnScaSBw72ET_Qkh5phQ ,提取码:qspw《深入MySQL实战》- 链接:https://pan.baidu.com/s/14IP44JpJruedpOilLTiUog ,提取码:ejia《mybatis-plus 实践及架构原理》- 链接:https://pan.baidu.com/s/1bBVCVy6_YRM0aMeNOAveww ,提取码:joexRedis 数据库《redis设计与实现(第二版)》- 链接:https://pan.baidu.com/s/19mtOtYKLFWoY9fpP4v7MsA ,提取码:7cmx《Redis深度历险:核心原理和应用实践》- 链接:https://pan.baidu.com/s/1tVTucPsmwY2M5HJSNvhpCg ,提取码:oyiyRabbitMQ 消息队列《RabbitMQ实战 高效部署分布式消息队列》- 链接:https://pan.baidu.com/s/11btAl5txQLPd6DcT3hX0FA ,提取码:lmz5《深入RabbitMQ》- 链接:https://pan.baidu.com/s/1F4iCPSJGhBBQzaebC_E_BA ,提取码:nhp0架构《架构探险:从零开始写分布式服务框架》- 链接:https://pan.baidu.com/s/1_Eo4pqAekHVyBlJ7EIayZA ,提取码:lc2t《大型网站技术架构 核心原理与案例分析》- 链接:https://pan.baidu.com/s/1zt4Ee4sUnO4H654ZeuRxTg ,提取码:w1rb
2021年08月11日
112 阅读
0 评论
0 点赞
1
2