t.start() for t in ts: t.join() 我们使用start启动多线程,使用 join 防止主线程退出的时候结束所有的线程,使用队列有序的且并发的下载壁纸 ) 输出: 进程2624 打印 0 进程2625 打印 1 进程2626 打印 3 进程2627 打印 2 进程2624 打印 4 进程2625 打印 5 进程2626 打印 6 进程2627 打印 7 正在消耗:9 时间:2019-01-09 12:50:48.708355 进程2804: 线程3 正在消耗:8 时间:2019-01-09 12:50:48.708355 进程2804: 线程2 正在消耗:7 www.liaoxuefeng.com/wiki/0014316089557264a6b348958f449949df42a6d3a2e542c000/001431929340191970154d52b9d484b88a7b343708fcc60000
status状态有6种:new, runnable, blocked, waiting, time waiting, terminated
CountDownLatch 是多线程控制JUt(java.util.concurrent.CountDownLatch)的一个工具类,它被称为 门阀 、 计数器 或者 闭锁 。这个工具经常用来用来协调多个线程之间的同步,或者说起到线程之间的通信(而不是用作互斥的作用)
JDK对ThreadLocal的定义如下: TheadLocal提供了线程内部的局部变量:每个线程都有自己的独立的副本;ThreadLocal实例通常是类中的private static字段,该类一般与线程状态相关(或线程上下文)中使用。只要线程处于活动状态且ThreadLocal实例时可访问的状态下,每个线程都持有对其线程局部变量的副本的隐式引用,在线程消亡后,ThreadLocal实例的所有副本都将进行垃圾回收。
异步编程 Python3.4后新增了asyncio模块,支持异步编程。异步是在一个线程中通过任务切换的方式让多个任务”同时“进展。 number in range(10) ]) print("results=", results) asyncio.run(main()) 运行结果: 6 8 9 5 0 7 3 4 1 2 results= [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] asyncio.gather() 用于收集多个协程以并发执行它们。 异步编程的实例 网络IO是一个合适用异步编程处理的任务,可惜requests库没有提供异步请求的方法,不过aiohttp提供了异步 HTTP方法 。
7. 线程安全 7.1 线程安全的定义 如果多线程下使用这个类,不过多线程如何使用和调度这个类,这个类总是表示出正确的行为,这个类就是线程安全的。 广泛用于JDK并发容器的实现中。 使用Lock实现(推荐) 并发 Lock 实现,如 ReentrantLock 还支持非阻塞式的获取锁操作 tryLock(),这是一个插队行为(barging),并不在乎等待的公平性,如果执行时对象恰好没有被独占 无锁 对于并发控制而言,锁是一种悲观的策略,它总是假设每一次的临界区操作会产生冲突,由此,如果有多个线程同时需要访问临界区资源,则宁可牺牲资源让线程进行等待。 读写分离锁来替换独占锁 在读多写少的场合,使用读写锁可以有效提升系统的并发能力 锁分离 如果将读写锁的思想进一步的延伸,就是锁分离.读写锁根据读写锁操作功能上的不同,进行了有效的锁分离.使用类似的思想,
PriorityQueue,这是一个(非并发的)优先队列。如上两个队列的操作不会阻塞,如果队列为空,那么获取元素的操作将返回空值。 如下:LinkedBlockingQueue 和 ArrayBlockingQueue 是 FIFO 队列,二者分别与 LinkedList 和 ArrayList 类似,但比同步 List 拥有更好的并发性能
我们可以把并发执行的任务传递给一个线程池,来替代为每个并发执行的任务都启动一个新的线程。只要池里有空闲的线程,任务就会分配给一个线程执行。 线程池的线程会并发的处理连接上的请求。 简单来说使用线程池有以下几个目的: 线程是稀缺资源,不能频繁的创建。 线程池可有效控制最大并发线程数,提高系统资源利用率,同时可以避免过多资源竞争,避免阻塞 线程池可提供定时执行、定期执行、单线程以及并发数控制等功能 直接new Thread的弊端: 每次new Thread 但是如果线程池的容量设置的过大,提高任务的数量过多的时候,并发量会增加,那么线程之间的调度就是一个需要考虑的问题,这样反而可能会降低处理任务的吞吐量。 接下来用一个例子演示一下如何通过ThreadPoolExecutor来创建线程池,这里使用7个参数的构造函数,示例代码如下: package org.zero.concurrency.demo.example.threadpool
类似于我们集合中有同步类容器 和 并发类容器,HashMap也是完全排他的,即使是读也只能同步执行,而ConcurrentHashMap就可以实现同一时刻多个线程之间并发。
其实,面对这样的面试要求,现实中的头部大厂,甚至一些普通大厂都是设计了很多编程题考查大家的基础功底。但是都不会很复杂,毕竟时间有限,往往都是经典题目,涉及一个或多个核心关键技术点。 这个题目考察的就是并发编程,多个线程并发执行,但是共享资源有限,需要阻塞等待,或者自旋竞争锁。其实如果不限制代码行数,我们有非常多的方式去实现。 首先Semaphore是JUC包提供的一个并发工具类,功能是:支持以及限制多个线程同时访问共享资源。 之前我们说《synchronized全能王的原理》和可重入锁《ReentrantLock核心原理剖析》都是限制仅允许一个线程访问共享资源,确保并发的原子性、有序性、可见性。
JAVA媒体提供任务机制来安全的终止线程。但是它提供了中断(interruption),这是一种写作机制,能够使一个线程终止另外一个线程。 一般来说没人希望立即终止,因为必要时总要先清理再终止。 开发一个应用能够妥善处理失败、关闭、取消等过程非常重要也有挑战。 7.1 任务取消 一定不要使用Thread.stop和suspend这些机制。 一种协作机制就是“标记位”。例如使用volatile类型的field来保存取消状态。 @ThreadSafe public class PrimeGenerator i
并发 / 并行编程几乎都这样,这不是Java特有的 - 这是并发和并行编程的本质。纯函数式语言也救不了? 并发 / 并行编程的本质就是这样 —— 看似是 “同时干多件事”,实则处处是坑,必须精打细算。 函数式语言:部分编程语言让并发任务彼此隔离,如 Erlang,还包含任务间安全通信机制,适合程序中大量使用并发的部分。java并发的四句格言1.不要这样做避免自己动手处理并发问题。 并发编程需关注细节:非并发程序中可忽略的细节,在并发程序中变得至关重要。例如,需了解处理器缓存及本地缓存与主内存的一致性问题,深入掌握对象构造的复杂性以避免构造器意外暴露数据给其他线程修改等。 认知偏差与误区:从确定性编程转向并发编程时,容易出现 “邓宁 - 克鲁格效应”,即不熟练的人会错误高估自己的能力。
并发编程 操作系统的进化 传统的纸带输入 磁带的存储降低了输入输出数据占用的时间,提高了CPU的利用率 多道操作系统的出现:提高了CPU的利用率,单纯的切换会浪费时间 a) 一台计算机上同时可以出现多个任务 网络操作系统 7. 分布式操作系统(多任务分给子系统处理) 并发和并行 并发:多个程序交替在同一个CPU上被计算 并行:多个程序同时在多个CPU上被计算 阻塞与非阻塞 CPU是否在工作 conn) server = socketserver.ThreadingTCPServer(('127.0.0.1', 9999), Myserver) server.serve_forever() 并发编程之多进程 multiprocessing模块用来开启子进程,并在子进程中执行我们定制的任务(比如函数),该模块与多线程模块threading的编程接口类似。
,可能会表现出意外的行为,最经典的便是ConcurrentModificationException,原因是当容器迭代的 过程中,被并发的修改了内容,这是由于早期迭代器设计的时候并没有考虑并发修改的问题 并发类容器 jdk5.0以后提供了多种并发类容器来替代同步类容器从而改善性能.同步类容器的状态都是串行化的.他们虽然实现了线程安全,但是严重降低了并发性,在多线程环境时, 严重降低了应用程序的吞吐量 并发类容器时专门针对并发设计的,使用ConcurrentHashMap来代替给予散列的传统的HashTable,而且在ConcurrnetHashMap中,添加了一些常见符复合操作的支持.以及使用 了CopyOnWriteArrayList代替Voctor,并发的CopyonWriteArraySet,以及并发的Queue,ConcurrentLinkedQueue和LinkedBlockingQueue 并发Queue 在并发队列上JDK提供了两套实现,一个是以ConcurrentLinkedQueue为代表的高性能队列,一个是以BlockingQueue接口为代表的阻塞队列,无论哪种继承自Queue
private void doTest(Request request) { printProcessor.processRequest(request); } } 并发编程的基础
本章将着重介绍Java并发编程的基础知识,从启动一个线程到线程间不同的通信方式,最后通过简单的线程池示例以及应用(简单的Web服务器)来串联本章所介绍的内容。 (3)更好的编程模型 Java为多线程编程提供了良好、考究并且一致的编程模型,使开发人员能够更加专注于问题的解决,即为所遇到的问题建立合适的模型,而不是绞尽脑汁地考虑如何将其多线程化。 一旦开发人员建立好了模型,稍做修改总是能够方便地映射到Java提供的多线程编程模型上。
一、前言 编写正确的程序本身就不容易,编写正确的并发程序更是难中之难,那么并发编程究竟难道哪里那?本节我们就来一探究竟。 运行main函数后,启动的进程里面存在两个并发运行的线程,分别是开启的新goroutine(起名为goroutine2)和main函数所在的goroutine(起名为goroutine1),前者试图修改共享变量 也就是这1000个线程的并发行使用锁转换为了串行执行,也就是对共享内存变量的访问施加了同步措施。 四、总结 本文我们从数据竞争、原子性操作、内存同步三个方面探索了并发编程到底难在哪里,后面章节我们会结合go的内存模型和happen-before原则在具体探索这些难点如何解决。
1.并发编程基础 1.1 基本概念 CPU核心与线程数关系 Java中通过多线程的手段来实现并发,对于单处理器机器上来讲,宏观上的多线程并行执行是通过CPU的调度来实现的,微观上CPU在某个时刻只会运行一个线程 关系:一个线程可以创建和撤销另一个线程;同一个进程中的多个线程之间可以并发执行.相对进程而言,线程是一个更加接近于执行体的概念,它可以与同进程中的其他线程共享数据,但拥有自己的栈空间,拥有独立的执行序列 但对于一些要求同时进行并且又要共享某些变量的并发操作,只能用线程,不能用进程。 优缺点:线程和进程在使用上各有优缺点:线程执行开销小,但不利于资源的管理和保护;而进程正相反。 并行和并发 并行:同一时刻,可以同时处理事情的能力。 并发:与单位时间相关,在单位时间内可以处理事情的能力。 高并发编程的意义和注意事项 意义和好处:充分利用cpu的资源、加快用户响应的时间,程序模块化,异步化 问题。
降低系统的响应时间 并发编程缺点 并发代码容易出错,不好调试,很容易产生概率性的、难以复现的Bug 线程有创建和上下文切换的开销(实验表明,当并发执行累加操作不超过百万次时,速度会比串行执行累加操作要慢 ) 什么是并发编程 并发编程领域可以抽象成三个核心问题:分工、同步和互斥 分工 从性能角度讲,我们为了提高执行一定计算机任务的效率,所以IO等待的时候不能让cpu闲着,所以我们把任务拆分交替执行,有了分时操作系统 并发和通信带来了较高的编程复杂度,同时也出现了多线程并发操作共享资源的问题。于是天下大势,分久必合,我们又要将对共享资源的访问串行化。 ),为了解决通信带来的安全性问题,我们引入了互斥 如何学好并发编程 方法论层面: 「跳出来,看全景」三个核心问题:分工、同步和互斥 「钻进去,看本质」JSR-133 构建并发领域全景图 稍微了解过并发领域知识的人都知道 计算机大师就在思考.能不不加锁也能实现并发.还不容易出错,于是就有了:CAS、copy-on-write等技术思想,这就是实现了「无锁」并发; 统一模型 通过思考我们发现 如果要实现一个好的并发编程,
从上篇文章 并发编程1:全面认识 Thread 我们了解了 Java 中线程的基本概念和关键方法。 在开始使用线程之前,我觉得我们有必要先了解下多线程给我们带来的好处与可能造成的损失,这样才能在合适的地方选用合适的并发策略。 多线程的优点 ? 1:提高资源利用率 “一口多用”其实就是一种多线程。 这里先简单地对“Java 内存模型”进行介绍,后序介绍完常见并发类后再详细总结。 多线程可能出现的问题 通过上述介绍,我们可以知道,如果多个线程共享一个对象,每个线程在自己的栈中会有对象的副本。 总结 本篇文章结合 Java 内存模型简单介绍了多线程开发的优点与可能导致的问题,犹豫了一下我还是觉得有必要在开始学习 Java 各种并发 API 之前了解它们出现的背景,这样更容易明白它们解决了什么问题