第一章 并发编程的挑战

Posted hzzjj

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了第一章 并发编程的挑战相关的知识,希望对你有一定的参考价值。

 

挑战一:上下文切换

多线程一定比单线程快么?

public class ConcurrencyTest {

    private static final long count = 10001;

    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, b = " + b);
    }

    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, b = " + b + ", a=" + a);
    }
}

输出

技术分享图片

答案是并不一定,当测试量达到一百万的时候,并发才能比串行优势点(本代码环境结果);

线程创建和上下文切换都是需要开销的。

如何减少上下文的切换?

  • 无锁并发编程:可以使用一些方法避免使用锁,如将数据的ID按照Hash算法取模分段,不同的线程处理不同段的数据。
  • CAS算法:Java的Atomic包使用CAS算法更新数据,而不需要加锁。
  • 使用最少线程:避免创建不需要的线程。
  • 协程:在单线程里实现多任务的调度,并在单线程里维持多个任务间的切换。

 

挑战二:死锁

public class DeadLockDemo {
    
    private static String A = "A";
    private static String B = "B";

    public static void main(String[] args) {
        new DeadLockDemo().deadLock();
    }
    
    private void deadLock(){
        Thread thread1 = new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (A){
                    try {
                        Thread.sleep(2000);
                    } catch (InterruptedException e){
                        e.printStackTrace();
                    }
                    synchronized (B){
                        System.out.println("1");
                    }
                }
            }
        });

        Thread thread2 = new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (B){
                    synchronized (A){
                        System.out.println("2");
                    }
                }
            }
        });
        thread1.start();
        thread2.start();
    }
}

避免死锁的几个常见方法:

  • 避免一个线程同时获取多个锁
  • 避免一个线程在锁内同时占用多个资源,尽量保证每个锁只占一个资源
  • 尝试使用定时锁,使用lock.tryLock(timeout)来替代使用内部锁机制
  • 对于数据库锁,加锁和解锁必须在一个数据库连接里

 

 

挑战三:资源限制的挑战

(1) 什么是资源限制?

指在进行并发编程时,程序的执行速度受限于计算机硬件资源或软件资源。

(2) 资源限制引发的问题?

在并发编程中,将代码执行速度加快的原则是将代码中串行的部分变成并发执行,但是如果将某段串行的代码并发执行,因为受限于资源,仍然在串行执行,这时候程序不仅不会加快执行,反而会更慢,因为增加了上下文切换和资源调度的时间。

(3)如何解决资源限制的问题?

对于硬件限制,可以考虑集群并行执行程序。既然单机的资源有限制,就让程序在多机上运行。比如使用ODPS、Hadoop或者自己搭建服务器集群;

对于软件限制,可以考虑使用资源池将资源复用。比如使用连接池将数据库和Socket连接复用,或者在调用对方webService接口获取数据时,只建立一个连接。

(4)如何在资源限制的情况下进行并发编程?

根据不同的资源限制调整程序的并发度,比如下载文件程序依赖于两个资源——宽带和硬盘读写速度。有数据库操作时,设计数据库连接数,如果SQL语句执行非常快,而线程的数量比数据库连接数大很多,则某些线程会被阻塞,等待数据库连接。

 

以上是关于第一章 并发编程的挑战的主要内容,如果未能解决你的问题,请参考以下文章

java并发编程实战第一章

java并发编程实战第一章

JAVA并发编程艺术 一(并发编程的挑战)

[Java 并发] Java并发编程实践 思维导图 - 第一章 简单介绍

多线程上下文切换

《Java并发编程实战》第一章