Java并发与多线程

锁性能优化

2018-09-29  本文已影响0人  城市里永远的学习者

优化思路:避免死锁、减少锁粒度、锁分离等
一、线程开销
1.处理线程任务外,还要维护多线程环境的特有信息,如:线程本身的元数据,线程的调度,线程上下文的切换等,更多的在于多线程的调度。
二、避免死锁
死锁是多线程特有的问题,在死锁时,线程间互相等待资源,而又不释放锁定的资源,导致一直等待。
一般需要满足以上条件:
1.互斥条件:一个资源只能有一个进程使用
2.请求与保持条件:对已获得的资源不释放
3.不剥夺条件:进程已获得资源,在进程执行完之前,不能强行剥夺
4.循环等待条件:若干线程形成一种头尾相接的循环等待资源
就像形成头尾相连的四个小车一样

package mythreadpool;

    public class DeadThread implements Runnable {

        public String username;
        public Object lock1 = new Object();
        public Object lock2 = new Object();

        @Override
        public void run() {
            // TODO Auto-generated method stub
            if (username.equals("a")) {
                synchronized (lock1) {
                    try {
                        System.out.println("username = " + username);
                        System.out.println(Thread.currentThread().getName());
                        Thread.sleep(3000);
                    } catch (Exception e) {
                        // TODO: handle exception
                        e.printStackTrace();
                    }
                    synchronized (lock2) {
                        System.out.println("按lock1->lock2的顺序执行代码");
                    }
                }
            }
            if (username.equals("b")) {
                synchronized (lock2) {
                    try {
                        System.out.println("username = " + username);
                        System.out.println(Thread.currentThread().getName());
                        Thread.sleep(3000);

                    } catch (Exception e) {
                        // TODO: handle exception
                        e.printStackTrace();
                    }
                    synchronized (lock1) {
                        System.out.println("按lock2->lock1顺序执行代码");
                    }
                }

            }
        }

        public void setFlag(String username) {
            this.username = username;
        }

        public static void main(String[] args) {

            DeadThread dt1 = new DeadThread();
            dt1.setFlag("a");
            Thread t1 = new Thread(dt1);
            t1.start();

            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            dt1.setFlag("b");
            Thread t2 = new Thread(dt1);

            t2.start();
        }

}

以上线程1获得了lock1,接着去请求lock2;线程2获得lock2,接着请求lock1。由于线程2持有lock2不能释放,而线程1去请求lock2无法获得则一直等待;而线程1持有lock1,线程2去请求lock1无法获得则一直等待,这样导致两个线程一直等待争抢资源,循环等待
死锁检测:
1.使用Jconsole,检测线程死锁,可以看到死锁代码行
2.使用线程dump,可以打印输出locked,查找到死锁情况
破死锁,可以破坏导致死锁的四个条件的任何一个。
三、减少锁的持有时间
锁的作用范围越小越精确,比如:

synchronized (lock1) {
                    try {
                        othercode1();
                        synchronized method();
                        othercode2();
                    } catch (Exception e) {
                        // TODO: handle exception
                        e.printStackTrace();
                    }
                }

假设othercode1和othercode2 耗时较长 ,且无需同步,则需要将synchronized加在 需要同步的代码块上。
四、减少锁的粒度
在ConcurrentMap类使用分段锁,将整个大对象拆分成多个segments,然后对每个segment加锁,从而实现高并发,减少整体锁的时间。
五、使用读写锁代替独占锁
读写锁利用了读写锁分类的思想,从而针对读多写少的情况进行性能的优化
六、原子操作
原子操作如 AtomicInteger等采用CAS思想设计,CompareAndSet,是一种无锁的实现

上一篇 下一篇

猜你喜欢

热点阅读