龙哥带你学java编程(一)-----关于线程并发死锁问题

来源:互联网 发布:啥录屏软件好 编辑:程序博客网 时间:2024/06/03 10:54

为什么要使用并发?

其实并发就是为了提高程序的运行效率也就是代码的执行速度,但是并发也是有一定的弊端,多线程就是不一定比单线程高效,并且并发编程更容易出现错误,如果是要实现正确并且是高效的的并发,就要在开发过程中注意一些问题:注意CPU的切换(也就是上下文的切换),死锁、资源的限制。

一、对于 第一个问题也就是CPU的切换或者是上下文的切换会带来额外的开销

       1、线程的运行机制介绍:

          (1)、CPU 在某一个时刻只能执行一条线程

          (2)、操作系统给每条线程分配不同长度的时间片

          (3)、操作系统在大量的线程中随机的选取一条线程来进行执行

          (4)、如果任意一条线程用完自己的时间片之后,即使任务没有完成,操作系统也会剥夺它的执行权,然后随机切换到另一条线程来进行执行

       2、CPU切换或者上下文切换以及切换过程详解

           (1)、 当一条线程的时间片用完后,操作系统会暂停该线程,并保存该线程相应的信息,然后再随机选择一条新线程去执行,这个过程就称为“线程的上下文切换”或者是CPU切换。

           (2)、暂停正在执行的线程---------保存该线程的相关信息----------从就绪队列中随机选一条线程-----------读取该线程的上下文信息,继续执行

      3、上下文切换相关问题以及如何减少上下文切换

           (1)、进行上下文切换的时候需要保存当前线程的执行状态,并且加载新的线程先前的状态,如果上下文频繁的切换的话,CPU在上下文切换的事件占比就会上升,相反处理任务的时间占比就会减少,所以为了提高并发程序的执行效率,最好让CPU把时间花在处理人文上,而不是频繁的上下文切换上面,因此需要减少上下文切换的时间,提高CPU处理任务的时间

            (2)、减少CPU上下文切换的时间

                    2.1、减少线程的数量。由于一个CPU每个时刻只能执行一条线程,而傲娇的我们又想让程序并发执行,操作系统只好不断地进行上下文切换来使我们从感官上觉得程序是并发执的行。因此,我们只要减少线程的数量,就能减少上下文切换的次数。 然而如果线程数量已经少于CPU核数,每个CPU执行一条线程,照理来说CPU不需要进行上下文切换了,但事实并非如此。

                    2.2、控制同一把锁上的线程的数量如果多条线程共用同一把锁。那么当一条线程获得锁后,其他线程就会被阻塞;当该线程释放锁后,操作系统会从被阻塞的线程中选一条执行,从而又会出现上下文切换。因此,减少同一把锁上的线程数量也能减少上下文切换的次数。

                    2.3、采用无锁的并发编程  。我们知道,如果减少同一把锁上线程的数量就能减少上下文切换的次数,那么如果不用锁,是否就能避免因竞争锁而产生的上下文切换呢? 答案是肯定的!但你需要根据以下两种情况挑选不同的策略:

    1. 需要并发执行的任务是无状态的:HASH分段
      所谓无状态是指并发执行的任务没有共享变量,他们都独立执行。对于这种类型的任务可以按照ID进行HASH分段,每段用一条线程去执行。
    2. 需要并发执行的任务是有状态的:CAS算法
      如果任务需要修改共享变量,那么必须要控制线程的执行顺序,否则会出现安全性问题。你可以给任务加锁,保证任务的原子性与可见性,但这会引起阻塞,从而发生上下文切换;为了避免上下文切换,你可以使用CAS算法, 仅在线程内部需要更新共享变量时使用CAS算法来更新,这种方式不会阻塞线程,并保证更新过程的安全性。

二、第二个问题,如果并发不当的话可能会产生死锁

         1、什么是死锁?

                指多个线程互相等待被对方占用的资源,就会产生死锁

             死锁的实例:

class DeadLock {

    // 锁A

    private ObjectlockA;

    // 锁B

    private ObjectlockB;

 

    // 第一条线程

    Thread t1 = newThread(new Runnable(){

        void run (){

            synchronized(lockA) {

               Thread.sleep(5000);

                synchronized(lockB) {

                   System.out.println("线程1");

                }

            }

        }

    }).start();

 

    // 第二条线程

    Thread t2 = newThread(new Runnable(){

        void run (){

            synchronized(lockB) {

               Thread.sleep(5000);

                synchronized(lockA) {

                   System.out.println("线程2");

                }

            }

        }

    }).start();

}

          解决办法:     
  • 不要在一条线程中嵌套使用多个锁;
  • 不要在一条线程中嵌套占用多个计算机资源;
  • 给锁和资源加超时时间 
  • 如果你非要在一条线程中嵌套使用多个锁或占用多个资源,那你需要给锁、资源加超时时间,从而避免无限期的等待。

三、计算机也就是硬件资源也会限制并发

               理解误区:线程越多越好,速度越快

               在并发编程中,并不是线程越多越好,相反线程过多的时候执行效率会变的很低

原因:

        1、线程多了会导致上下文切换增多,CPU花在上下文切换的时间增多后,花在处理任务上的时间自然就减少了

        2、计算机资源会限制程序的并发度。

例如:假如网入口带宽10M,你写了个多线程下载的软件,同时开100条线程下载,那每条线程平均以每秒100k的速度下载,然而100条线程之间还要不断进行上下文切换,所以你还不如只开5条线程,每条平均2M/s的速度下载。再比如:数据库连接池最多给你用10个连接,然而你却开了100条线程进行数据库操作,那么当10个用完后其他线程就要等待,从而操作系统要在这100条线程间不断进行上下文切换;所以与其这样还不如只开10条线程,减少上下文切换的次数

     资源又是什么?

                            资源分为硬件资源和软件资源:

其中硬件资源:

                           硬盘读写速度

                           网盘带宽

软件资源:

                         Socket套接字连接数

                         数据库连接数

                        线程的条数

解决资源限制的方法;

                                        提高电脑的配置

                                        限制线程的条数



原创粉丝点击