精华内容
下载资源
问答
  • 多线程并发执行及解决方法

    千次阅读 2019-02-02 13:10:49
    用一个案例来说明:假如我们要实现一个售票的小功能,用3个线程售出共2000张票。 初始模型为: package com.test7; public class synchronizedTest { public static void main(String [] args){ TicketWindow ...

    用一个案例来说明:假如我们要实现一个售票的小功能,用3个线程售出共2000张票。

    初始模型为:

    package com.test7;
    
    public class synchronizedTest {
        public static void main(String [] args){
            TicketWindow tw = new TicketWindow();
            Thread t1 = new Thread(tw);
            Thread t2 = new Thread(tw);
            Thread t3 = new Thread(tw);
            t1.start();
            t2.start();
            t3.start();
        }
    
    }
    
    class TicketWindow implements Runnable{
        int nums = 2000;
    
        @Override
        public void run() {
            while(true){
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                if(nums>0){
                    System.out.println(Thread.currentThread().getName()+"当前正在售出弟"+nums+"张票");
                    nums--;
                }
                else{
                    break;
                }
            }
        }
    }
    

     代码运行结果为:

    但是我们发现这3个线程会出现同时售出一张票的情况,这是怎么回事呢?以售出第1766张票为例,因为这3个线程是多线程并发执行的,当其中一个线程Thread-2访问第1766张票时,进入if语句,执行完

    System.out.println(Thread.currentThread().getName()+"当前正在售出弟"+nums+"张票");

    这条语句而还未执行nums--时,另一个线程Thread-1也开始访问第1766张票,进入了if语句,开始执行

    System.out.println(Thread.currentThread().getName()+"当前正在售出弟"+nums+"张票");

    此时便会同时售出两张1766张票。

    我们知道,这种线程是极其危险的,那么怎么解决,让线程变得安全呢?

    其实很简单,只需要加一个对象锁,什么是对象锁呢?别急,往下看。

    如果我们把上面线程并发执行的过程抽象成上厕所的话,就一个马桶,很多个人在外面排队,如果两个或者多个人程同时上一个厕所,就很容易出现问题,这种线程是不安全的。要解决问题只需要在门上安一把锁,必须等上一个人解决了下一个人才能进入,要解决的一个一个来。(扩展一下,会不会有一直解决不了的情况呢?会,比如当一个线程a在A厕所中执行线程,线程b等a解决完了才能进入A厕所,而a执行过程中要调用B厕所,而B厕所正好有人在里面了,这个人正好是B。这样的话,线程A永远结束不了,就出现了死锁问题)

    我们解决上述问题的方法就是加个对象锁,对象锁有0和1两种状态,默认情况为1,代表厕所里没人,线程可以进入。一旦线程进入,她就会变成0---有人的状态,其他线程不能进入,不能进入的线程会进入线程等待池中(blocked阻塞状态)。任何对象都可以充当对象锁,一般用this充当对象锁。

    代码是

    synchronized (Object) {要线程同步的代码块}

    最终修改代码为:

    package com.test7;
    
    public class synchronizedTest {
        public static void main(String [] args){
            TicketWindow tw = new TicketWindow();
            Thread t1 = new Thread(tw);
            Thread t2 = new Thread(tw);
            Thread t3 = new Thread(tw);
            t1.start();
            t2.start();
            t3.start();
        }
    
    }
    
    class TicketWindow implements Runnable{
        int nums = 2000;
    
        @Override
        public void run() {
            while(true){
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (this) {
                    if (nums > 0) {
                        System.out.println(Thread.currentThread().getName() + "当前正在售出弟" + nums + "张票");
                        nums--;
                    } else {
                        break;
                    }
                }
            }
        }
    }
    

    运行结果为:

    展开全文
  • C++多线程并发(一)--- 线程创建与管理

    万次阅读 多人点赞 2020-03-16 22:21:32
    简单来说,并发指的是两个或个独立的活动在同一时段内交替发生。与并发相近的另一个概念是并行,并行则强调的是个独立的活动在同一时刻点同时发生。 二、为什么使用并发 在应用程序中使用并发的原因主要有两个:...

    前言

    我们都听说过摩尔定律:预计18个月会将芯片的性能提高一倍。早期的计算机时钟频率较低,比如1985年intel 80386 工作频率只有20 MHZ,提升CPU 核心的时钟频率带来的性能收益更大,到2006年Intel Core 2 处理器已经能够达到3.5 GHZ 的工作频率了。从2007年开始,CPU 时钟频率的提升就变得缓慢了,主要因为CPU 的功耗随时钟频率呈幂律增长,需要根据散热技术和制程工艺在性能与功耗间寻求平衡,既然CPU 时钟频率提升有限了,怎么继续按照摩尔定律提升性能呢?

    CPU 是用来处理计算任务的,想要在单位时间内处理更多的计算任务,除了提升单核心的时钟频率让其计算的更快之外,还可以增加CPU 核心数,让多个CPU 核心协同计算,CPU 开始往多核心方向发展,到2019年AMD EPYC 2 代已经达到64核心128线程了。为了充分发挥多核心CPU 的性能,操作系统和编程语言对并发执行的支持越来越好,各种编程语言也陆续提供了并发编程的函数库,比如C++11 就新增了并发编程的线程支持库。我们想要让多核CPU 更好的发挥性能,更高效的为我们的程序服务,掌握并发编程思想还是很有必要的。

    一、何为并发

    刚开始接触计算机编程语言时,我们编写一个程序,在main入口函数中调用其它的函数,计算机按我们设定的调用逻辑来执行指令获得结果。如果我们想在程序中完成多个任务,可以将每个任务实现为一个函数然后根据业务逻辑逐个调用。但如果我们想让多个任务几乎同时执行(时间间隔很小,我们感觉是同时执行的一样),比如一边放歌一边显示歌词,恐怕实现起来就会有明显的顿挫感(比如先播放一句歌声,然后显示一行歌词),影响交互体验。

    随着我们对计算性能的要求越来越高,多核心处理器很快普及流行。如果我们想让自己开发的程序更高效的运行,自然要充分发挥多核心处理器的优势。在多核心处理器上同时运行多个任务,比在单核心处理器上顺序执行多个任务高效的多。像单片机这种单核心处理器,在任务较多或者多个任务需要几乎同时执行时,也需要应用多任务并发编程提高对包括处理器在内的各硬件资源的利用效率。

    1.1 并发与并行

    说了这么多,那什么是并发呢?简单来说,并发指的是两个或多个独立的活动在同一时段内发生。并发在生活中随处可见:比如在跑步的时候同时听音乐,在看电脑显示器的同时敲击键盘等。

    与并发相近的另一个概念是并行。它们两者存在很大的差别,图示如下:

    • 并发:同一时间段内可以交替处理多个操作,强调同一时段内交替发生。
      并发
    • 并行:同一时刻内同时处理多个操作,强调同一时刻点同时发生。
      并行

    1.2 硬件并发与任务切换

    既然并发是在同一时间段内交替发生即可,不要求同时发生,像单片机上的单核处理器也是可以支持并发多任务处理的,所以有单片机上跑的RTOS(Real-time operating system)诞生。单核心处理器上的多任务并发是靠任务切换实现的,跟多核处理器上的并行多任务处理还是有较大区别的,但对处理器的使用和多任务调度工作主要由操作系统完成了,所以我们在两者之间编写应用程序区别倒是不大。下面再贴个直观的图示:

    • 双核处理器并行执行(硬件并发)对比单核处理器并发执行(任务上下文切换

    并行与并发

    • 双核处理器均并发执行(一般任务数远大于处理器核心数,多核并发更常见)

    双核并发

    1.3 多线程并发与多进程并发

    前面一直在聊多任务并发,但计算机术语中用得更多的是线程与进程,三者的主要区别如下:

    • 任务:从我们认知角度抽象出来的一个概念,放到计算机上主要指由软件完成的一个活动。一个任务既可以是一个进程,也可以是一个线程。简而言之,它指的是一系列共同达到某一目的的操作。例如,读取数据并将数据放入内存中。这个任务可以作为一个进程来实现,也可以作为一个线程(或作为一个中断任务)来实现。
    • 进程:资源分配的基本单位,也可能作为调度运行的单位。可以把一个进程看成是一个独立的程序,在内存中有其完备的数据空间和代码空间。一个进程所拥有的数据和变量只属于它自己。例如,用户运行自己的程序,系统就创建一个进程,并为它分配资源,包括各种表格、内存空间、磁盘空间、I/O设备等。然后,把该进程放人进程的就绪队列。进程调度程序选中它,为它分配CPU以及其它有关资源,该进程才真正运行。所以,进程是系统中的并发执行的单位。
    • 线程:执行处理器调度的基本单位。一个进程由一个或多个线程构成,各线程共享相同的代码和全局数据,但各有其自己的堆栈。由于堆栈是每个线程一个,所以局部变量对每一线程来说是私有的。由于所有线程共享同样的代码和全局数据,它们比进程更紧密,比单独的进程间更趋向于相互作用,线程间的相互作用更容易些,因为它们本身就有某些供通信用的共享内存:进程的全局数据。

    由上面的定义可以看出,一个进程和一个线程最显著的区别是:线程有自己的全局数据。线程存在于进程中,因此一个进程的全局变量由所有的线程共享。由于线程共享同样的系统区域,操作系统分配给一个进程的资源对该进程的所有线程都是可用的,正如全局数据可供所有线程使用一样。

    在Mac、Windows NT等采用微内核结构的操作系统中,进程的功能发生了变化:它只是资源分配的单位,而不再是调度运行的单位。在微内核系统中,真正调度运行的基本单位是线程。因此,实现并发功能的单位是线程。在Linux系统中,线程的实现和进程并不特别区分,线程只不过是一种特殊的进程。多进程并发编程与多线程并发编程的区别主要在有没有共享数据,多进程间的通信较复杂且代价较大,主要的进程间通信渠道有管道、信号、文件、套接字等。由于C++没有提供进程间通信的原生支持,后续主要介绍多线程并发编程,和多线程间的同步与通信。
    并发进程与并发线程间通信
    多任务与多线程

    二、如何使用并发

    2.1 为什么使用并发

    在应用程序中使用并发的原因主要有两个:关注点分离和性能。事实上,我甚至可以说它们差不多是使用并发的唯一原因;当你观察的足够仔细时,一切其他因素都可以归结到这两者之一(或者可能是二者兼有)。

    • 关注点分离:通过将相关的代码放在一起并将无关的代码分开,可以使你的程序更容易理解和测试,从而减少出错的可能性。你可以使用并发来分隔不同的功能区域,即使在这些不同功能区域的操作需要在同一时刻发生的情况下;若不显式地使用并发,你要么被迫编写任务切换框架,要么在操作中主动地调用不相关的一段代码。
    • 更高效的性能:为了充分发挥多核心处理器的优势,使用并发将单个任务分成几部分且各自并行运行,从而降低总运行时间。根据任务分割方式的不同,又可以将其分为两大类:一类是对同样的数据应用不同的处理算法(任务并行);另一类是用同样的处理算法共同处理数据的几部分(数据并行)。

    知道何时不使用并发与知道何时使用它一样重要。基本上,不使用并发的唯一原因就是在收益比不上成本的时候。使用并发的代码在很多情况下难以理解,因此编写和维护的多线程代码就有直接的脑力成本,同时额外的复杂性也可能导致更多的错误。除非潜在的性能增益足够大或关注点分离地足够清晰,能抵消确保其正确所需的额外的开发时间以及与维护多线程代码相关的额外成本,否则不要使用并发。

    2.2 在C++中使用并发和多线程

    在早期的C++标准中,比如1998 C++标准版不承认线程的存在,并且各种语言要素的操作效果都以顺序抽象机的形式编写。不仅如此,内存模型也没有被正式定义,所以对于1998 C++标准,你没办法在缺少编译器相关扩展的情况下编写多线程应用程序。如果在之前想使用多线程并发编程,可以借助编译器厂商提供的平台相关的扩展多线程支持API(比如POSIX C和Microsoft Windows API),但这种多线程支持对平台依赖度较高,导致可移植性较差。

    为了解决平台相关多线程API使用上的问题,逐渐开发出了Boost、ACE等平台无关的多线程支持类库。直到C++11标准的发布,借鉴了很多Boost类库的经验,将多线程支持纳入C++标准库。C++11标准不仅提供了一个全新的线程感知内存模型,也包含了用于管理线程、保护共享数据、线程间同步操作以及低级原子操作的各个类。

    对于C++整体以及包含低级工具的C++类——特别是在新版C++线程库里的那些,参与高性能计算的开发者常常关注的一点就是效率。如果你正寻求极致的性能,那么理解与直接使用底层的低级工具相比,使用高级工具所带来的实现成本,是很重要的。这个成本就是抽象惩罚(abstraction penalty)。标准C++线程库在设计时,就非常注重高效的性能,提供了足够的低级工具(比如原子操作库),以付出尽可能低的抽象惩罚。C++标准库也提供了更高级别的抽象和工具,它们使得编写多线程代码更简单和不易出错。有时候运用这些工具确实会带来性能成本,因为必须执行额外的代码。但是这种性能成本并不一定意味着更高的抽象惩罚;总体来看,这种性能成本并不比通过手工编写等效的函数而招致的成本更高,同时编译器可能会很好地内联大部分额外的代码。

    三、C++线程创建

    一个多线程C++程序是什么样子的?它看上去和其他所有C++程序一样,通常是变量、类以及函数的组合。唯一真正的区别在于某些函数可以并发运行,所以你需要确保共享数据的并发访问是安全的。当然,为了并发地运行函数,必须使用特定的函数以及对象来管理各个线程。

    3.1 C++11新标准多线程支持库

    • < thread > : 提供线程创建及管理的函数或类接口;
    • < mutex > : 为线程提供获得独占式资源访问能力的互斥算法,保证多个线程对共享资源的同步访问;
    • < condition_variable > : 允许一定量的线程等待(可以定时)被另一线程唤醒,然后再继续执行;
    • < future > : 提供了一些工具来获取异步任务(即在单独的线程中启动的函数)的返回值,并捕捉其所抛出的异常;
    • < atomic > : 为细粒度的原子操作(不能被处理器拆分处理的操作)提供组件,允许无锁并发编程。

    3.2 线程创建的简单示例

    线程创建和管理的函数或类主要由< thread >库文件来提供,该库文件的主要操作如下:
    thread库文件
    由上表可知,通过std::thread t(f, args…)创建线程,可以给线程函数传递参数。通过join()函数关联并阻塞线程,等待该线程执行完毕后继续;通过detach()函数解除关联使线程可以与主线程并发执行,但若主线程执行完毕退出后,detach()接触关联的线程即便没有执行完毕,也将自动退出,有时可能这并非我们预期的结果,所以需要特别注意。下面给出一段线程管理的示例代码:

    //thread1.cpp  创建线程,并观察线程的并发执行与阻塞等待
    
    #include <iostream>
    #include <thread>
    #include <chrono>
    
    using namespace std;
     
    void thread_function(int n)
    {
        std::thread::id this_id = std::this_thread::get_id();			//获取线程ID
    
        for(int i = 0; i < 5; i++){    
            cout << "Child function thread " << this_id<< " running : " << i+1 << endl;
            std::this_thread::sleep_for(std::chrono::seconds(n));   	//进程睡眠n秒
        }
    }
    
    class Thread_functor
    {
    public:
        // functor行为类似函数,C++中的仿函数是通过在类中重载()运算符实现,使你可以像使用函数一样来创建类的对象
        void operator()(int n)
        {
            std::thread::id this_id = std::this_thread::get_id();
    
            for(int i = 0; i < 5; i++){
                cout << "Child functor thread " << this_id << " running: " << i+1 << endl;
                std::this_thread::sleep_for(std::chrono::seconds(n));   //进程睡眠n秒
            }
        }	
    };
    
     
    int main()
    {
        thread mythread1(thread_function, 1);      // 传递初始函数作为线程的参数
        if(mythread1.joinable())                  //判断是否可以成功使用join()或者detach(),返回true则可以,返回false则不可以
            mythread1.join();                     // 使用join()函数阻塞主线程直至子线程执行完毕
        
        Thread_functor thread_functor;			 //函数对象实例化一个对象
        thread mythread2(thread_functor, 3);     // 传递初始函数作为线程的参数
        if(mythread2.joinable())
            mythread2.detach();                  // 使用detach()函数让子线程和主线程并行运行,主线程也不再等待子线程
    
        auto thread_lambda = [](int n){			//lambda表达式格式:[capture list] (params list) mutable exception-> return type { function body }
            std::thread::id this_id = std::this_thread::get_id();
            for(int i = 0; i < 5; i++)
            {
                cout << "Child lambda thread " << this_id << " running: " << i+1 << endl;
                std::this_thread::sleep_for(std::chrono::seconds(n));   //进程睡眠n秒
            }       
        };
    
        thread mythread3(thread_lambda, 4);     // 传递初始函数作为线程的参数
        if(mythread3.joinable())
            mythread3.join();                     // 使用join()函数阻塞主线程直至子线程执行完毕
    
        std::thread::id this_id = std::this_thread::get_id();
        for(int i = 0; i < 5; i++){
            cout << "Main thread " << this_id << " running: " << i+1 << endl;
            std::this_thread::sleep_for(std::chrono::seconds(1));
        }
    
        getchar();
        return 0;
    }
    

    使用GCC编译为可执行程序的命令如下:

    g++ -Wall -g -std=c++11 -pthread thread1.cpp -o thread1		
    # -Wall显示所有警告,-g输出调试信息,-std=c++11使用c++11标准编译,-pthread编译使用POSIX thread库文件
    

    线程创建的参数是函数对象,函数对象不止是函数指针或成员函数指针,同时还包括函数对象(仿函数)与lambda表达式。上面的代码分别用三种函数对象创建了三个线程,其中第一个线程mythread1阻塞等待其执行完后继续往下执行,第二个线程mythread2不阻塞等待在后台与后面的第三个线程mythread3并发执行,第三个线程继续阻塞等待其完成后再继续往下执行主线程任务。

    为了便于观察并发过程,对三个线程均用了睡眠延时this_thread::sleep_for(duration)函数,且延时时间作为参数传递给该函数。这里的参数是支持C++泛型模板的,STL标准容器类型(比如Array/Vector/Deque/List/Set/Map/String等)都可以作为参数传递,但这里的参数默认是以拷贝的方式传递参数的,当期望传入一个引用时,要使用std::ref进行转换。

    针对任何线程(包括主线程),< thread > 还声明了一个命名空间std::this_thread,用以提高线程专属的全局函数。函数声明和效果见下表:
    this_thread命名空间
    上面的代码就是利用了std::this_thread提供的函数获得当前线程的ID,让当前线程睡眠一段时间(一般需要< chrono >头文件提供duration或timepoint)的功能,代码执行结果如下图所示:
    thread执行结果

    上面的示例假如多重复运行几次,有很大可能会出现某行与其他行交叠错乱的情况(如下图所示),为何会出现这种情况呢?这就涉及到多线程资源竞争的问题了,即一个线程对某一资源(这里指显示终端)的访问还未完成,另一线程抢夺并访问了该资源,导致该资源数据混乱情况的出现。解决方案详见下一篇文章:C++多线程并发(二)—线程同步
    行间交错

    更多文章:

    展开全文
  • springboot2.0 多线程并发执行任务

    千次阅读 2020-01-14 11:22:29
    springboot2.0版本 执行多线程方式,个人学习了两种,一种直接是继承父类Thread或实现Runnable 接口类,,重写run方法;第二种是通过springboot的支持注解@Async的方式。 第一种:自定义类继承Thread类或继承...

    springboot2.0版本 执行多线程方式,个人学习了两种,一种直接是继承父类Thread或实现Runnable 接口类,,重写run方法;第二种是通过springboot的支持注解@Async的方式。

    第一种:自定义类继承Thread类或继承Runnnable接口,重写run方法

    import com.xxx.xx.taskphone.model.PhoneCallin;
    import com.xxx.xxx.taskphone.service.PhoneCallinService;
    
    public class PhoneCallInThread implements Runnable{
    
    	private PhoneCallinService phoneCallinService;
    	public PhoneCallInThread(PhoneCallinService phoneCallinService) {
    		super();
    		this.phoneCallinService = phoneCallinService;
    	}
    	@Override
    	public void run() {
    		System.out.println("-------------------");
            //phoneCallinService.test();自定义逻辑业务
    	}
    	
    	
    }
    

    开启线程

    @Component
    public class TaskScheduling {
    
    	@Autowired
    	private PhoneCallinService phoneCallinService;
    
    	@Scheduled(fixedRate = 2000)
    	public void savePhoneCallin(){
    		long size = 5;
    		if(size>0){
    			new Thread(new PhoneCallInThread(phoneCallinService)).start();
    		}
    	}
    
    }

    注:因为线程处理是执行逻辑的,所以spring无法通过注解的形式来说完成代理的类对象(如此处的phoneCallInService);但是可以通过获取ApplicationSpringContext上下文来获取代理对象。

    我这里用的定时任务@scheduled(fixedRate = 2000) 每隔2秒钟执行一次。

    第二种:springboot2.0使用@Async进行异步调用

    多线程的配置类

    package com.xxx.perform.config;
    
    import java.util.concurrent.Executor;
    import java.util.concurrent.ThreadPoolExecutor;
    
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
    
    @Configuration
    public class TaskPoolConfig {
    	@Bean("taskExecutor")
    	public Executor taskExecutor() {
    		ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
    		executor.setCorePoolSize(10);//核心线程数目
    		executor.setMaxPoolSize(20);//指定最大线程数
    		executor.setQueueCapacity(200);//队列中最大的数目
    		executor.setKeepAliveSeconds(60);//线程空闲后的最大存活时间
    		executor.setThreadNamePrefix("taskExecutor-");//线程名称前缀
    		executor.setWaitForTasksToCompleteOnShutdown(true);//设置 线程池关闭 的时候 等待 所有任务都完成后,再继续 销毁 其他的 Bean,确保 异步任务 的 销毁 就会先于 数据库连接池对象 的销毁。
    		executor.setAwaitTerminationSeconds(60);//设置线程池中 任务的等待时间
    		executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
    		executor.initialize();
    		return executor;
    	}
    }

    启动类上开启线程支持

    @EnableAsync
    public class PerformAdminApplication {
    
    	public static void main(String[] args) {
    		SpringApplication.run(AdminApplication.class, args);
    	}
    }

    使用@Async注解表明当前类或当前方是一个线程类,并支持多线程处理

    @Scheduled(fixedRate = 2000)
    @Async
    public void test(){
    	System.out.println("---------");
    }

    需要注意的事,如果多次调用被@Async注解的方法,会每次都会生成一个线程来处理方法逻辑。这样就需要及时处理逻辑后的结果。需要提供方法的返回处理逻辑。

    多线程的异步回调

    在被注明是线程处理的方法提供回调信息类

    @Async
    public Future<String> doTaskCallback() throws Exception {
        super.doTaskTwo();
        return new AsyncResult<>("任务二完成");
    }

    然后直接正常获取返回值就可以了

    List<String> result = new Test().doTaskCallback();

    最后个人建议在执行下一次的调用之前先判断当前线程是否已经完成,result.isDone()。如果线程未结束,可以Thead.sleep(1000),休眠一定时间后重新执行。

    展开全文
  • 在使用多线程时,简单的IO操作有时满足不了我们的...右侧:通过多线程并发执行,共进行了4次调用,整个执行时间大约为用时最长的一次的时间 先看一下要进行TTS的数据: ["我的公众号是Python疯子", "内容没有花架...

    在使用多线程时,简单的IO操作有时满足不了我们的需求,我们需要有序的接收返回值,例如:调用第三方API

    我这个栗子是调用TTS的在线合成API,先看一下结果吧:

    左侧:正常的顺序执行,共进行了4次调用,最后的总时间为4次之和
    右侧:通过多线程并发执行,共进行了4次调用,整个执行时间大约为用时最长的一次的时间

    先看一下要进行TTS的数据:
    ["我的公众号是Python疯子", "内容没有花架子", "都是真实案例", "欢迎您的关注"]

    ######顺序执行

    顾名思义就是很简单的通过遍历调用API,然后对返回的音频数据进行拼接。


    共进行了4次调用,返回每次调用API的耗时,以及最后的总时间发现总时间为每次调用之和,这个时间已经很漫长,满足不了我的要求。

    多线程并发

    用多线程并发,可以很好的解决这个问题,但并发时的任务返回顺序是无法预料的,于是这里我用了sort进行序号话,这样就能知道返回的是那一句的内容了。现将现在的列表改为列表包含字典式,并将内容进行排序。


    然后进行TTS的API请求处理,对返回数据时同样进行添加对应的sort,对返回的数据再通过sort进行排序,这样就得到了有序的返回内容

    API请求处理返回值处理


    因为是多线程并发执行,共进行了4次调用,几乎是同时发起请求处理,整个执行时间大约为用时最长的一次的时间,远远高于顺序执行这是多线程处理代码


    Git代码: 公众号后台回复 thread_tools 

    展开全文
  • 我们启动项目通过控制台输出信息验证一下结果,最后发现所有的任务都在同一个线程池但不同线程中完成,说明这个方案完全可行,这样,我们就完成了spring boot 多线程并发定时任务。 注 @Scheduled所支持的...
  • SpringBoot实现多线程并发动态执行计划任务

    千次阅读 热门讨论 2020-07-07 14:32:49
    原来写了一篇关于springboot实现计划任务的文章,但是有较多的人都在问为什么数据库变更后计划任务没刷新,怎么去动态获取,怎么实现多线程并发执行,所以现在新开一篇文章,重新实现计划任务的方法,抽象出刷新功能...
  • 主线程启动一个子线程t并等到t线程结束后才执行: import threading import time def reading(): for i in range(5): print("reading", i) time.sleep(1) t = threading.Thread(target = reading)...
  • c++多线程并发执行

    千次阅读 2018-07-15 13:35:09
    算是学到的一个重要知识C++:线程(std::thread)C++11并发之std::thread语音识别中,声源定位线程所需时间很短。在一次while循环中,在声源定位线程得到角度并赋值给全局变量rotate_angle的时候,语音识别还没完成。当...
  • 没有特殊需求情况下JS中较大部分仍然是单线程运行...值得一提的是,单线程运行并非是代码的单线程运行而是任务的单线程运行,当然多线程并发执行 还有其他的实现这里不在阐述。此博文仅帮助理解异步与并发两个概念。
  • import java.util.concurrent.*; /** * 线程池测试类 */ ...public class ThreadPoolTest { ... * 多线程的任务类 */ static class Task implements Runnable{ //线程id private int id; ...
  • 今天在做模拟并发抽奖的demo,遇到并发子线程不运行的问题。运行以下代码,控制台输出“抽奖开始...”,后面有时候会输出子线程中... //锁住所有线程,等待并发执行 final CountDownLatch begin = new CountDownL...
  • 使用junit进行多线程并发测试

    万次阅读 2017-08-24 15:06:38
    这篇讲一下如何使用junit进行多线程并发测试 ~ 实际上junit是不支持多线程的,你可以试试,在test方法里面new个 Thread ~ 会报错 ~ 那你会问我在这还扯什么犊子 ~ 我当然不是在这扯犊子了~~ 有插件 ~~ 对,有...
  • 如何让多线程同一时刻并发执行

    千次阅读 2019-10-29 10:35:26
    多线程同一时刻并发执行 代码使用场景:需要在本机试试自己的接口在某个时间访问可以被执行的频率【严格意义上来说,不算同一时刻,因为多线程执行也是上下文切换的】 其实你想找的是某个时间范围(如一秒内),你...
  • JAVA中的多线程并发运行安全问题

    千次阅读 热门讨论 2021-03-19 17:58:27
    JAVA中的多线程并发运行安全问题 1.什么是多线程并发运行安全问题? 当多个线程并发操作一个数据时,由于线程操作的时间不可控的原因,可能会导致操作该数据时的过程没有按照程序设计的执行顺序运行,导致操作后数据...
  • c#多线程并发执行一个操作函数

    万次阅读 2013-05-13 16:41:08
    有时候我们进行很多的数据对比运算时,单线程的程序显得很慢,这个时候可以用多线程并发运行: int maxThread = 10; //10个并发线程 int currTNum = 0; WaitHandle[] whs = new WaitHandle...
  • 办法是使用java.util.concurrent包下的计数器工具类CountDownLatch,看demo,本demo巧妙的使用了CountDownLatch让多线程模拟高并发以及所有线程执行完之后的耗时。 private static Date startDate; private ...
  • 十个多线程并发编程面试题(附答案)

    千次阅读 多人点赞 2021-02-06 11:29:10
    1.说说你知道的创建线程的方式 1、继承Thread类,重写run方法。2、实现Runnable接口,重写run方法。3、实现Callable接口,重写call方法。4、通过线程池创建线程。 2.说说Runnable和Callable的区别 Callable可以...
  • 多线程&并发-实例与解决方案

    千次阅读 2020-06-23 11:27:24
    java中你知道哪些锁? 问题回答: 乐观锁/悲观锁 共享锁/独享锁 公平锁/非公平锁 互斥锁/读写锁 可重入锁 自旋锁 分段锁 偏向锁/轻量级锁/重量级锁...2.就绪/运行(RUNNABLE):该状态包含了经典线程模型的两种状态:就
  • Rust的并发编程(二) 多线程并发

    千次阅读 2020-09-10 11:33:12
    文章目录Rust的并发编程(二)多线程并发创建子线程线程间通信使用通道传递数据共享内存 Rust的并发编程(二) 并发,是指在宏观意义上同一时间处理多个任务。并发的方式一般包含为三种:多进程、多线程以及最近几年...
  • 一份经典多线程并发面试题!

    千次阅读 2019-05-07 19:49:28
    synchronized关键字解决的是线程之间访问资源的同步性,synchronized关键字可以保证被它修饰的方法或者代码块在任意时刻只能有一个线程执行。 另外,在 Java 早期版本中,synchronized属于重量级锁,效率低下,...
  • java多线程详解(并发,并行,同步)

    万次阅读 2017-07-04 10:30:41
    要解决上述问题,咱们得使用多进程或者多线程来解决. 并发和并行是即相似又有区别(微观概念): 并行:指两个或多个事件在同一时刻点发生; 并发:指两个或多个事件在同一时间段内发生。 在操作系统中,在
  • JAVA多线程之间实现同步+多线程并发同步解决方案

    万次阅读 多人点赞 2018-03-04 14:09:15
    一、什么是线程安全问题 为什么...案例:需求现在有100张火车票,有两个窗口同时抢火车票,请使用多线程模拟抢票效果。/** * 需求现在有100张火车票,有两个窗口同时抢火车票,请使用多线程模拟抢票效果。 * Crea...
  • 如何解决多线程并发问题

    千次阅读 2019-04-26 13:45:08
    多线程编程中的三个核心概念 原子性 这一点,跟数据库事务的原子性概念差不多,即一个操作(有可能包含有多个子操作)要么全部执行(生效),要么全部都不执行(都不生效)。 关于原子性,一个非常经典的例子就是...
  • 只好学习控制线程数了,官方文档不好看,觉得结构不够清晰,网上找很多文章也都不很清晰,只有for全开线程,没有控制线程数的具体说明,最终终于根据多篇文章和官方文档算是搞明白基础的多线程怎么实现法了,怕长...
  • 跟着作者的65节课彻底搞懂Java并发原理专栏,一步步彻底搞懂Java并发原理。 作者简介:笔名seaboat,擅长工程算法、人工智能算法、自然语言处理、架构、分布式、高并发、大数据和搜索引擎等方面的技术,大多数编程...
  • 批量接口多线程并发执行

    千次阅读 2014-10-29 16:02:44
    一般大家会想到使用Future,即希望通过多线程并发执行解决该问题。查询执行开始,启动四个线程同时执行A、B、C、D四个接口查询,最后合并多个线程查询结果即可。开发过程中一般结合线程池配合,以保证更好的线程管理...
  • 多线程并发队列实现

    千次阅读 2019-04-20 13:53:59
    如果get执行时,队列为空,线程必须阻塞等待,直到有队列有数据。如果add时,队列已经满,则add线程要等待,直到队列有空闲空间。 /** * 1.使用 wait notify 实现一个队列,队列有2个方法,add 和 ge...
  • Java多线程并发编程-线程池

    千次阅读 2019-04-14 00:04:34
    Java多线程并发编程线程池问题思考线程池原理任务用什么表示仓库用什么:BlockingQueue自己实现一个线程池JDK线程池API 线程池 问题思考 问题1、用多线程的目的是什么? 充分利用 CPU 资源,并发做多件事。 ...
  • 多线程并发服务器

    千次阅读 2017-03-25 15:45:23
    进程并发服务器的应用程序中,父进程accept一个连接,fork一个子进程,该子进程负责处理与该连接对端的客户之间的通信。 尽管进程的编程模型中,各进程拥有独立的地址空间,减少了出错的概率,然而,fork调用...
  • 运行main方法以后,为什么不能实现线程启动?我想用java程序模拟个消费者和kafka生产者通信,可以实现吗 import org.apache.kafka.clients.consumer.ConsumerRecord; import org.apache.kafka.clients....

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 560,217
精华内容 224,086
关键字:

如何创建多线程并发执行