zoukankan      html  css  js  c++  java
  • 并发编程-问题解决

    概述

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

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

      并发编程:在多线程环境下,应用程序的执行;

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

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

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

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

        3.间断任务执行;

    并发编程中待解决的问题

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

        即使是单核处理器也支持多线程执行代码,CPU通过给每个线程分配CPU时间片来实现这个机制;

        时间片是CPU分配给各个线程的时间,因为时间非常短,所以CPU通过不停地切换线程执行,让我们感觉多个线程是同时执行的,时间片一般是十几毫秒;

        CPU通过时间片分配算法来循环执行任务,当前任务执行一个时间片后会切换到下一个任务。但是,在切换前会保存上一个任务的状态,以便下一次切换回这个任务时,可以再加载这个任务的状态。所以任务从保存到再加载的过程就是一次上下文切换;

        这就像我们同时读两本书,当我们读一本英文的技术书时,发现某个单词不认识,于是打开中英文字典,但是在的放下英文技术书之前,大脑必须先记住这本书读到了多少页的第多少行,等查完单词之后,能够继续读这本书。这样的切换是会影响读书效率的,同样上下文切换回影响多线程的执行速度;

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

          1.无锁并发编程;

          2.CAS;

          3.使用最少线程数量;

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

      2.并发编程中死锁问题

        锁是一个非常有用的工具,运用场景非常多,因为它使用起来非常简单,而且易于理解。但同时它也会带来一些困扰,那就是可能会引起死锁,一旦产生死锁,就会造成系统功能不可用; 

    package com.wn.demo01;
    
    public class DeadLockDemp {
        //线程
        private static final Object obj_A =new Object();
        private static final Object obj_B=new Object();
    
        public static void main(String[] args){
            //A线程
            new Thread(new Runnable() {
                @Override
                public void run() {
                   synchronized (obj_A){
                       System.out.println("使用obj_A线程!");
                       //延迟时间
                       try {
                           Thread.sleep(100);
                       } catch (InterruptedException e) {
                           e.printStackTrace();
                       }
                       //抢占B线程
                       synchronized (obj_B){
                           System.out.println("抢占B线程");
                       }
                   }
                }
            }).start();
    
            //B线程
            new Thread(new Runnable() {
                @Override
                public void run() {
                    synchronized (obj_B){
                        System.out.println("使用obj_B线程!");
                        //延迟时间
                        try {
                            Thread.sleep(100);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        //抢占A线程
                        synchronized (obj_A){
                            System.out.println("抢占A线程");
                        }
                    }
                }
            }).start();
        }
    }

        这段代码会引起死锁,是线程A和线程B互相等待对象释放锁;

        一旦出现死锁,业务是可感知的,因为不能继续提供服务了,可以通过一些工具查看到底哪个线程出现了问题,一下是DeadLockDemp类的第43行和23行引起了死锁;

          

        避免死锁的方法:

          1.避免一个线程同时获取多个锁;

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

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

          4.破坏请求和保持条件:在申请资源时,一次性将资源都申请到;

          5.破坏不可占用条件:抢占资源如果不满足,那就释放所有资源,以后如果需要则再次申请即可;

          6.破坏循环等待条件;

      3.线程安全问题

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

    package com.wn.demo01;
    
    import java.util.concurrent.CountDownLatch;
    
    public class UnsafeThread {
        //资源
        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(new Runnable() {
                    @Override
                    public void run() {
                        for (int j=0;j<100;j++){
                            inCreate();
                            try {
                                Thread.sleep(100);
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                            //每个线程执行完毕,让计数-1
                            countDownLatch.countDown();
                        }
                    }
                }).start();
            }
            //等待计算器为0或者小于执行await下面的代码
            countDownLatch.await();
            System.out.println(num);
        }
    }

        

         以上代码会造成线程不安全问题,每次读取的数据不一致;

        解决线程不安全问题:

          1.同步方法

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

            使用synchronized修改的方法叫做同步方法,保证该线程执行该方法的时候,其他线程只能等着;

            同步锁:1.非static方法,同步锁是this;

                 2.static方法,使用当前方法所在的字节码对象;

            注意:synchronized不能修饰run方法,修饰之后,一个线程就执行了所有的功能,线程出现串行,相当于单线程; 

          2.同步代码块  

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

            synchronized(同步锁){

            }

            对象的同步锁只有一个概念,可以想象在对象上标记一个锁;

            java程序运行可以使用任何对象作为同步监听对象,但是一般我们将当前i并发访问的共同资源(多个线程同步共享的资源对象)所谓同步监听对象;

            注意:在任何时候,只允许一个线程拥有同步锁,谁拿到锁就进入代码块,其他线程只能在外面等;

          3.ReentrantLock(锁机制)

            锁是一种通过多个线程控制对共享资源的访问工具。通常,一个锁提供对共享资源的独占访问:在一个时间只有一个线程可以获取锁和所有访问共享资源,需要先获得锁;

    package com.wn.demo01;
    
    import java.util.concurrent.CountDownLatch;
    import java.util.concurrent.locks.ReentrantLock;
    
    public class UnsafeThread {
        //资源
        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(new Runnable() {
                    @Override
                    public void run() {
                        for (int j=0;j<100;j++){
                            inCreate();
                            try {
                                Thread.sleep(100);
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                            //每个线程执行完毕,让计数-1
                            countDownLatch.countDown();
                        }
                    }
                }).start();
            }
            //等待计算器为0或者小于执行await下面的代码
            countDownLatch.await();
            System.out.println(num);
        }
    }
  • 相关阅读:
    Known Notation括号匹配类问题(2014年ACM/ICPC 亚洲区域赛牡丹江)
    01串LIS(固定串思维)--Kirk and a Binary String (hard version)---Codeforces Round #581 (Div. 2)
    大学生究竟哪种姿势查成绩
    让你的手机缓存视频在电脑上播放
    亲,你的多功能工具箱到了,麻烦签收一下
    真WIFI密码查看器
    爱剪辑永久vip版爱要不要
    这两款ios和Android的免费影视神器最近火爆了
    全球免费直播软件爱要不要
    一次性给你上万款黑科技软件,留着自己悄悄用吧。
  • 原文地址:https://www.cnblogs.com/wnwn/p/12517369.html
Copyright © 2011-2022 走看看