38

java多线程,作死等级 & 脑残等级

 4 years ago
source link: https://www.tuicool.com/articles/Jveiu2m
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

许多java开发,都是刚刚接触多线程开发。但即使是有经验的开发,也会陷入很多 多线程 的陷阱。本篇内容,基本上都是一些反例,有些很低级但常见。当你的程序没有得相应的期望,希望本文能帮你了解到其中的微妙之处。

当然,面试时拿来装逼用,也是极好的。

先来10个。

我来评个级

玩命的创建线程池

现象:系统资源耗尽,进程僵死。

原因:每次方法执行,都new一个线程池。代码示例。

小姐姐味道解决方式:共用一个线程池即可。

作死等级:五颗星

脑残等级:五颗星

void doJob(){
    ThreadPoolExecutor exe = new ThreadPoolExecutor(...);
    exe.submit(new Runnable(){...})
}

现象:某个线程一直持有锁而不释放,造成锁泄漏。

原因:未知异常或逻辑导致unlock函数未执行。

小姐姐味道解决方式:始终将unlock函数放在finally中。

作死等级:三颗星

脑残等级:四颗星

private final Lock lock = new ReentrantLock();
void doJob(){
    try{
        lock.lock();
        //do. sth
        lock.unlock();
    }catch(Exception e){
    }
}

忘记同步变量

现象:在某个条件下,抛出IllegalMonitorStateException。

原因:调用wait、notify等,忘记synchronized,或者同步了错误的变量。

小姐姐味道解决方式:调用这些函数之前,要使用同步关键字同步它。

作死等级:两颗星

脑残等级:四颗星

Object condition = new Object();

condition.wait();

HashMap死循环

现象:cpu占用高,发生死循环,使用jstack查看是阻塞在get方法上。

原因:在某种条件下,进行rehash时,会形成环形链。某些get请求会走到这个环上。

小姐姐味道解决方式:多线程环境下,使用ConcurrentHashMap,别犹豫。

作死等级:四颗星

脑残等级:四颗星

给同步的变量重新赋值

现象:不能够达到同步效果,结果是错误的。

原因:非基本类型被重新赋值,会改变锁的指向,不同线程持有的锁可能不一样。

小姐姐味道解决方式:把锁对象声明为final类型。

作死等级:四颗星

脑残等级:三颗星

List listeners = new ArrayList();

void add(Listener listener, boolean upsert){
    synchronized(listeners){
        List results = new ArrayList();
        for(Listener ler:listeners){
        ...
        }
        listeners = results;
    }
}

线程循环未捕获异常

现象:线程作业无法继续运行,不明终止。

原因:未捕获循环中的异常,造成线程退出。

小姐姐味道解决方式:习惯性捕获所有异常。

作死等级:三颗星

脑残等级:三颗星

volatile boolean run = true;
void loop(){
    while(run){
        //do . sth
        int a = 1/0;
    }
}

volatile误作计数器

现象:多线程计数结果有误。

原因:volatile保证可见性,不保证原子性,多线程操作并不能保证其正确性。

小姐姐味道解决方式:直接使用Atomic类。

作死等级:三颗星

脑残等级:两颗星

volatile count = 0;
void add(){
    ++count;
}

错误保护范围

现象:虽然使用了线程安全的集合,但达不到同步效果。

原因:操作要修改多个线程安全的集合,但操作本身不是原子的。

小姐姐味道解决方式:弄明白要保护的代码逻辑域。

作死等级:三颗星

脑残等级:四颗星

private final ConcurrentHashMap<String,Integer> nameToNumber;
private final ConcurrentHashMap<Integer,Salary> numberToSalary;
public int geBonusFor(String name) {
    Integer serialNum = nameToNumber.get(name);
    Salary salary = numberToSalary.get(serialNum);
    return salary.getBonus();
}

再比如下面的错误代码。

Map<String, String> map = Collections.synchronizedMap(new HashMap<String, String>());

if(!map.containsKey("foo"))
    map.put("foo", "bar");

一些老的日期处理类

现象:使用全局的Calendar,SimpleDateFormat等进行日期处理,发生异常或者数据不准确。

原因:这俩东西不是线程安全的,并发调用会有问题。

小姐姐味道解决方式:放在ThreadLocal中,建议使用线程安全的DateTimeFormatter。

作死等级:三颗星

脑残等级:三颗星

SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");

Date dododo(String str){
    return format(str);
}

代码死锁

现象:代码产生死锁和相互等待。

原因:代码满足了下面四个条件:互斥;不可剥夺;请求和保持;循环等待。

小姐姐味道解决方式:破坏这四个条件。或者少用同步。

作死等级:两颗星

脑残等级:一颗星

下面是一段简单的死锁代码。

final Object lock1 = new Object();
final Object lock2 = new Object();
new Thread(new Runnable() {
    @Override
    public void run() {
        sleep(1000);
        synchronized (lock1) {
            synchronized (lock2) {
            }
        }
    }
}).start();
new Thread(new Runnable() {
    @Override
    public void run() {
        synchronized (lock2) {
            sleep(1000);
            synchronized (lock1) {
            }
        }
    }
}).start();

long变量读取无效值

现象:会读取到非设置的值。

原因:long变量读写不是原子的,可能会读到1个变量的高32位和另一个变量的低32位字节。

小姐姐味道解决方式:确保long和double变量的数据正确,可以加上volatile关键字。

作死等级:一颗星

脑残等级:没有星

扩展阅读(jdk10): https://docs.oracle.com/javase/specs/jls/se10/html/jls-17.html#jls-17.7

多线程的使用是及其复杂的,使用低级api出错的概率会成倍增加,对技能要求也较高。所幸,concurrent包使得这个过程方便了很多,但依然存在资源规划和同步失效的问题。小姐姐味道这里一个比较浅显但全面的总结: JAVA多线程使用场景和注意事项简版 ,但健壮的代码还要靠你自己去实践呀。

3MfQJb7.png!web

更多精彩文章。

《微服务不是全部,只是特定领域的子集》

《“分库分表" ?选型和流程要慎重,否则会失控》

这么多监控组件,总有一款适合你

《Linux生产环境上,最常用的一套“vim“技巧》

《使用Netty,我们到底在开发些什么?》

Linux五件套之类的。

《Linux之《荒岛余生》(一)准备篇》

《Linux之《荒岛余生》(二)CPU篇》

《Linux之《荒岛余生》(三)内存篇》

《Linux之《荒岛余生》(四)I/O篇》

《Linux之《荒岛余生》(五)网络篇》

更多请关注。当然也可以关注公众号。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK