并发编程-问题解决(代码片段)

wnwn wnwn     2023-04-11     226

关键词:

概述

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

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

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

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

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

    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);
    

 

day823.java原子性问题解决方案-java并发编程实战(代码片段)

...,今天学习记录的是关于Java原子性问题解决方案。在并发编程Bug的源头中提到,一个或者多个操作在CPU执行的过程中不被中断的特性,称为“原子性”。理解这个特性有助于分析并发编程Bug出现的原因,例如利用... 查看详情

day823.java原子性问题解决方案-java并发编程实战(代码片段)

...,今天学习记录的是关于Java原子性问题解决方案。在并发编程Bug的源头中提到,一个或者多个操作在CPU执行的过程中不被中断的特性,称为“原子性”。理解这个特性有助于分析并发编程Bug出现的原因,例如利用... 查看详情

解决并发编程之痛的良药--结构化并发编程(代码片段)

解决并发编程之痛的良药--结构化并发编程作者简介:曹家锋,Westar实验室技术专家。Westar实验室(westar.io),成立于2018年,关注于区块链及分布式前沿技术,包括区块链分层架构、二层路由,网络性能、智能合约、PoW优化等。... 查看详情

并发编程-java内存模型:解决可见性与有序性问题(代码片段)

背景我们知道导致cpu缓存导致了可见性问题,编译器优化带来了有序性问题。那么如果我们禁用了cpu缓存与编译器优化,就能够解决问题,但是性能就无法提升了。所以一个合理的方案,就是按照一定规范来禁用缓存和编译器优... 查看详情

java并发编程系列——死锁的解决(代码片段)

接下来我会以一个经典的转帐问题,跟大家一起聊聊死锁,以及如何解决这个问题。假如客户A需要给客户B转账,客户A的账户减少100元,客户B的账户增加100元。我们转化为代码描述:有一个账户类Account,... 查看详情

java并发编程系列——死锁的解决(代码片段)

接下来我会以一个经典的转帐问题,跟大家一起聊聊死锁,以及如何解决这个问题。假如客户A需要给客户B转账,客户A的账户减少100元,客户B的账户增加100元。我们转化为代码描述:有一个账户类Account,... 查看详情

java并发编程线程操作原子性问题(问题业务场景分析|使用synchronized解决线程原子性问题)(代码片段)

文章目录总结一、原子性问题示例二、线程操作原子性问题分析三、使用synchronized解决线程原子性问题总结原子操作问题:线程中,对变量副本count进行自增操作,不是原子操作,首先从工作内存中读取变量副本到执行引擎(操作数栈)... 查看详情

java并发编程与高并发解决方案--安全发布对象(代码片段)

发布对象使一个对象能够被当前范围之外的代码所使用privateString[]states="a","b","c";......publicstaticvoidmain(String[]args)a=newUnsafePushlish();log.info("输出修改前信息");//结果为a,b,ca.getSa 查看详情

原创java并发编程系列04|java内存模型详解(代码片段)

【原创】Java并发编程系列04|Java内存模型详解收录于话题#进阶架构师|并发编程专题12个点击上方“java进阶架构师”,选择右上角“置顶公众号”20大进阶架构专题每日送达思维导图写在前面前面讲解了并发编程的三大核心问题:... 查看详情

day846.并发工具类一些问题-java并发编程实战(代码片段)

并发工具类一些问题Hi,我是阿昌,今天学习记录的是关于并发工具类一些问题的内容。关于JavaSDK提供的并发工具类(JUC),这些工具类都是久经考验的,所以学好用好它们对于解决并发问题非常重要。在... 查看详情

并发编程(学习笔记-共享模型之无锁)-part5(代码片段)

文章目录并发编程-5-共享模型之无锁1.无锁解决线程安全问题2.CAS与volatile2-1CAS2-2volatile2-3为什么无锁效率高2-4CAS特点3.原子整数4.原子引用4-1原子引用的使用4-2ABA问题及解决5.原子数组6.字段更新器7.原子累加器8.LongAdder详解8-1cas锁8... 查看详情

并发编程之内存模型(代码片段)

...的问题。3、内存模型解决什么问题  内存模型解决了并发编程场景中的原子性、可见性和有序性问题。  1)如何解决原子性问题:    在Java中,为了保证原子性,提供了两个高级的字节码指令monitorenter和monitorexit。在s... 查看详情

50_并发编程-线程-事件(代码片段)

一、定义     如果程序中的其他线程需要通过判断某个线程的状态来确定自己下一步的操作,这时线程同步问题就会变得非常棘手。为了解决这些问题,我们需要使用threading库中的Event对象。  事件的方法:1event=Even... 查看详情

java并发编程实战之互斥锁(代码片段)

文章目录Java并发编程实战之互斥锁如何解决原子性问题?锁模型Javasynchronized关键字Javasynchronized关键字只能解决原子性问题?如何正确使用Javasynchronized关键字?锁和受保护资源的合理关联关系死锁预防死锁破坏占有... 查看详情

java并发编程实战之互斥锁(代码片段)

文章目录Java并发编程实战之互斥锁如何解决原子性问题?锁模型Javasynchronized关键字Javasynchronized关键字只能解决原子性问题?如何正确使用Javasynchronized关键字?锁和受保护资源的合理关联关系死锁预防死锁破坏占有... 查看详情

并发编程(协程)(代码片段)

... 随着我们对于效率的追求不断提高,基于单线程来实现并发 查看详情

java并发编程基础(代码片段)

并发与并行并发与并行的区别?并发:同时完成多个任务,无需等待当前任务完成即可执行其它任务。例如解决IO密集型任务。并行:同时在多个CPU中执行多个任务,将任务分为多个部分,在多个CPU中执行多个子任务。用于解决C... 查看详情

并发编程基础(代码片段)

synchronized原理分析synchronized关键字解决的是多个线程之间访问资源的同步性问题,synchronized关键字可以保证被它修饰的⽅法或者代码块在任意时刻只能有⼀个线程执⾏。jdk1.6之前性能⽐较低,Java的线程是映射到操作系统的原⽣... 查看详情