01Java并发编程的艺术之并发编程的挑战

167 阅读2分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第6天,点击查看活动详情

一、上下文切换

1、并发编程真的快吗?什么是上下文切换?

答案是不一定,根据测试结果,当数据小于百万的时候并发并没有串行快,这是为什么那?单核处理器的多线程并发,其实就是CPU个每个线程分配时间片,当时间片执行完就需要切换另一个线程的时间片来执行,时间片是非常小的单位,所以我们会觉得是并发!而他们发生切换的时候需要保存当前状态,然后在切换,而保存在切换之后的重新加载就是上下文切换,这也是比较浪费时间的!

public class Demo01 {
    /**
     * count = 10000001:
     *  concurrency:31ms,b:-50000005
     *  serial:39ms,b:-50000005,a:50000005
     *
     * count = 1000001;
     *  concurrency:17ms,b:-500005
     *  serial:12ms,b:-500005,a:500005
     *      
     * count = 100001;
     *  concurrency:11ms,b:-500005
     *  serial:8ms,b:-500005,a:500005
     */
    private static final long count = 1000001;

    public static void concurrency() throws InterruptedException {
        long startTime = System.currentTimeMillis();


        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                int a = 0;
                for (int i = 0; i < count; i++){
                    a += 5;
                }
            }
        });

        thread.start();

        int b = 0;
        for (int i = 0; i < count; i++){
            b -= 5;
        }

        thread.join();
        long time = System.currentTimeMillis() - startTime;
        System.out.println("concurrency:" + time + "ms,b:" + b);
    }

    public static void serial(){
        long startTime = System.currentTimeMillis();

        int a = 0;
        for (int i = 0; i < count; i++){
            a += 5;
        }

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

    public static void main(String[] args) throws InterruptedException {
        concurrency();
        serial();
    }
}

2、如何减少上下文切换?

  • 使用无锁并发编程,没有锁的话就会减少线程之间的竞争,从而减少上下文切换!
  • 使用CAS算法,使用CAS更新值无需加锁!
  • 使用最少线程,尽量减少线程的时候从而避免上下文切换!
  • 协程,可以试试单线程维护多个任务的切换!



二、死锁

1、什么是死锁?

线程之间都拿着对方需要的资源,并且都不是释放资源,从而导致死锁!例如两个共享资源A和B,第一个线程先拿到A后锁住A,准备拿B,但是在第一个线程准备拿B的时候,第二个线程已经拿到B并且锁住,然后准备去拿资源A,这时候就发生了死锁了!这就是死锁!

public class Demo02 {
    private static String a = "a";
    private static String b = "b";
    public static void main(String[] args) {
        Thread thread_A = new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (a){
                    try {
                        TimeUnit.SECONDS.sleep(2000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    synchronized (b){
                        System.out.println(Thread.currentThread().getName());
                    }
                }
            }
        },"Thread_A");

        Thread thread_B = new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (b){
                    synchronized (a){
                        System.out.println(Thread.currentThread().getName());
                    }
                }
            }
        }, "Thread_B");

        thread_A.start();
        thread_B.start();
    }

}

2、如何避免死锁

  • 避免一个线程获取多个锁
  • 避免一个线程同时占用多个资源
  • 使用定时锁,例如lock.tryLock();
  • 使用数据库锁,加解锁都是在一个数据库连接当中!



感谢大家的阅读,我是Alson_Code,一个喜欢把简单问题复杂化,把复杂问题简单化的程序猿! ❤