final List listeners = new ArrayList();
或者直接声明专用的锁对象,定义成普通的Object对象即可。
final Object listenersLock = new Object();
2.5. 处理循环中的异常
在异步线程里处理一些定时任务,或者执行时间非常长的批量处理,是经常遇到的需求。我就不止一次看到小伙伴们的程序执行了一部分就停止的情况。
排查到这些中止的根本原因,就是其中的某行数据发生了问题,造成了整个线程的死亡。
我们还是来看一下代码的模板。
volatile boolean run = true;
void loop(){
while(run){
for(Task task: taskList){
//do . sth
int a = 1/0;
}
}
}
在loop函数中,执行我们真正的业务逻辑。当执行到某个task的时候,发生了异常。这个时候,线程并不会继续运行下去,而是会抛出异常直接中止。在写普通函数的时候,我们都知道程序的这种行为,但一旦到了多线程,很多同学都会忘了这一环。
值得注意的是,即使是非捕获类型的NullPointerException
,也会引起线程的中止。所以,时刻把要执行的逻辑,放在try catch中,是个非常好的习惯。
volatile boolean run = true;
void loop(){
while(run){
for(Task task: taskList){
try{
//do . sth
int a = 1/0;
}catch(Exception ex){
//log
}
}
}
}
2.6. HashMap正确用法
HashMap在多线程环境下,会产生死循环问题。这个问题已经得到了广泛的普及,因为它会产生非常严重的后果:CPU跑满,代码无法执行,jstack查看时阻塞在get方法上。
至于怎么提高HashMap效率,什么时候转红黑树转列表,这是阳春白雪的八股界话题,我们下里巴人只关注怎么不出问题。
网络上有详细的文章描述死循环问题产生的场景,大体因为HashMap在进行rehash时,会形成环形链。某些get请求会走到这个环上。JDK并不认为这是个bug,虽然它的影响比较恶劣。
如果你判断你的集合类会被多线程使用,那就可以使用线程安全的ConcurrentHashMap来替代它。
HashMap还有一个安全删除的问题,和多线程关系不大,但它抛出的是ConcurrentModificationException,看起来像是多线程的问题。我们一块来看看它。
Map<String, String> map = new HashMap<>();
map.put(“xjjdog0”, “狗1”);
map.put(“xjjdog1”, “狗2”);
for (Map.Entry<String, String> entry : map.entrySet()) {
String key = entry.getKey();
if (“xjjdog0”.equals(key)) {
map.remove(key);
}
}
上面的代码会抛出异常,这是由于HashMap的Fail-Fast机制。如果我们想要安全的删除某些元素,应该使用迭代器。
Iterator<Map.Entry<String, String>> iterator = map.entrySet().iterator();
while (iterator.hasNext()) {
Map.Entry<String, String> entry = iterator.next();
String key = entry.getKey();
if (“xjjdog0”.equals(key)) {
iterator.remove();
}
}
2.7. 线程安全的保护范围
使用了线程安全的类,写出来的代码就一定是线程安全的么?答案是否定的。
线程安全的类,只负责它内部的方法是线程安全的。如我我们在外面把它包了一层,那么它是否能达到线程安全的效果,就需要重新探讨。
比如下面这种情况,我们使用了线程安全的ConcurrentHashMap来存储计数。虽然ConcurrentHashMap本身是线程安全的,不会再出现死循环的问题。但addCounter函数,明显是不正确的,它需要使用synchronized函数包裹才行。
private final ConcurrentHashMap<String,Integer> counter;
public int addCounter(String name) {
Integer current = counter.get(name);
int newValue = ++current;
counter.put(name,newValue);
return newValue;
}
这是开发人员常踩的坑之一。要达到线程安全,需要看一下线程安全的作用范围。如果更大维度的逻辑存在同步问题,那么即使使用了线程安全的集合,也达不到想要的效果。
2.8. volatile作用有限
volatile关键字,解决了变量的可见性问题,可以让你的修改,立马让其他线程给读到。
虽然这个东西在面试的时候问的挺多的,包括ConcurrentHashMap中队volatile的那些优化。但在平常的使用中,你真的可能只会接触到boolean变量的值修改。
volatile boolean closed;
public void shutdown() {
closed = true;
}
千万不要把它用在计数或者线程同步上,比如下面这样。
volatile count = 0;
void add(){
++count;
}
这段代码在多线程环境下,是不准确的。这是因为volatile只保证可见性,不保证原子性,多线程操作并不能保证其正确性。
直接用Atomic类或者同步关键字多好,你真的在乎这纳秒级别的差异么?
2.9. 日期处理要小心
很多时候,日期处理也会出问题。这是因为使用了全局的Calendar,SimpleDateFormat等。当多个线程同时执行format函数的时候,就会出现数据错乱。
SimpleDateFormat format = new SimpleDateFormat(“yyyy-MM-dd hh:mm:ss”);
Date getDate(String str){
return format(str);
}
为了改进,我们通常将SimpleDateFormat放在ThreadLocal中,每个线程一份拷贝,这样可以避免一些问题。当然,现在我们可以使用线程安全的DateTimeFormatter了。
static DateTimeFormatter FOMATTER = DateTimeFormatter.ofPattern(“MM/dd/yyyy HH:mm:ss”);
public static void main(String[] args) {
ZonedDateTime zdt = ZonedDateTime.now();
System.out.println(FOMATTER.format(zdt));
}
2.10. 不要在构造函数中启动线程
在构造函数,或者static代码块中启动新的线程,并没有什么错误。但是,强烈不推荐你这么做。
因为Java是有继承的,如果你在构造函数中做了这种事,那么子类的行为将变得非常魔幻。另外,this对象可能在构造完毕之前,出递到另外一个地方被使用,造成一些不可预料的行为。
所以把线程的启动,放在一个普通方法,比如start中,是更好的选择。它可以减少bug发生的机率。
End
wait和notify是非常容易出问题的地方,
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新
如果你觉得这些内容对你有帮助,可以添加V获取:vip1024b (备注Java)
技术学习总结
学习技术一定要制定一个明确的学习路线,这样才能高效的学习,不必要做无效功,既浪费时间又得不到什么效率,大家不妨按照我这份路线来学习。
最后面试分享
大家不妨直接在牛客和力扣上多刷题,同时,我也拿了一些面试题跟大家分享,也是从一些大佬那里获得的,大家不妨多刷刷题,为金九银十冲一波!
650274670)]
技术学习总结
学习技术一定要制定一个明确的学习路线,这样才能高效的学习,不必要做无效功,既浪费时间又得不到什么效率,大家不妨按照我这份路线来学习。
[外链图片转存中…(img-sgyjLofH-1711650274670)]
[外链图片转存中…(img-EOi9HOSJ-1711650274671)]
[外链图片转存中…(img-9Czyfhtb-1711650274671)]
最后面试分享
大家不妨直接在牛客和力扣上多刷题,同时,我也拿了一些面试题跟大家分享,也是从一些大佬那里获得的,大家不妨多刷刷题,为金九银十冲一波!
[外链图片转存中…(img-E6tLsTBQ-1711650274671)]
[外链图片转存中…(img-60UQWgJ0-1711650274672)]