zoukankan      html  css  js  c++  java
  • 并发编程(一):并发编程中的一些问题和死锁

    串行和并行:

    串行:一个线程在处理操作

    并行:多个线程在处理同一个操作


    什么叫做并发编程:

    在多线程环境下,应用程序的执行

    并发编程的目的:充分运用到资源,提高程序的效率

    什么情况下用到并发编程:

    1.在线程阻塞时,导致应用程序停止

    2.处理任务时间过长时,可以创建子任务,来进行分段处理

    3.间断任务执行


    一.并发编程中待解决的问题

    1.并发编程中频繁上下文切换的问题

    频繁上下文切换,可能会带来一定的性能开销

    2.如何减少上下文性能开销:

    2.1.无锁并发编程

    2.2.CAS

    2.3.使用最少线程数量

    2.4.协程:在单线程环境下进行多任务的调度,可以在多任务之间进行任务切换


    3.并发编程中死锁问题

    3.1什么是死锁

    死锁是指多个进程因竞争资源而造成的一种僵局(互相等待),若无外力作用,这些进程都将无法向前推进。

    例如,在某一个计算机系统中只有一台打印机和一台输入 设备,进程P1正占用输入设备,同时又提出使用打印机的请求,但此时打印机正被进程P2 所占用,而P2在未释放打印机之前,又提出请求使用正被P1占用着的输入设备

    这样两个进程相互无休止地等待下去,均无法继续执行,此时两个进程陷入死锁状态。

    例如下面的案例

    package com.wish;
    
    public class SiSuo {
        //资源
        private static final Object HAIR_A=new Object();
        private static final Object HAIR_B=new Object();
    
        public static void main(String[] args) {
            //进程P1
            new Thread(()->{
                //我在使用输入设备
                synchronized (HAIR_A){
                    System.out.println("我在使用输入设备,提出使用打印机的请求");
                    //延迟时间
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    //提出使用打印机的请求
                    synchronized (HAIR_B){
                        System.out.println("进程P1使用打印机");
                    }
                }
            }).start();
    
    
            //进程P2
            new Thread(()->{
                //我在使用打印机
                synchronized (HAIR_B){
                    System.out.println("我在使用打印机,提出使用输入设备的请求");
                    //延迟时间
                    try {
                        Thread.sleep(100);      //当前线程休眠,让渡CPU资源
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    //提出使用输入设备的请求
                    synchronized (HAIR_A){
                        System.out.println("进程P2使用输入设备");
                    }
                }
            }).start();
        }
    }
    

      

    结果:就是双方互相占有进程没有停止一直耗着

    3.2死锁产生的原因

    3.2.1.系统资源的竞争导致系统资源不足,以及资源分配不当,导致死锁。

    3.2.2.进程在运行过程中,请求和释放资源的顺序不当,会导致死锁。

    3.3死锁的四个必要条件

    互斥条件:一个资源每次只能被一个进程使用,即在一段时间内某 资源仅为一个进程所占有。此时若有其他进程请求该资源,则请求进程只能等待。

    请求与保持条件:进程已经保持了至少一个资源,但又提出了新的资源请求,而该资源 已被其他进程占有,此时请求进程被阻塞,但对自己已获得的资源保持不放。

    不可剥夺条件:进程所获得的资源在未使用完毕之前,不能被其他进程强行夺走,即只能 由获得该资源的进程自己来释放(只能是主动释放)。

    循环等待条件: 若干进程间形成首尾相接循环等待资源的关系

    这四个条件是死锁的必要条件,只要系统发生死锁,这些条件必然成立,而只要上述条件之一不满足,就不会发生死锁。

    3.4如何预防死锁问题

    3.4.1.破坏请求和保持条件:在申请资源时,一次性将资源都申请到
    3.4.2.破坏不可占用条件:抢占资源如何不满足,那就释放所有资源,以后如果再需要则再次申请即可
    3.4.3.破坏循环等待条件

    4.线程安全问题

    多个线程同时操作同一个资源,可能会造成资源数据不安全问题

    列如如下代码

    package com.wish;
    
    import java.util.concurrent.CountDownLatch;
    
    public class ThreadTest {
        //资源
        private static int num=0;
        //计算线程数量
        private static CountDownLatch countDownLatch=new CountDownLatch(10);
        //对资源进行操作
        public static void inCreate(){
            num++;
        }
    
    
        public static void main(String[] args) throws InterruptedException {
            for (int i = 0 ; i < 10 ; i++ ){
                new Thread(()->{
                    for (int j = 0 ; j < 100; j++){
                        inCreate();
                        try {
                            Thread.sleep(100);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    //每一个线程执行完毕,让计数-1
                    countDownLatch.countDown();
                }).start();
            }
            //等待计数器为0或者小于0执行await下面代码
            countDownLatch.await();
            System.out.println(num);
        }
    }
    

      

    结果:它结果应该是1000但是它的结果却是681,就是应有许多个线程对它进行操作,但是互相都不知道别的线程的计算情况

     解决线程不安全问题:

    1.ReentrantLock对‘对资源的操作这一步骤’上一把锁,在执行完后在把锁释放掉

    package com.wish;
    
    import java.util.concurrent.CountDownLatch;
    import java.util.concurrent.locks.ReentrantLock;
    
    public class ThreadTest {
        //资源
        private static int num=0;
        //计算线程数量
        private static CountDownLatch countDownLatch=new CountDownLatch(10);
        private static ReentrantLock reentrantLock = new ReentrantLock();
        //对资源进行操作
        public static  void inCreate(){
            //上锁
            reentrantLock.lock();
            num++;
            reentrantLock.unlock();
    
        }
        public static void main(String[] args) throws InterruptedException {
            for (int i = 0 ; i < 10 ; i++ ){
                new Thread(()->{
                    for (int j = 0 ; j < 100; j++){
                        inCreate();
                        try {
                            Thread.sleep(100);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    //每一个线程执行完毕,让计数-1
                    countDownLatch.countDown();
                }).start();
            }
            //等待计数器为0或者小于0执行await下面代码
            countDownLatch.await();
            System.out.println(num);
        }
    }
    

      

    2.使用synchronized关键字,对操作资源的方法进行修饰 

    package com.wish;
    
    import java.util.concurrent.CountDownLatch;
    import java.util.concurrent.locks.ReentrantLock;
    
    public class ThreadTest {
        //资源
        private static int num=0;
        //计算线程数量
        private static CountDownLatch countDownLatch=new CountDownLatch(10);
        private static ReentrantLock reentrantLock = new ReentrantLock();
        //对资源进行操作
        public static synchronized void inCreate(){
            //上锁
            num++;
        }
        public static void main(String[] args) throws InterruptedException {
            for (int i = 0 ; i < 10 ; i++ ){
                new Thread(()->{
                    for (int j = 0 ; j < 100; j++){
                        inCreate();
                        try {
                            Thread.sleep(100);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    //每一个线程执行完毕,让计数-1
                    countDownLatch.countDown();
                }).start();
            }
            //等待计数器为0或者小于0执行await下面代码
            countDownLatch.await();
            System.out.println(num);
        }
    }
    

      

  • 相关阅读:
    软工课设第一周周五报告
    软工课设第一周周四报告
    软工课设第一周周三报告
    软工课设第一周周二报告
    软工课设第一周周一报告
    团队项目记录4
    团队项目记录3
    团队项目记录2
    jQuery 打气球小游戏 点击气球爆炸效果
    计网第二章:物理层
  • 原文地址:https://www.cnblogs.com/wishsaber/p/12518608.html
Copyright © 2011-2022 走看看