《JAVA并发编程的艺术》要点(一)并发编程的挑战

2019-07-26  本文已影响0人  YongtaoHuang

并发编程的目的是为了让程序运行的更快

并发编程面临的挑战

一、上下文切换问题
二、死锁问题
三、资源受限问题

(一)上下文切换

CPU通过给每个线程分配CPU时间片(几十毫秒)来实现多线程。
CPU通过时间片分配算法来循环执行任务。任务从保存到再加载的过程就是一次上下文切换。但是,多线程不一定快,测试代码如下:

/**
 * 并发和单线程执行测试
 */
public class ConcurrencyTest {

    /** 执行次数 */
    private static final long count = 100000000l;

    public static void main(String[] args) throws InterruptedException {
        //并发计算
        concurrency();
        //单线程计算
        serial();
    }

    private static void concurrency() throws InterruptedException {
        long start = System.currentTimeMillis();
        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                int a = 0;
                for (long i = 0; i < count; i++) {
                    a += 5;
                }
            }
        });
        thread.start();
        int b = 0;
        for (long i = 0; i < count; i++) {
            b--;
        }
        thread.join();
        long time = System.currentTimeMillis() - start;
        System.out.println("concurrency :" + time + "ms");
    }

    private static void serial() {
        long start = System.currentTimeMillis();
        int a = 0;
        for (long i = 0; i < count; i++) {
            a += 5;
        }
        int b = 0;
        for (long i = 0; i < count; i++) {
            b--;
        }
        long time = System.currentTimeMillis() - start;
        System.out.println("serial:" + time + "ms" );
    }

}

当count = 100000000l时,输出结果:

concurrency :48ms
serial:74ms

当count = 10000l;时,输出结果:

concurrency :1ms
serial:0ms

从上述试验结果可知:多线程不一定快,使用多线程得在合适的场合。
如何减少上下文切换:
1、无锁并发编程
2、CAS算法(比较并交换)
3、使用最少线程
4、协程

(二)死锁

在多任务系统下,当一个或多个进程等待系统资源,而资源又被进程本身或其它进程占用时,就形成了死锁。死锁会造成系统功能不可用。下面举一个典型的例子来说明死锁问题:

/**
 * 死锁例子
 */
public class DeadLockDemo {

    /** A锁 */
    private static String A = "A";
    /** B锁 */
    private static String B = "B";

    public static void main(String[] args) {
        new DeadLockDemo().deadLock();
    }

    private void deadLock() {
        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (A) {
                    try {
                        System.out.println("线程t1获得A锁,准备去拿B锁");
                        Thread.sleep(2000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    synchronized (B) {
                        System.out.println("线程t1获得B锁");
                    }
                }
            }
        });

        Thread t2 = new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (B) {
                    System.out.println("线程t2获得B锁,准备去拿A锁");
                    synchronized (A) {
                        System.out.println("线程t2获得A锁");
                    }
                }
            }
        });
        t1.start();
        t2.start();
    }

}

输出结果:

线程t1获得A锁,准备去拿B锁
线程t2获得B锁,准备去拿A锁

避免死锁的几个方法:
1、避免一个线程同时获得多个锁
2、尽量每个锁占用一个资源
3、用定时锁替代内部锁机制
4、对于数据库锁,加解锁必须在一个数据库连接里,否则会解锁失败

(三)资源受限

资源限制是指在并发编程时,程序的执行速度受限于软硬件资源。
将某段串行代码并发执行,因为资源受限,代码实际上并发度不高,反而增加了上下文切换和资源调度的时间。

硬件资源

带宽的上传下载,硬盘读写速度,CPU的处境速度等
解决方法:服务器集群

软件资源

数据库连接数,socket连接数等
解决方法:使用资源池将资源复用

根据不同的资源限制调整程序的并发度
上一篇下一篇

猜你喜欢

热点阅读