java并发(二)【并发编程的简介和挑战】

论坛 期权论坛 编程之家     
选择匿名的用户   2021-5-23 20:48   11   0

1.并发编程的目的

是为了让程序运行得更快。(但是,并不少启动更多的线程就能让程序最大限度地并发执行)

2.为了实现改目的,可能会面临的问题

2.1 上下文切换的问题

2.2 死锁的问题

2.3 资源的限制(硬件和软件资源的限制)

3.多线程的实现

时间片是CPU分配给各个线程的时间。

CPU通过给每个改线程分配CPU时间片,不停地切换线程的执行。由于时间片时间很短(几十毫秒)。让我们感觉是同时执行的。

上下文的切换:当前任务执行一个时间片后,会切换到下一个任务。

切换前会保存一下任务的状态,以便下次切换回来。

4.上下文切换

4.1 多线程是否一定快

不一定。

并发和串行执行时间demo代码如下:

public class ConcurrentTimeTest {
    private static final long count = 10000000;
//   private static final long count = 1000000000;
    public static void main(String[] args) throws Exception {
        concurrent();
        serial();
    }

    private static void concurrent() 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 = getBValue();

        long currentCostTime = System.currentTimeMillis() - start;
        thread.join();
        System.out.println("currentCostTime:" + currentCostTime + "ms\t" + "b=" + b);

    }

    private static void serial() {
        long start = System.currentTimeMillis();
        int a = 0;
        for (long i = 0; i < count; i++) {
            a += 5;
        }
        int b = getBValue();

        long serialTime = System.currentTimeMillis() - start;
        System.out.println("serialTime:" + serialTime + "ms\ta=" + a + "\tb=" + b);

    }

    private static int getBValue() {
        int b = 0;
        for (long i = 0; i < count; i++) {
            b--;
        }
        return b;
    }
}

得出的结论:数据量达不到百万以上的量级,多线程反而会慢。因为多线程有线程创建和上下文的切换

4.2 减少上下文切换的方法

(1)无锁并发编程。一批数据根据hash算法取模,不同段,交给不同线程去处理。

(2)CAS算法。Java的Atomic包,使用CAS算法来更新数据,无需加锁

(3)使用最少线程。任务很少却创建了很多线程来处理。会导致大量的线程处于等待状态

(4)协程。单线程内实现多任务的调度。

5.死锁

死锁demo如下:

public class DeadLockTest {
    private static final String lockA = "A";
    private static final String lockB = "B";

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

    private static void deadLock(){
        Thread thread1 = new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (lockA){
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    synchronized (lockB){
                        System.out.println(lockB+"---in--"+lockA);
                    }
                }
            }
        });

        Thread thread2 = new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (lockB){
                    synchronized (lockA){
                        System.out.println(lockA+"---in---"+lockB);
                    }
                }
            }
        });

        thread1.start();
        thread2.start();
    }

}

现象: thread1和tread2,两个线程相互等待对方释放锁。

如何避免死锁?

1.避免一个线程获取多个锁。

2.避免一个线程在锁内同时占用多个资源,尽量保证每个锁只占用一个资源。

3.尝试使用定时锁,使用lock.tryLock(timeout)来代替使用内部锁机制。

4.对于数据库锁,加锁和解锁必须在同一个数据库连接里,否则会出现解锁失败的情况。

6. 资源的限制

1.硬件和软件资源的限制

硬件的限制有宽带上传和下载的速度,硬盘读写速度和CPU的处理速度等。

软件资源的限制。数据库的连接数和socket连接数等。

2.资源限制可能导致的问题

并发编程提高执行效率,根本上是把串行的部分改变为并行。但是,资源有限的情况下,仍然会是串行执行。在加上线程的创建和上下文的切换,效率会极其低下。

3.解决资源限制的问题

硬件资源限制的话,可以使用集群来并行执行。数据id%机器数得到机器号,然后,对应机器执行对应的数据。

软件资源限制的话,可以考虑用连接池对数据库连接等连接复用。

参考资料:

《 Java并发编程的艺术》

分享到 :
0 人收藏
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

积分:3875789
帖子:775174
精华:0
期权论坛 期权论坛
发布
内容

下载期权论坛手机APP