多线程

线程之间的通信

独自空忆成欢 提交于 2020-04-07 19:58:29
一、为什么要线程通信? 1. 多个线程并发执行时, 在默认情况下CPU是 随机切换线程 的,当我们需要多个线程来 共同完成 一件任务,    并且我们希望他们 有规律 的执行, 那么多线程之间需要一些 协调通信 ,以此来帮我们达到 多线程共同操作一份数据 。 2.当然如果我们没有使用线程通信来使用多线程共同操作同一份数据的话,虽然可以实现,   但是在很大程度会造成多线程之间对同一共享变量的争夺,那样的话势必为造成很多错误和损失! 3.所以,我们才引出了线程之间的通信, 多线程之间的通信能够避免对同一共享变量的争夺。 二、什么是线程通信?   多个线程在处理 同一个资源 ,并且任务不同时,需要线程通信来帮助解决线程之间对同一个变量的 使用或操作 。   就是 多个线程在操作同一份数据时, 避免对同一共享变量的争夺 。   于是我们引出了 等待唤醒机制 :( wait() 、 notify() )   就是在一个线程进行了规定操作后,就进入等待状态( wait ), 等待其他线程执行完他们的指定代码过后 再将其唤醒( notify ); 1. 多个线程并发执行时, 在默认情况下CPU是 随机切换线程 的,当我们需要多个线程来 共同完成 一件任务,    并且我们希望他们 有规律 的执行, 那么多线程之间需要一些 协调通信 ,以此来帮我们达到 多线程共同操作一份数据 。 2

jvm主内存与工作内存

白昼怎懂夜的黑 提交于 2020-04-07 19:49:08
一、jvm主内存与工作内存 首先,JVM将内存组织为主内存和工作内存两个部分。 主内存主要包括本地方法区和堆。每个线程都有一个工作内存,工作内存中主要包括两个部分,一个是属于该线程私有的栈和对主存部分变量拷贝的寄存器(包括程序计数器PC和cup工作的高速缓存区)。 1.所有的变量都存储在主内存中(虚拟机内存的一部分),对于所有线程都是共享的。 2.每条线程都有自己的工作内存,工作内存中保存的是主存中某些变量的拷贝,线程对变量的所有操作都必须在工作内存中进行,而不能直接读写主内存中的变量。 3.线程之间无法直接访问对方的工作内存中的变量,线程间变量的传递均需要通过主内存来完成。 JVM规范定义了线程对内存间交互操作: Lock(锁定):作用于主内存中的变量,把一个变量标识为一条线程独占的状态。 Read(读取):作用于主内存中的变量,把一个变量的值从主内存传输到线程的工作内存中。 Load(加载):作用于工作内存中的变量,把read操作从主内存中得到的变量的值放入工作内存的变量副本中。 Use(使用):作用于工作内存中的变量,把工作内存中一个变量的值传递给执行引擎。 Assign(赋值):作用于工作内存中的变量,把一个从执行引擎接收到的值赋值给工作内存中的变量。 Store(存储):作用于工作内存中的变量,把工作内存中的一个变量的值传送到主内存中。 Write(写入)

Python爬虫案例演示:Python多线程、多进程、协程

安稳与你 提交于 2020-04-07 15:32:41
很多时候我们写了一个爬虫,实现了需求后会发现了很多值得改进的地方,其中很重要的一点就是爬取速度。本文 就通过代码讲解如何使用 多进程、多线程、协程 来提升爬取速度。注意:我们不深入介绍理论和原理,一切都在代码中。 二、同步 首先我们写一个简化的爬虫,对各个功能细分,有意识进行函数式编程。下面代码的目的是访问300次百度页面并返回状态码,其中 parse_1 函数可以设定循环次数,每次循环将当前循环数(从0开始)和url传入 parse_2 函数。 import requestsdef parse_1(): url = 'https://www.baidu.com' for i in range(300): parse_2(url)def parse_2(url): response = requests.get(url) print(response.status_code)if __name__ == '__main__': parse_1() 性能的消耗主要在IO请求中,当单进程单线程模式下请求URL时必然会引起等待 示例代码就是典型的串行逻辑, parse_1 将url和循环数传递给 parse_2 , parse_2 请求并返回状态码后 parse_1 继续迭代一次,重复之前步骤 三、多线程 因为CPU在执行程序时每个时间刻度上只会存在一个线程

java中线程队列BlockingQueue的用法

試著忘記壹切 提交于 2020-04-07 11:09:48
在新增的Concurrent包中,BlockingQueue很好的解决了多线程中,如何高效安全“传输”数据的问题。通过这些高效并且线程安全的队列类,为我们快速搭建高质量的多线程程序带来极大的便利。本文详细介绍了BlockingQueue家庭中的所有成员,包括他们各自的功能以及常见使用场景。 [ @more @] 认识BlockingQueue 阻塞队列,顾名思义,首先它是一个队列,而一个队列在数据结构中所起的作用大致如下图所示: 从上图我们可以很清楚看到,通过一个共享的队列,可以使得数据由队列的一端输入,从另外一端输出; 常用的队列主要有以下两种:(当然通过不同的实现方式,还可以延伸出很多不同类型的队列,DelayQueue就是其中的一种)   先进先出(FIFO):先插入的队列的元素也最先出队列,类似于排队的功能。从某种程度上来说这种队列也体现了一种公平性。   后进先出(LIFO):后插入队列的元素最先出队列,这种队列优先处理最近发生的事件。 多线程环境中,通过队列可以很容易实现数据共享,比如经典的“生产者”和“消费者”模型中,通过队列可以很便利地实现两者之间的数据共享。假设我们有若干生产者线程,另外又有若干个消费者线程。如果生产者线程需要把准备好的数据共享给消费者线程,利用队列的方式来传递数据,就可以很方便地解决他们之间的数据共享问题。但如果生产者和消费者在某个时间段内

Java里阻塞线程的三种实现方法

丶灬走出姿态 提交于 2020-04-07 10:50:09
在日常开发中,我们有时会遇到遇到多线程处理任务的情况,JDK里提供了便利的ThreadPoolExecutor以及其包装的工具类Executors。但是我们知道ExecutorService.excute(Runnable r)是异步的,超过线程池处理能力的线程会被加入到执行队列里。有时候为了保证任务提交的顺序性,我们不希望有这个执行队列,在线程池满的时候,则把主线程阻塞。那么,怎么实现呢? 最直接的想法是继承ThreadPoolExecutor,重载excute()方法,加入线程池是否已满的检查,若线程池已满,则等待直到上一个任务执行完毕。这里ThreadPoolExecutor提供了一个afterExecute(Runnable r, Throwable t)方法,每个任务执行结束时会调用这个方法。 同时,我们会用到concurrent包的ReentrantLock以及Condition.wait/notify方法。以下是实现代码(代码来自: http://www.cnblogs.com/steeven/archive/2005/12/08/293219.html ): <!-- lang: java --> private ReentrantLock pauseLock = new ReentrantLock(); private Condition unpaused =

清明花了几天总结了多线程的知识点

家住魔仙堡 提交于 2020-04-07 09:53:11
前言 只有光头才能变强。 文本已收录至我的GitHub精选文章,欢迎Star : https://github.com/ZhongFuCheng3y/3y 在上周总结了一篇「工作中常用到的Java集合类」,反响还不错。这周来写写Java另一个重要的知识点:「 多线程 」 多线程大家在初学的时候,对这个知识点应该有不少的疑惑的。我认为主要原因有两个: 多线程在初学的时候不太好学,并且一般写项目的时候也很少用得上(至少在初学阶段时写的项目基本不需要自己创建线程)。 多线程的知识点在面试经常考,多线程所涉及的知识点非常多,难度也不低。 这就会给人带来一种感觉「 这破玩意涉及的东西是真的广,平时也不怎么用,怎么面试就偏偏爱问这个鬼东西 」 不多BB,我要开始了。 为什么使用多线程? 首先,我们要明确的是「为什么要使用多线程」,可能有人会认为「 使用多线程就是为了加快程序运行的速度啊 」。如果你是这样回答了,那面试官可能会问你「那多线程是怎么加快程序运行速度的?」 于我的理解:使用多线程最主要的原因是 提高系统的资源利用率 。 现在CPU基本都是多核的,如果你只用单线程,那就是只用到了一个核心,其他的核心就相当于空闲在那里了。 厕所的坑位有5个,如果只用一个坑位,那不是很亏?比如现在我有5个人要上厕所。 在单线程的时候:进去一个人解决要10分钟,然后后面的人都得等一个坑位

ios atomic nonatomic区别

血红的双手。 提交于 2020-04-07 03:35:38
atomic和nonatomic用来决定编译器生成的getter和setter是否为原子操作。 atomic 设置成员变量的@property属性时,默认为atomic,提供多线程安全。 在多线程环境下,原子操作是必要的,否则有可能引起错误的结果。加了atomic,setter函数会变成下面这样: {lock} if (property != newValue) { [property release]; property = [newValue retain]; } {unlock} 禁止多线程,变量保护,提高性能。 atomic是Objc使用的一种线程保护技术,基本上来讲,是防止在写未完成的时候被另外一个线程读取,造成数据错误。而这种机制是耗费系统资源的,所以在iPhone这种小型设备上,如果没有使用多线程间的通讯编程,那么nonatomic是一个非常好的选择。 指出访问器不是原子操作,而默认地,访问器是原子操作。这也就是说,在多线程环境下,解析的访问器提供一个对属性的安全访问,从获取器得到的返回值或者通过设置器设置的值可以一次完成,即便是别的线程也正在对其进行访问。如果你不指定 nonatomic ,在自己管理内存的环境中,解析的访问器保留并自动释放返回的值,如果指定了 nonatomic ,那么访问器只是简单地返回这个值。 来源: oschina 链接: https:/

多线程学习笔记一

戏子无情 提交于 2020-04-07 02:25:23
关于线程的常见问题- 什么是并发与并行? 并行:两个或者多个事件在同一时刻发生 并发:两个或者多个事件在同一个时间段内发生 什么是进程与线程? 进程是正在运行的程序的实例 进程是线程的容器,一个进程可以开启多个线程 比如打开一个浏览器,会创建进程 线程如何创建? 继承Thread类 实现Runnable接口 实现Callable接口鸭 从线程池-Excutor 获取线程 实现类接口和继承类接口的比较:    接口更适合多个相同的程序代码去共享同一个资源   接口可以避免java中单继承的局限性   接口代码可以被多个线程共享,代码和线程独立   线程池只能 放入实现Runnable和Callable接口的线程,不能直接放入继承Thread的线程    java中,每次运行程序至少启动2个线程,一个是main线程,一个是垃圾收集线程 线程的生命周期? 什么是线程安全问题?线程安全问题解决方案? 线程安全问题都是由全局变量及静态变量引起的 若每个线程对全局变量,静态变量只读,不写,则这个变量是线程安全的 若有多个线程同时执行写操作,则需要考虑线程同步,否则就可能影响线程安全      解决方案 :线程同步 - 只要在某个线程修改共享资源的时候,其他线程不能修改该资源,等待修改完毕同步之后,才能去抢夺资源,完成对应的操作,保证了数据的同步性。 java引入了7种同步机制: 同步代码块

单核cpu多线程有必要吗?

こ雲淡風輕ζ 提交于 2020-04-07 01:32:08
问题分析 现代计算机一般都是多核cpu,多线程的可以大大提高效率,但是可能会有疑问,那单核CPU使用多线程是不是没有必要了,假定一种情况, web应用服务器,单核CPU、单线程 ,用户发过来请求,单个线程处理,CPU等待这个线程的处理结果返回,查询数据库,CPU等待查询结果...,只有一个线程的话,每次线程在处理的过程中CPU都有大量的空闲等待时间,那这样来说并行和串行似乎并没有体现并行的优势,因为任务的总量在那里,实际情况肯定不是这样的,即便是单核CPU,一个进程中往往也是有多个线程存在的,每个线程各司其职,CPU来调度各线程。 这里需要区分CPU处理指令和IO读取的不同,CPU的执行速度要远大于IO的过程,因此在大多数情况下多一些复杂的CPU计算都比增加一次IO要快,这一块深入理解要学习计算机原理相关的知识。 现实生活中也是有很多类似的例子,比如厨师做一道菜,买菜和买配料需要去不同的两个商店,如果这个过程只依靠他一个人来做,那耗费的总时间就是买菜再去买调料的总时间,如果有一个帮厨,那么就可以兵分两路,再来汇总结果,时间基本可以减半,厨师和帮厨就是不同的线程。 编程是高度抽象生活的一门艺术。 场景模拟 模拟单线程和多线程的效率差距,这里使用连接数据库,和读取磁盘文件来模拟IO操作,期望结果: 单线程总耗时:数据库连接耗时 + 磁盘文件读取耗时 多线程总耗时

多线程:硬件基础

懵懂的女人 提交于 2020-04-06 19:04:11
多线程:硬件基础 目录 多线程:硬件基础 一、高速缓存 二、缓存一致性协议 三、写缓冲器 一、高速缓存 现代CPU的处理性能是远远高于主内存,一次内存读或者写操作的时间内,CPU能够处理上百条指令。为了弥补CPU与内存之间性能的鸿沟,引入高速缓存(Cache)。 高速缓存的性能远远高于主内存,但是容量要小于主内存 。每个处理器都有其独立的高速缓存。有了高速缓存之后,处理器在做内存读写操作时就不必直接与主内存打交道,而是与缓存打交道(从缓存中读或写)。 缓存的数据结构相当于一个散列表,由若干桶和缓存条目组成,缓存条目又可以细分成三个部分:Data Block(缓存行)中存储了从内存中读取的或者将要写入内存的数据;Tag储存了缓存行中数据相应的内存地址的部分信息(内存地址的高位部分比特);Flag用于表示缓存行的状态信息,有哪些中状态值下面会进一步说明。 那么,处理器如果想操作储存在主内存地址A的变量时,具体时如何与缓存打交道的呢? 处理器把地址A解码为index,tag,offset三个数据。 首先通过index值定位到桶。 然后通过tag值比对定位到具体的缓存条目。 最后根据offset偏移量的值定位到缓存行中该变量的起始位置。 根据上述步骤如如果能够定位到相应的缓存行且所在缓存条目的Flag值表示缓存条目是有效的,我们就称相应的内存操作产生了 缓存命中 ,相反则称为 缓存未命中