-
2018-07-12 16:35:51
线程和进程各自有什么区别和优劣呢?
1 进程是资源分配的最小单位,线程是程序执行的最小单位。
2 进程有自己的独立地址空间,每启动一个进程,系统就会为它分配地址空间,建立数据表来维护代码段、堆栈段和数据段,这种操作非常昂贵。而线程是共享进程中的数据的,使用相同的地址空间,因此CPU切换一个线程的花费远比进程要小很多,同时创建一个线程的开销也比进程要小很多。
3 线程之间的通信更方便,同一进程下的线程共享全局变量、静态变量等数据,而进程之间的通信需要以通信的方式(IPC)进行。不过如何处理好同步与互斥是编写多线程程序的难点。
但是多进程程序更健壮,多线程程序只要有一个线程死掉,整个进程也死掉了,而一个进程死掉并不会对另外一个进程造成影响,因为进程有自己独立的地址空间。进程的并发只是宏观上的并发,就是不断的上下文切换,不停的切换,其实微观还是来回交替的的
但是多线程的并发,就是并行加上并发
缺点:进程只能在一个时间干一件事,如果想同时干两件事或多件事,进程就无能为力了。
进程在执行的过程中如果阻塞,例如等待输入,整个进程就会挂起,即使进程中有些工作不依赖于输入的数据,也将无法执行。
如果这两个缺点理解比较困难的话,举个现实的例子也许你就清楚了:如果把我们上课的过程看成一个进程的话,那么我们要做的是耳朵听老师讲课,手上还要记笔记,脑子还要思考问题,这样才能高效的完成听课的任务。而如果只提供进程这个机制的话,上面这三件事将不能同时执行,同一时间只能做一件事,听的时候就不能记笔记,也不能用脑子思考,这是其一;如果老师在黑板上写演算过程,我们开始记笔记,而老师突然有一步推不下去了,阻塞住了,他在那边思考着,而我们呢,也不能干其他事,即使你想趁此时思考一下刚才没听懂的一个问题都不行,这是其二。
现在你应该明白了进程的缺陷了,而解决的办法很简单,我们完全可以让听、写、思三个独立的过程,并行起来,这样很明显可以提高听课的效率。而实际的操作系统中,也同样引入了这种类似的机制——线程
理解进程的并发
注意:这只是宏观上看来,其实处理器在同一时刻只能处理一个程序,千万不要以为同一时刻处理器在处理多个程(要是有多个处理器那就另当别论了~)。就像你的大脑一样,让你同一时刻算多道运算题,你觉得可能吗。计算机并发地计算多道题目只不过是先计算A题,但是还没执行完,然后又去执行B题,B题还没执行完,下面说不定又去执行A题或者其他的题目。这样在用户看来好像是多个程序在同时执行。当然了这一切的保证是计算机运算速度是相当快的。如果计算机运行的速度非常慢(究竟有多慢自己脑补),你会明显感觉到cpu的控制权在各个程序之间来回交替。
进程有独立的地址空间,一个进程崩溃后,在保护模式下不会对其它进程产生影响,而线程只是一个进程中的不同执行路径。线程有自己的堆栈和局部变量,但线程之间没有单独的地址空间,一个线程死掉就等于整个进程死掉,所以多进程的程序要比多线程的程序健壮,但在进程切换时,耗费资源较大,效率要差一些。但对于一些要求同时进行并且又要共享某些变量的并发操作,只能用线程,不能用进程。
更多相关内容 -
Python控制多进程与多线程并发数总结
2020-09-21 12:29:53本篇文章主要介绍了Python控制多进程与多线程并发数,详细讲诉了进程和线程的区别,并介绍了处理方法,有需要的朋友可以了解一下。 -
go 进程 线程 协程 并发
2021-01-07 22:46:43// 一个进程可以创建和撤销多个线程 同一个进程中的多个线程之间可以并发执行 //并发 并行 //多线程程序在单核心的cpu上运行 称为并发 //多线程程序在多核心的cpu上运行 称为并行 //并发与并行并不相同 并发主要是由... -
Java多线程之并发工具类
2020-12-22 18:16:08一、总论:在JDK中提供了几种并发工具类 1)CountDownLatch(同步倒数计数器:等待多线程(或者多步骤)完成) 2)CyclicBarrier(循环屏障:同步屏障) 3)Semaphore(信号量:控制并发进程数) 主要参考... -
进程线程和并行并发
2019-01-17 16:37:40定义 进程 进程是具有一定独立功能的程序,关于某个数据集合上的一次运行活动,进程是系统进行资源分配和调度的一个独立单位。一个进程要是能独立运行,它必须拥有一定的资源,包括用于存放程序正文,...线程是进程...定义
进程
进程是具有一定独立功能的程序,关于某个数据集合上的一次运行活动,进程是系统进行资源分配和调度的一个独立单位。一个进程要是能独立运行,它必须拥有一定的资源,包括用于存放程序正文,数据的磁盘和内存地址空间,以及它在运行时所需的I/O设备,已打开的文件,信号量等。
进程同时又是一个可独立调度和分派的基本单位,一个进程要是能独立运行,它还必须是一个可独立调度和分派的基本单位。线程
线程是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位。线程自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源(如程序计数器,一组寄存器),但是它可与同属一个进程的其他线程共享进程所拥有的全部资源
关系
在传统的OS中,进程是作为独立调度和分派的基本单位,因而进程是能独立运行的基本单位。在每次被调度的时候,都需要进行上下文切换,开销比较大。而在引入线程的OS中,已把线程作为调度和分派的基本单位,因而线程是能独立运行的基本单位。当线程切换时,仅需保存和设置少量的寄存器内容,切换代价远低于进程。同一个进程中,线程的切换不会引起进程的切换,但从一个进程中的线程切换到另一个进程中的线程时,必然会引起进程的切换.
一个线程可以创建和撤销另一个线程;同一个进程中的多个线程之间可以并发执行;
相对进程而言,线程是一个更加接近执行体的概念,它可以与同进程中的其他线程共享数据,因此线程可以读出同样的数据结构和变量,便于线程之间的通信,但拥有自己的栈空间,拥有独立的执行序列区别
主要差别在于它们是不同的操作系统资源管理方式。进程有独立的地址空间,一个进程崩溃后,在保护模式下不会对其他进程产生影响,而线程只是进程中的不同执行路径。进程有自己的堆栈和局部变量,但线程之间没有单独的地址空间,一个线程死掉就等于进程死掉,所以多进程的程序要比多线程的程序更健壮,但在进程切换时,耗费资源较大,但对于一些要求同时进行并且又要共享某些变量的并发操作,只能使用线程,不能使用进程
-
简而言之,一个程序至少有一个进程,一个进程至少有一个线程.
-
线程的划分尺度小于进程,使得多线程程序的并发性高。
-
另外,进程在执行过程中拥有独立的内存单元,而多个线程共享内存,从而极大地提高了程序的运行效率。
-
线程在执行过程中与进程还是有区别的。每个独立的线程有一个程序运行的入口、顺序执行序列和程序的出口。但是线程不能够独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制。
-
从逻辑角度来看,多线程的意义在于一个应用程序中,有多个执行部分可以同时执行。但操作系统并没有将多个线程看做多个独立的应用,来实现进程的调度和管理以及资源分配。这就是进程和线程的重要区别。
优缺点
线程执行开销小,但不利于资源的管理和保护。而进程相反,同时,线程适合于在SMP机器上运行,而进程则可以跨机器迁移
多线程应用
例如并发读取两个文件,文件读取完毕后cpu才开始处理,完成文件读取前cpu一直空闲。如果调整操作顺序,cpu的利用率会提高,程序执行时间也会缩短,在读取B文件时候cpu可以处理文件A,通常情况,cpu在等待IO时,可以执行命令。IO可以是文件IO、网络IO、输入输出IO等。这种情况经常会发生,因为IO速度比cpu的处理速度慢很多。(前提保证,执行顺序不会影响最后结果,结果大于性能,例如一次付款,执行顺序会影响结果就不适合多线程并行执行),多线程是为了更充分的利用cpu资源,再例如服务器在一个端口监听用户请求,服务器处理完用户的请求后继续监听其他的用请求,这样带来的问题是,服务器在处理用户的请求过程中,其他的请求是无法监听和接收的。为解决这个问题,可以采用多线程的方式,每个用户请求的处理都交给一个线程去做。这样,服务器在处理用户请求时可以继续监听端口上其他用户请求
并发与并行
并发是指同一时间片段同时执行,并行是指同一时间点同时执行。进程之间相互独立可以实现并行,线程不可以。多线程只能并发执行,而且是顺序执行,只是在同一时间片段,类似同时执行,CPU可以按时间切片执行,单核CPU同一时刻只支持一个线程执行任务,多线程并发事实上就是多个线程排队申请调用CPU,CPU执行速度很多,所以看上去是多个线程任务说是并发处理。
-
-
Python多进程并发与多线程并发编程实例总结
2020-12-25 09:27:07多线程并发则由程序员管理并发处理的任务,这种并发方式可以方便地在线程间共享数据(前提是不能互斥)。Python对多线程和多进程的支持都比一般编程语言更高级,最小化了需要我们完成的工作。 一.多进程并发 Mark ... -
Java多线程并发执行demo代码实例
2020-08-18 23:50:22主要介绍了Java多线程并发执行demo代码实例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 -
tcp socket实现单进程单线程 高并发服务端(c++源码)
2018-11-22 15:30:37tcp socket实现单进程单线程 高并发服务端源码 c++源码 -
php多线程并发实现方法
2020-10-21 09:58:27主要介绍了php多线程并发实现方法,结合实例形式分析了php模拟多线程并发的相关操作技巧,以及在Linux平台借助shell实现多线程并发的操作方法,需要的朋友可以参考下 -
python并发技术实现(多线程、多进程)
2018-12-07 01:15:54压缩包内包含四个文件,实现的效果都是通过多线程或多进程执行加法运算; multiprocess_queue,通过任务队列方式实现多进程任务;(multiprocessing模块) multithread_queue,通过任务队列方式实现多线程任务;... -
操作系统实验报告理解linux下进程和线程的创建并发执行过程。.doc
2020-07-19 00:34:04操作系统上机实验报告 实验名称 进程和线程 实验目的 理解uix/Linux下进程和线程的创建并发执行过程 实验内容 1进程的创建 2多线程应用 实验步骤及分析 进程的创建 下面这个C程序展示了UNIX系统中父进程创建子进程及... -
Java_多线程与并发编程总结.doc
2020-04-14 22:18:35对于一个进程中的多个线程来说,多个线程共享进程的内存块,当有新的线程产生的时候,操作系统不分配新的内存,而是让新线程共享原有的进程块的内存。因此,线程间的通信很容易,速度也很快。不同的进程因为处于不同... -
Python实现web服务器入门学习笔记(4)——单进程单线程非阻塞实现并发及其原理
2020-12-20 23:23:42在Python实现web服务器入门学习笔记(3)——多进程、多线程实现并发HTTP服务器中,我们知道可以分别通过多进程、多线程的方式实现并发服务器,那么,是否可以通过单进程单线程的程序实现类似功能呢? 实际上,在... -
Python并发:多线程与多进程
2021-02-24 20:39:25多线程并发下载图片4.多进程并发提高数字运算在计算机编程领域,并发编程是一个很常见的名词和功能了,其实并发这个理念,最初是源于铁路和电报的早期工作。比如在同一个铁路系统上如何安排多列火车,保证每列火车的... -
Python并发:多线程与多进程的详解
2020-12-31 03:35:083.多线程并发下载图片 4.多进程并发提高数字运算 关于并发 在计算机编程领域,并发编程是一个很常见的名词和功能了,其实并发这个理念,最初是源于铁路和电报的早期工作。比如在同一个铁路系统上如何安排多列火车,... -
python并发编程之多进程、多线程、异步和协程详解
2020-12-25 12:03:10即使是单CPU的计算机,也可以通过不停地在不同线程的指令间切换,从而造成多线程同时运行的效果。 多线程相当于一个并发(concunrrency)系统。并发系统一般同时执行多个任务。如果多个任务可以共享资源,特别是同时... -
多线程与并发
2019-04-14 17:04:34文章目录1、什么是进程2、进程的状态3、线程4、线程的基本使用5、线程休眠6、守护线程与yield7、join和中断线程8、线程同步同步代码块:同步方法Lockvolatile阻塞队列使用原子变量实现线程同步9、死锁经典案例面试题...文章目录
1、什么是进程
程序是指令和数据的有序集合,其本身没有任何运行的含义,是一个静态的概念,而进程是程序在处理机上的一次执行过程,是个动态的概念。
进程是一个具有一定独立功能的程序,一个实体,每一个进程都有自己的地址空间。
2、进程的状态
进程执行的间断性决定了进程可能具有多种状态,事实上,运行中的进程具有以下三种状态:
- 就绪状态
- 运行状态
- 阻塞状态
3、线程
线程实际上是在进程的基础上进一步划分,一个进程启动后。里面的若干程序又可以划分若干个线程。
线程:是进程的一个执行路径,共享一个内存空间,线程之间可以自由切换,并发执行,一个进程最少有一个线程
并行:两个任务同时运行(多个CPU)
并发:就是将任务轮流执行,由于CPU时间片运行时间较短,就会感觉两个任务在同时执行。
4、线程的基本使用
- 一种是继承Thread类
class MyThread extends Thread{ public void run{ //逻辑处理 } } MyThread mt = new MyThread(); mt.start()
- 另外一种是实现Runnable接口(推荐使用)
class MyRunnable implements Runnable{ public void run(){ //逻辑处理 } } MyRunnable mr = new MyRunnable(); Thread t = new Thread(mr); t.start();
5、线程休眠
pulic static void sleep(long mills) 在当前线程的执行中,暂停指定的毫秒数,释放CPU的时间片
6、守护线程与yield
public final void setDaemon(boolean on) 将此线程标记为daemon线程或用户线程,当运行的唯一线程都是守护进程线程时,Java虚拟机将退出 参数为true时,线程为守护线程 public static void yield() 暂停当前正在执行的线程对象,并执行其他线程 void setPriority(int newPriority) 更改线程优先级 static int MAX_PRIORITY 最大优先级,抢到CPU时间片的概率大 static int MIN_PRIORITY 最小优先级 static int NORM_PRIORITY 默认优先级
package per.learn.multhread; public class ThreadDemo2 { public static void main(String[] args) { MyRunnable2 mr = new MyRunnable2(); Thread tr = new Thread(mr); tr.setDaemon(true);//设置为守护线程 tr.start(); for(int i =0;i<10;i++){ System.out.println("main--"+i); try { Thread.sleep(200); } catch (InterruptedException e) { e.printStackTrace(); } } } } class MyRunnable2 implements Runnable{ private long millis; @Override public void run() { for(int i =0;i<10;i++){ System.out.println("--"+i); try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } } } } OUT: main--0 --0 main--1 main--2 --1 main--3 main--4 --2 main--5 main--6 main--7 --3 main--8 main--9 --4 main运行完,线程tr并未执行完。
7、join和中断线程
public final void join() throws InterruptedException 等待这个线程死亡 调用此方法的行为方式与调用相同 join(0) public void interrupt() 中断这个线程 除非当前线程中断自身,这是允许的 public static boolean interrupted() 测试当前线程是否中断,该方法会清除中断状态
join方法:
加入线程,将调用的线程执行指定时间或执行完毕,再执行其他package per.learn.multhread; public class ThreadDemo3 { public static void main(String[] args) { MyRunnable3 mr3 = new MyRunnable3(); Thread t = new Thread(mr3); t.start(); for(int i =0;i<10;i++){ System.out.println(Thread.currentThread().getName()+"--"+i); try { Thread.sleep(200); } catch (InterruptedException e) { e.printStackTrace(); } if(i==5){ try{ t.join();//让线程t执行完毕 }catch (InterruptedException e) { e.printStackTrace(); } //t.interrupt();//中断线程,只是做了中断标记,抛出异常,线程仍然会执行 } } } } class MyRunnable3 implements Runnable{ @Override public void run() { for(int i =0;i<10;i++){ System.out.println(Thread.currentThread().getName()+"--"+i); try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } } } } Out: main--0 Thread-0--0 main--1 main--2 Thread-0--1 main--3 main--4 Thread-0--2 main--5 Thread-0--3 Thread-0--4 Thread-0--5 Thread-0--6 Thread-0--7 Thread-0--8 Thread-0--9 main--6 main--7 main--8 main--9
package per.learn.multhread; public class ThreadDemo3 { public static void main(String[] args) { MyRunnable3 mr3 = new MyRunnable3(); Thread t = new Thread(mr3); t.start(); for(int i =0;i<10;i++){ System.out.println(Thread.currentThread().getName()+"--"+i); try { Thread.sleep(200); } catch (InterruptedException e) { e.printStackTrace(); } if(i==5){ // try{ // t.join();//让线程t执行完毕 // }catch (InterruptedException e) { // e.printStackTrace(); // } t.interrupt();//中断线程,只是做了中断标记,抛出异常,线程仍然会执行 } } } } class MyRunnable3 implements Runnable{ @Override public void run() { for(int i =0;i<10;i++){ if(Thread.interrupted()){ break; } System.out.println(Thread.currentThread().getName()+"--"+i); try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); //会将中断状态清除 Thread.currentThread().interrupt();//将中断状态重新标记 } } } } main--0 Thread-0--0 main--1 main--2 Thread-0--1 main--3 main--4 main--5 Thread-0--2 main--6 java.lang.InterruptedException: sleep interrupted at java.lang.Thread.sleep(Native Method) at per.learn.multhread.MyRunnable3.run(ThreadDemo3.java:35) at java.lang.Thread.run(Thread.java:748) main--7 main--8 main--9
8、线程同步
- 多线程共享数据
在多线程的操作中,多个线程可能同时处理同一个资源,这就是多线程中的共享数据,会发生不安全的情况。 - 线程同步
同步代码块:
同步是一种高开销的操作,因此应该尽量减少同步的内容。
通常没有必要同步整个方法,使用synchronized代码块同步关键代码即可。synchronized(object){ 要同步的操作 }
例:
package per.learn.multhread; import sun.awt.windows.ThemeReader; public class ThreadDemo4 { public static void main(String[] args) { MyRunnable5 mr5 = new MyRunnable5(); Thread t1 = new Thread(mr5); Thread t2 = new Thread(mr5); //模拟两个窗口 t1.start(); t2.start(); } } class MyRunnable5 implements Runnable{ private int ticket=10;//车票 private Object obj = new Object(); @Override public void run() { for(int i=0;i<300;i++){ synchronized (obj){ if(ticket > 0){ ticket--; try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("您购买的车票已剩余"+ticket+"张"); } } } } }
同步方法
public synchronized void method(){
要同步的操作
}同步的方法是当前对象(this)
注: synchronized关键字也可以修饰静态方法,此时如果调用该静态方法,将会锁住整个类
package per.learn.multhread; import sun.awt.windows.ThemeReader; public class ThreadDemo4 { public static void main(String[] args) { MyRunnable5 mr5 = new MyRunnable5(); Thread t1 = new Thread(mr5); Thread t2 = new Thread(mr5); //模拟两个窗口 t1.start(); t2.start(); } } class MyRunnable5 implements Runnable{ private int ticket=10;//车票 private Object obj = new Object(); private synchronized void method(){ if(ticket > 0){ ticket--; try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("您购买的车票已剩余"+ticket+"张"); } } @Override public void run() { for(int i=0;i<300;i++){ method(); } } }
Lock
结构化更加方便,以上两种只能等同步方法或同步代码块执行完毕后才能释放锁。
ReentrantLock类是可重入、互斥、实现了Lock接口的锁,
它与使用synchronized方法和快具有相同的基本行为和语义,并且扩展了其能力ReenreantLock类的常用方法有: ReentrantLock() : 创建一个ReentrantLock实例 lock() : 获得锁 unlock() : 释放锁 注:ReentrantLock()还有一个可以创建公平锁的构造方法,但由于能大幅度降低程序运行效率,不推荐使用
package per.learn.multhread; import sun.awt.windows.ThemeReader; import java.util.concurrent.locks.ReentrantLock; public class ThreadDemo4 { public static void main(String[] args) { MyRunnable5 mr5 = new MyRunnable5(); Thread t1 = new Thread(mr5); Thread t2 = new Thread(mr5); //模拟两个窗口 t1.start(); t2.start(); } } class MyRunnable5 implements Runnable{ private int ticket=10;//车票 private Object obj = new Object(); ReentrantLock lock = new ReentrantLock(); private void method2(){ lock.lock();//锁 try{ if(ticket > 0){ ticket--; try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("您购买的车票已剩余"+ticket+"张"); } }finally { lock.unlock();//释放锁 } } @Override public void run() { for(int i=0;i<300;i++){ method2(); } } }
注意:
- 如果synchronized关键字能满足用户的需求,就用synchronized,因为它能简化代码
- 如果需要更高级的功能,就用ReentrantLock类,此时要注意及时释放锁,否则会出现死锁,通常在finally代码释放锁
volatile
- volatile关键字为域变量的访问提供了一种免锁机制,
- 使用volatile修饰域相当于告诉虚拟机该域可能会被其他线程更新,
- 因此每次使用该域就要重新计算,而不是使用寄存器中的值
- volatile不会提供任何原子操作,它也不能用来修饰final类型的变量
阻塞队列
LinkedBlockingQueue 类常用方法
- LinkedBlockingQueue() : 创建一个容量为Integer.MAX_VALUE的LinkedBlockingQueue
- put(E e) : 在队尾添加一个元素,如果队列满则阻塞
- size() : 返回队列中的元素个数
- take() : 移除并返回队头元素,如果队列空则阻塞
当队列满时:
add()方法会抛出异常
offer()方法返回false
put()方法会阻塞使用原子变量实现线程同步
需要使用线程同步的根本原因在于对普通变量的操作不是原子的。
那么什么是原子操作呢?
原子操作就是指将读取变量值、修改变量值、保存变量值看成一个整体来操作
即-这几种行为要么同时完成,要么都不完成。在java的util.concurrent.atomic包中提供了创建了原子类型变量的工具类,
使用该类可以简化线程同步。其中AtomicInteger 表可以用原子方式更新int的值,可用在应用程序中(如以原子方式增加的计数器),
但不能用于替换Integer;可扩展Number,允许那些处理机遇数字类的工具和实用工具进行统一访问。AtomicInteger类常用方法:
AtomicInteger(int initialValue) : 创建具有给定初始值的新的AtomicInteger
addAddGet(int dalta) : 以原子方式将给定值与当前值相加
get() : 获取当前值9、死锁
过多的同步有可能出现死锁。
经典案例
如:服务员需要等待厨师准备好食物,然后通知服务员上菜,然后回去继续等待,厨师代表生产者,服务员代表消费者。
package per.learn.multhread; /** * 两个线程协同工作,先生产,再消费 */ public class ProducterCustomerDemo { public static void main(String[] args) { Food food = new Food();//共享food对象 Producter p = new Producter(food); Customer c = new Customer(food); //先生产,再消费 Thread t1 = new Thread(p); Thread t2 = new Thread(c); t1.start(); t2.start(); } } /** * 消费者 */ class Customer implements Runnable{ private Food food; public Customer(Food food){ this.food =food; } @Override public void run() { for (int i = 0; i < 20; i++) { food.get(); } } } /** * 生产者 */ class Producter implements Runnable{ private Food food; public Producter(Food food){ this.food=food; } @Override public void run() { //模拟生产20份 for (int i = 0; i <20 ; i++) { if(i%3==0){ food.set("锅包肉","酸甜口味"); } else if(i%3==1){ food.set("佛跳墙","多辣"); }else{ food.set("小炒肉","多肉"); } } } } /* 共享 */ class Food{ private String name; private String describe; private boolean flag = true;//true 表示生产,false表示消费 /** * 生产产品 */ public synchronized void set(String name,String describe){ if(!flag){ //表示不能生产,要释放cpu和锁 try { this.wait();//线程进入等待状态,释放监视器所有权(对象锁) } catch (InterruptedException e) { e.printStackTrace(); } } this.setName(name); try { Thread.sleep(1000); //模拟生产过程 产生时间差 } catch (InterruptedException e) { e.printStackTrace(); } this.setDescribe(describe); flag = false; this.notify();//唤醒等待的线程(随机的其中一个) } /** * 消费 */ public synchronized void get(){ if(flag){ //表示不能消费 try { this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(this.getName()+"->"+this.getDescribe()); flag = true; this.notify(); } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getDescribe() { return describe; } public void setDescribe(String describe) { this.describe = describe; } @Override public String toString() { return "Food{" + "name='" + name + '\'' + ", describe='" + describe + '\'' + '}'; } public Food(String name, String describe) { this.name = name; this.describe = describe; } public Food() { } }
线程生命周期和线程池
线程池是预先创建线程的一种技术。线程池在还没有任务来之前,创建一定数量的线程,放入空闲队列中,然后对这些资源进行复用。减少频繁的创建和销毁对象。线程池接口是ExecutorService。Executor接口:
执行已提交的Runnable任务对象ExecutorService:
Executor提供了管理终止方法,以及可为追踪一个或多个异步任务执行状况而生成Future的方法。Executors类:
此包中所定义的Executor、ExecutorService等的工厂和实用方法。newSingleThreadExecutor:
创建单线程线程池。相当于单线程串行执行所有任务,如果这个唯一线程因为异常结束,那么会有一个新的线程替代它,此线程池保证所有任务的执行顺序按照任务的提交顺序执行。newFixedThreadPool:创建固定大小的线程池,每次提交一个任务就创建一个线程,直到达到线程池最大大小。
如果某个线程因为异常结束,就会补充一个新线程newCachedThredPool: 创建一个可缓存的线程池,如果线程池的大小超过了处理任务所需要的线程,那么就会收回部分空闲(60s不执行任务)的线程,线程池不会对线程池大小做限制,依赖操作系统(或者说JVM)能创建的最大线程池大小。
newSchedeledThreadPool:创建一个大小无限的线程池,支持定时及周期性执行任务的需求。(需要指定初始大小)
package per.learn.multhread; import java.util.concurrent.*; public class ThreadDemo5 { public static void main(String[] args) { //创建线程池(4种) //ExecutorService es = Executors.newSingleThreadExecutor(); //ExecutorService es = Executors.newFixedThreadPool(2); ScheduledExecutorService es = Executors.newScheduledThreadPool(2); //es.execute(new MyRunnable6()); //es.execute(new MyRunnable6()); es.schedule(new MyRunnable6(),3000, TimeUnit.MILLISECONDS); es.shutdown();//结束 } } class MyRunnable6 implements Runnable{ @Override public void run() { for (int i = 0; i < 10; i++) { System.out.println(Thread.currentThread().getName()+"---"+i); try { Thread.sleep(300); } catch (InterruptedException e) { e.printStackTrace(); } } } }
面试题
sleep() wait de 区别:
sleep:让线程进入休眠状态,让出CPU时间时间片,不释放对象锁wait:让线程进入等待状态,让出CPU时间片,释放对象锁,等待其他线程通过notify()方法来唤醒,也可以设置等待时间,该线程再次竞争资源
-
Python中进程和线程的区别详解
2020-12-24 12:02:42一个进程中可以并发多条线程,每条线程并行执行不同的任务。 Num02–>进程 进程就是一个程序在一个数据集上的一次动态执行过程。 进程有以下三部分组成: 1,程序:我们编写的程序用来描述进程要完成哪些功能以及... -
多线程(并发执行)
2020-08-02 15:06:54 当系统有一个以上CPU时,同一时刻,当一个CPU在执行一个任务时,另一个CPU在执行另一个任务,两个任务互不抢占CPU资源,可以同时进行(多核CPU,一个CPU执行一个进程) 并发 一个CPU,同一时间,有多个任务在...一、概念区分
1、并行与并发
并行
当系统有一个以上CPU时,同一时刻,当一个CPU在执行一个任务时,另一个CPU在执行另一个任务,两个任务互不抢占CPU资源,可以同时进行(多核CPU,一个CPU执行一个进程)
并发
一个CPU,同一时间,有多个任务在执行。但并发不是真正意义上的“同时进行”,只是将CPU划分成好几个时间片段,每个片段内执行一个任务,然后在这几个片段之间来回切换,由于CPU处理速度快,让用户感觉像是多个任务在同时执行。
区别:
-
并行是某一时刻,真正有多个程序在运行;并发是在一段时间内,宏观上多个程序同时运行。
-
并发,指多个事情,在同一时间段内同时发生了;多个任务之间是相互抢占资源的
并行,指多个事情,在同一时间点上同时发生了;多个任务之间是不相互抢占资源的
-
只有在多个CPU或CPU多核时,才会发生并行,否则看似同时发生的事情,都是并发的
2、进程与线程
进程
指系统中正在运行的一个应用程序;是资源分配的最小单位
线程
是进程内独立执行的一个单一顺序的控制流;是系统分配处理器时间资源的基本单位;是程序执行的最小单位
区别
- 进程之间数据不共享
- 线程之间可以共享资源
二、线程的生命周期
生命周期:在程序开发中,一个对象从被实例化完成,到这个对象使用结束并销毁的整个过程,类似于人的一生
线程的生命周期:一个线程被实例化,到这个线程销毁的整个过程
线程的状态
- 新建:New
一个线程被实例化完成,但是还没有做任何动作
- 就绪:Ready
一个线程已经被启动 (调用start()方法),开始争抢CPU的时间片
- 运行:Run
一个线程抢到了CPU的时间片,开始执行这个线程中的逻辑
- 阻塞:Interrupt
一个线程在运行的过程中,受到某些操作的影响,放弃已经获取的CPU时间片,并且不再参与CPU时间片的争抢,此时线程处于挂起状态
- 死亡:Dead
一个线程对象需要被销毁
三、开启线程的方式
1、继承Thread类,实现其run()方法
//要自定义一个线程类,并且该类要继承Thread类 class MyThread extends Thread{ //重写run方法 @Override public void run() { for(int i=0;i<5;i++) { System.out.println("子线程逻辑:"+i); } } } public class ThreadClass { public static void main(String[] args) { MyThread mt=new MyThread(); //新建 mt.start(); //就绪 System.out.println("主线程逻辑执行结束"); } } /*输出结果: 主线程逻辑执行结束 子线程逻辑:0 子线程逻辑:1 子线程逻辑:2 子线程逻辑:3 子线程逻辑:4 */
如果是串行运行,则“主线程逻辑执行结束”这句话应该最后执行。但由于并发执行的多线程存在,使得主程序逻辑先执行完毕,在执行子线程
注意:只有调用start方法才会启动线程,并且使该线程执行run方法;如果直接调用run方法,则并没有开启线程,即线程不会进入就绪状态。
2、实现Runnable接口,实现其run()方法
/* * Runnable接口是一个函数式接口,可以采用Lambda表达式实现其run方法 */ public class ThreadClass { public static void main(String[] args) { Runnable r1=()->{ for(int i=0;i<5;i++) { System.out.println("子线程中的逻辑:"+i); } }; Thread t=new Thread(r1); //新建 t.start(); //就绪 System.out.println("主线程逻辑执行结束"); } } //输出结果同上
3、实现Callable接口
与Runnable接口类似,只是该方式有返回值,但Runnable没有返回值
需要使用一个中介FutureTask
import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.FutureTask; public class Test { public static void main(String[] args) { //返回值是int类型 Callable callable=()->{ int result=0; for(int i=0;i<100;i++) { result+=i; } return result; }; //Thread thread=new Thread(callable); 不能直接像创建Runnable接口一样 //知道返回值是int性。使用泛型约束 FutureTask<Integer> task=new FutureTask<> (callable); Thread thread=new Thread(task); thread.start(); //获取计算结果 Integer integer = null; try { integer = task.get(); //该方法会抛出两个异常,需要手动处理 } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } System.out.println(integer); } }
4、异同点
- 继承Thread类,可读性更高,但是如果某个类类继承了Thread类,那么该类将不能再继承其他类,这有可能会破坏原有的继承结构
- 使用Runnable接口,程序可对象降低,但不会破坏继承结构,一般多使用这种方式
四、线程的常用方法
1、线程的命名setName
- 实例化一个线程,使用setName()方法
- 实例化一个线程的同时,通过构造方法对线程进行命名
- 3、使用用户自定义的线程类,在实例化的同时,进行名字的赋值
需要给自定义线程类添加对应的构造方法
class MyThread extends Thread{ public MyThread() {} public MyThread(String name) { this.setName(name); //使用setName()方法 //super(name); //直接调用父类的构造方法 } } public class ThreadClass { public static void main(String[] args) { //1、实例化一个线程,使用setName()方法 Thread t=new Thread(); t.setName("用户线程1"); System.out.println(t.getName()); //2、实例化一个线程的同时,通过构造方法对线程进行命名 // 构造方法:Thread(Runnable r,String name); Thread t2=new Thread(()->{},"用户线程2"); System.out.println(t2.getName()); //3、使用用户自定义的线程类,在实例化的同时,进行名字的赋值 // 需要给自定义线程类添加对应的构造方法 MyThread t3=new MyThread("用户线程3"); System.out.println(t3.getName()); } }
2、线程休眠sleep(Run->Interrupt)
- 调用**sleep()**方法,参数:以毫秒为单位的时间差
- 会抛出InterruptedException异常,需要处理
- 使得线程由运行状态变为阻塞状态,当休眠时间到达时,才会重新变为就绪状态。即使此时系统中没有其他可执行的线程,处于sleep的线程也依然不会执行
class MyThread extends Thread{ //重写run方法 @Override public void run() { for(int i=0;i<5;i++) { System.out.println(+i); //线程休眠 //参数:以毫秒为单位 //需要捕获异常 try { Thread.sleep(1000); //休眠1秒 } catch (InterruptedException e) { e.printStackTrace(); } } } } public class ThreadClass { public static void main(String[] args) { //调用threadSleep方法 threadSleep(); } /****线程休眠****/ public static void threadSleep() { //实例化一个线程 MyThread mt=new MyThread(); mt.start(); } } //输出形式:每隔1秒输出一个i值
3、线程的优先级setPriority
- 调用**setPriority()**方法,参数:[0,10]范围内的一个整数,默认是5
- 设置优先级,只是设置这个线程可以抢到CPU时间片的概率,并不是优先级高的线程一定能抢到CPU时间片(不是优先级高的线程一定先执行,也不是优先级高的线程执行完再执行其他线程)
- 设置优先级必须要放在线程开始(start)之前
public class ThreadClass { public static void main(String[] args) { threadPriority(); } /****设置线程的优先级***/ public static void threadPriority() { Runnable r=()->{ for(int i=0;i<5;i++){ System.out.println(Thread.currentThread().getName()+":"+i); } }; //1、线程实例化 Thread t1=new Thread(r,"Thread-1"); Thread t2=new Thread(r,"Thread-2"); //2、设置优先级, 必须要将该操作放在线程开始(start)之前 t1.setPriority(10); t2.setPriority(1); //3、线程启动 t1.start(); t2.start(); } } //输出结果:交替执行
4、线程的礼让yield(Run->Ready)
- 调用**yield()**方法,类方法
- 线程礼让是指让当前运行的线程释放自己的CPU资源,由运行状态,回到就绪状态。**但并不意味着一定去执行另一个线程,**此时依然是两个线程进行CPU时间片的抢夺
public class ThreadClass { public static void main(String[] args) { threadYield(); } /***线程的礼让***/ public static void threadYield() { Runnable r=()->{ for(int i=0;i<10;i++) { System.out.println(Thread.currentThread().getName()+":"+i); //线程礼让 if(i==3) { Thread.yield(); } } }; Thread t1=new Thread(r,"Thread-1"); Thread t2=new Thread(r,"Thread-2"); t1.start(); t2.start(); } } /*输出结果: Thread-2:0 Thread-2:1 Thread-2:2 Thread-2:3 //Thread-2礼让,CPU被Thread-1抢到 Thread-1:0 Thread-1:1 Thread-1:2 Thread-1:3 //Thread-1礼让,但是CPU还是被Thread-1抢到,Thread-1继续执行 Thread-1:4 Thread-1:5 Thread-1:6 Thread-1:7 Thread-1:8 Thread-1:9 //Thread-1执行完毕,Thread-2接着执行 Thread-2:4 Thread-2:5 Thread-2:6 Thread-2:7 Thread-2:8 Thread-2:9 */
5、线程合并join
- 执行join的线程,在该过程中,其他线程阻塞,待此线程执行完毕,再执行其他线程。(插队)
- 抛出InterruptException异常
public class JoinTest { public static void main(String[] args) { Runnable runnable=()->{ for(int i=0;i<100;i++) { System.out.println("vip线程"+i); } }; Thread thread=new Thread(runnable); thread.start(); //主线程输出100次 for(int i=0;i<100;i++) { /* * 当主线程运行到第50次时,调用join方法,那么此时会等join方法加入的线程执行完毕,在执行主线程 * */ if(i==50) { try { thread.join(); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println("main"+i); } } } /*输出:在50之前,主线程和子线程交替执行,但是等到主线程为50时,此时子线程会执行直到100结束,然后主线程才执行 */
6、守护线程setDaemon
- 如果所有的用户线程结束,那么守护线程会自动死亡;虚拟机不需要等待守护线程执行结束
- setDaemon默认是false,如果要设置一个线程为守护线程,则改为true即可
public class DaemonTest { public static void main(String[] args) { Runnable r1=()->{ while(true) { System.out.println("守护线程"); } }; for(int i=0;i<10;i++) { System.out.println("主线程"+i); } Thread thread=new Thread(r1); thread.setDaemon(true); //默认是false,表示用户线程 thread.start(); } } //守护线程是一个死循环,但是等待主线程执行结束后,该线程会自动停止
五、线程安全问题
临界资源:多个线程共享的资源。当多个线程同时去访问这个共享资源时,会出现线程安全问题
1、产生的原因
当一个线程在访问并操作某个资源的过程中,还没来得及完全修改该资源,CPU时间片就被其他线程抢走
//用四个线程模拟四个售票员卖票,仓库中的余票即为临界资源 class TicketCenter{ //描述剩余票的数量 public static int restCount=100; } public class SourseProblem { public static void main(String[] args) { Runnable r=()->{ //当余票大于0时,可以继续售票 while(TicketCenter.restCount>0) { System.out.println(Thread.currentThread().getName()+"卖出一张票,剩余"+ --TicketCenter.restCount+"张"); } }; //四个线程模拟四个售票员,线程名模拟售票员名 Thread t1=new Thread(r,"Thread-1"); Thread t2=new Thread(r,"Thread-2"); Thread t3=new Thread(r,"Thread-3"); Thread t4=new Thread(r,"Thread-4"); t1.start(); t2.start(); t3.start(); t4.start(); } }
输出结果:
出现临界资源问题,这是因为一个线程在计算余票的过程中,还没来的及将计算、或计算后的结果还没来得及赋给restCount,CPU就被其他线程抢走,此时其他线程中的余票是当前抢到时刻的余票值。
2、解决方法
- JVM实现的synchronized
- JDK实现的ReentrantLock
方式一:使用同步代码块
用synchronized修饰多线程需要访问的代码
class TicketCenter{ //描述剩余票的数量 public static int restCount=100; } public class SourseProblem { public static void main(String[] args) { Runnable r=()->{ //当余票大于0时,可以继续售票 while(TicketCenter.restCount>0) { //同步监视器 synchronized("") { if(TicketCenter.restCount<=0) { return; } System.out.println(Thread.currentThread().getName()+"卖出一张票,剩余"+ --TicketCenter.restCount+"张"); } } }; //四个线程模拟四个售票员,线程名模拟售票员名 Thread t1=new Thread(r,"Thread-1"); Thread t2=new Thread(r,"Thread-2"); Thread t3=new Thread(r,"Thread-3"); Thread t4=new Thread(r,"Thread-4"); t1.start(); t2.start(); t3.start(); t4.start(); } }
方法二:同步方法:使用关键字synchronized修饰的方法
将上面的同步代码段用一个方法实现
- 静态方法:同步监视器就是:当前类.class
- 非静态方法:同步监视器是 this
class TicketCenter{ //描述剩余票的数量 public static int restCount=100; } public class SourseProblem { public static void main(String[] args) { Runnable r=()->{ while(TicketCenter.restCount>0) { soldTicket(); } }; Thread t1=new Thread(r,"Thread-1"); Thread t2=new Thread(r,"Thread-2"); Thread t3=new Thread(r,"Thread-3"); Thread t4=new Thread(r,"Thread-4"); t1.start(); t2.start(); t3.start(); t4.start(); } //同步方法 public synchronized static void soldTicket(){ if(TicketCenter.restCount<=0) { return; } System.out.println(Thread.currentThread().getName()+"卖出一张票,剩余"+ --TicketCenter.restCount+"张"); } }
方式三:同步锁
显式定义同步锁对象来实现同步
class TicketCenter{ //描述剩余票的数量 public static int restCount=100; } public class SourseProblem { public static void main(String[] args) { //实例化一个锁对象 ReentrantLock rt=new ReentrantLock(); Runnable r=()->{ while(TicketCenter.restCount>0) { //对临界资源上锁 rt.lock(); if(TicketCenter.restCount<=0) { return; } System.out.println(Thread.currentThread().getName()+"卖出一张票,剩余"+ --TicketCenter.restCount+"张"); //对临界资源解锁 rt.unlock(); } }; Thread t1=new Thread(r,"Thread-1"); Thread t2=new Thread(r,"Thread-2"); Thread t3=new Thread(r,"Thread-3"); Thread t4=new Thread(r,"Thread-4"); t1.start(); t2.start(); t3.start(); t4.start(); } }
3、死锁
多个线程彼此持有对方所需要的锁,而不释放自己的锁
//线程A、B互相等待对方释放拥有的锁 public class DeadLock { public static void main(String[] args) { Runnable runnable1=()->{ synchronized("A"){ System.out.println("A线程持有了A锁,等待B锁"); //此时A线程已经持有A锁了,让它继续持有B锁 /*为了确保产生死锁 try { Thread.sleep(1000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); }*/ synchronized("B"){ System.out.println("A线程持有了A锁和B锁"); } } }; Runnable runnable2=()->{ synchronized("B"){ System.out.println("B线程持有了B锁,等待A锁"); //此时B线程已经持有B锁了,让它继续去持有A锁 synchronized("A"){ System.out.println("B线程持有了A锁和B锁"); } } }; Thread t1=new Thread(runnable1); Thread t2=new Thread(runnable2); t1.start(); t2.start(); } } /*输出结果: B线程持有了B锁,等待A锁 A线程持有了A锁,等待B锁 (程序未结束) */
上述代码其实不能完全产生死锁,如果在A线程获取B锁之前,B线程都没有获得执行机会,那么B线程就不会获取到B锁,此时程序依然会执行,不会产生死锁。为了一定产生死锁情况,可以在A线程执行过程中调用一个sleep方法。
4、线程通信:解决死锁的办法
方式1:synchronized下的通信
- wait():等待,当前的线程释放对同步监视器的锁定,并且让出CPU资源,使得当前的线程进入等待队列中
- notify():通知,唤醒在此同步监视器上等待的一个线程(具体哪一个由CPU决定),使这个线程进入锁池
- notifyAll():通知,唤醒在此同步监视器上等待的所有线程,使这些线程进入锁池
public class DeadLock { public static void main(String[] args) { Runnable runnable1=()->{ synchronized("A"){ System.out.println("A线程持有了A锁,等待B锁"); //A线程释放A锁(捕获异常) try { "A".wait(); } catch (InterruptedException e) { e.printStackTrace(); } synchronized("B"){ System.out.println("A线程持有了A锁和B锁"); } } }; Runnable runnable2=()->{ synchronized("B"){ System.out.println("B线程持有了B锁,等待A锁"); synchronized("A"){ System.out.println("B线程持有了A锁和B锁"); //此时B线程已经执行完成了,但是A线程任然还在等待,因此需要唤醒A线程 "A".notify(); } } }; Thread t1=new Thread(runnable1); Thread t2=new Thread(runnable2); t1.start(); t2.start(); } } /*输出结果: A线程持有了A锁,等待B锁 B线程持有了B锁,等待A锁 B线程持有了A锁和B锁 A线程持有了A锁和B锁 */
方式2:Lock锁下的通信,采用Condition控制通信。JUC中的类(java.util.comcurrent类)
- await():等价于wait()
- signal():等价于notify()
- signalAll():等价于notifyAll()
4、多线程下的单例类
懒汉式单例类会出现问题
//定义一个单例类 class Boss{ //构造器私有化 private Boss() { System.out.println("一个Boss对象被实例化了"); } private static Boss instance=null; //外部类只能通过该方法获取Boss类的实例 public static Boss getBoss() { if(instance==null) { instance=new Boss(); } return instance; } } public class SingletonTest { public static void main(String[] args) { Runnable runnable=()->{ Boss.getBoss(); }; //开辟了100条线程去获取这Boss实例 for(int i=0;i<100;i++) { new Thread(runnable).start(); } } }
当多线程去执行这个单例类时,还是希望只产生一个实例对象,但程序输出结果明显不是,这是由于多线程导致的。
修改方式1:对临界资源上锁,使用同步代码
//定义一个单例类 class Boss{ //构造器私有化 private Boss() { System.out.println("一个Boss对象被实例化了"); } private static Boss instance=null; public static Boss getBoss() { //同步代码段 synchronized("") { if(instance==null) { instance=new Boss(); } } return instance; } } public class SingletonTest { public static void main(String[] args) { Runnable runnable=()->{ Boss.getBoss(); }; //开辟了100条线程去获取这Boss实例 for(int i=0;i<100;i++) { new Thread(runnable).start(); } } }
修改方式2:对临界资源上锁,使用同步方法
class Boss{ //构造器私有化 private Boss() { System.out.println("一个Boss对象被实例化了"); } private static Boss instance=null; //同步方法 public static synchronized Boss getBoss() { if(instance==null) { instance=new Boss(); } return instance; } } public class SingletonTest { public static void main(String[] args) { Runnable runnable=()->{ Boss.getBoss(); }; //开辟了100条线程去获取这Boss实例 for(int i=0;i<100;i++) { new Thread(runnable).start(); } } }
六、线程池
线程池在系统启动时就创建大量空闲的线程。提前创建多个线程,放入线程池,使用时直接从线程池中获取,使用完放回池中
1、作用
可以避免频繁创建销毁线程的过程,实现充分利用
- corePoolSize:核心池的大小(可以放多少个线程)
- maximumPoolSize:最大线程数(一次可以同时运行的线程数量)
- keepAliveTime:线程没有任务时最多保持多长时间后会终止
2、创建方式
- ExecutorService接口:线程池真正的接口
- Executor:创建线程的工具类,调用该类的newFixedThreadPool(corePoolSizesize)方法来创建线程池
- execute:执行Runnable接口的,无返回值
- Future submit:执行Callable接口的,有返回值
- shutdown:关闭连接
import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class ThreadPoolTest { public static void main(String[] args) { Runnable r=()->{ System.out.println(Thread.currentThread().getName()); }; //创建线程池,设置大小为10 ExecutorService service=Executors.newFixedThreadPool(10); //执行 service.execute(r); service.execute(r); service.execute(r); service.execute(r); //关闭连接 service.shutdown(); } } /*输出结果: pool-1-thread-3 pool-1-thread-4 pool-1-thread-2 pool-1-thread-1 */
七、JUC组件
1、未来任务FutureTask
利用Callable创建线程时,有返回值,该值由Future进行封装,FutureTask实现了RunnableFuture接口,而该接口继承自Runnable和Future接口,因此FutureTask既可以当做一个任务执行,也可以有返回值。
当计算一个任务需要很长时间时,可使用FutureTask来封装这个任务,使得主线程在完成自己的任务后在去获取这个计算结果
import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.FutureTask; public class FutureTaskTest { public static void main(String[] args) { //创建一个Clallable接口,有返回值,给子线程执行 Callable<Integer> cla=()->{ int result=0; for(int i=0;i<100;i++) { Thread.sleep(10); //每一次计算时都让让主线程执行一段时间 result+=i; } return result; }; //新建一个FutureTask实例 FutureTask<Integer> futureTask=new FutureTask<>(cla); //执行计算任务的线程 Thread t1=new Thread(futureTask); t1.start(); //创建Runnable接口,给主线程执行 Runnable runnable=()->{ System.out.println("主线程任务正在执行"); try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } }; Thread t2=new Thread(runnable); t2.start(); //得到有返回值的输出 try { System.out.println(futureTask.get()); } catch (InterruptedException | ExecutionException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } /*输出结果: 另一个线程任务正在执行 4950 */ //如果将Callable执行体中的Thread.sleep(10);去掉,则执行结果为:4950 另一个线程任务正在执行。
2、阻塞队列BlockingQueue
利用BlockingQueue作为线程同步的工具,主要用来实现消费者生产者设计模式。详见《生产者消费者设计模式》
3、叉链接ForkJoin
主要用于并行计算中,将大的任务分成小的任务进行计算,再把小任务的结果合并成总的计算结果
-
-
进程、线程、多线程、并发、并行 详解
2019-04-30 14:09:42文章目录进程、线程、多线程、并发、并行#1 进程#2 线程#3 多进程#4 多线程#5 并发#6 并行#7 通过多线程实现并发,并行 进程、线程、多线程、并发、并行 首先,并行与并发都是程序多线程处理场景,因此,一旦提到... -
实验三 观察Linux进程线程的异步并发执行
2021-05-27 17:29:03实验三 观察Linux进程线程的异步并发执行 一、实验目的 通过本实验学习如何创建Linux进程及线程,通过实验,观察Linux进程及线程的异步执行。理解进程及线程的区别及特性,进一步理解进程是资源分配单位,线程是独立... -
进程、线程、并发执行的概念
2017-11-16 15:29:07于进程、线程、并发执行的概念,我们先来看下面的一段话:“一般来说,当运行一个应用程序的时候,就启动了一个进程,当然有些会启动多个进程。启动进程的时候,操作系统会为进程分配资源,其中最主要的资源是内存... -
【多线程高并发编程】一进程和线程(并发和并行)
2020-02-15 19:45:35了解并发和并行,进程和线程的一些概念,更好的学习多线程编程 -
Java中的多线程与高并发
2021-11-22 17:20:53线程是系统调度的最小单元,是线程的运算单元,一个进程可以包括一个或多个线程。 线程的三种创建方式: 一、继承Thread类 class MyThread extends Thread { @Override public void run() { Log.i(TAG, ... -
C++11的多线程并发编程(一)
2021-01-20 13:16:55在疫情期间以及未来毕业后即将工作的憧憬中,给自己立个学习flag,学习并第一次通过CSDN记录下C++11的多线程并发编程。初学者的我写的不好,还望大家多多指正与批评, 学习多线程并发编程首先一定得熟悉并发相关概念... -
Java—并发编程—-线程创建方式
2020-12-20 23:15:11Java通过对多线程的支持来在一个进程内并发执行多个线程,每个线程都并行执行不同的任务。 二、线程创建方式 一共四种方式:继承Thread类、实现Runnable接口、ExecutorService和Call(有返回Class类型值)、基于... -
多线程&并发-实例与解决方案
2020-06-23 11:27:24Java的线程状态被定义在公共枚举类java.lang.Thread.state中。一种有六种状态 1.新建(NEW):表示线程新建出来还没有被启动的状态,比如:Thread t = new MyThread(); 2.就绪/运行(RUNNABLE):该状态包含了经典...