线程阻塞

Java并发编程笔记之ArrayBlockingQueue源码分析

守給你的承諾、 提交于 2020-03-30 08:48:26
JDK 中基于数组的阻塞队列 ArrayBlockingQueue 原理剖析,ArrayBlockingQueue 内部如何基于一把独占锁以及对应的两个条件变量实现出入队操作的线程安全? 首先我们先大概的浏览一下ArrayBlockingQueue 的内部构造,如下类图: 如类图所示,可以看到ArrayBlockingQueue 内部有个数组items 用来存放队列元素,putIndex变量标示入队元素的下标,takeIndex是出队的下标,count是用来统计队列元素个数, 从定义可以知道,这些属性并没有使用valatile修饰,这是因为访问这些变量的使用都是在锁块内被用。而加锁了,就足以保证了锁块内变量的内存可见性。 另外还有个独占锁lock 用来保证出队入队操作的原子性,这保证了同时只有一个线程可以进行入队出队操作,另外notEmpty,notFull条件变量用来进行出队入队的同步。 由于ArrayBlockingQueue 是有界队列,所以构造函数必须传入队列大小的参数。 接下来我们进入ArrayBlockingQueue的源码看,如下: public ArrayBlockingQueue(int capacity) { this(capacity, false); } public ArrayBlockingQueue(int capacity, boolean fair

sleep() 和 wait() 有什么区别?

半城伤御伤魂 提交于 2020-03-30 03:57:22
sleep() 和 wait() 有什么区别? sleep()和wait()都是线程暂停执行的方法。 1、这两个方法来自不同的类分别是Thread和Object,sleep方法属于Thread类中的静态方法,wait属于Object的成员方法。 2、 sleep() 是线程类(Thread)的方法,不涉及线程通信,调用时会暂停此线程指定的时间,但监控依然保持, 不会释放对象锁 ,到时间 自动恢复 ; wait() 是Object的方法,用于线程间的通信,调用时会 放弃对象锁 ,进入 等待 队列,待调用notify()/notifyAll()唤醒指定的线程或者所有线程,才进入对象锁定池准备获得对象锁进入运行状态。 3、wait,notify和notifyAll只能在 同步控制方法 或者 同步控制块 里面使用,而sleep可以在任何地方使用(使用范围)。 4、sleep()方法必须捕获异常InterruptedException,而wait()\notify()以及notifyAll()不需要捕获异常。 注意:   sleep方法只让出了CPU,而并不会释放同步资源锁。   线程执行sleep()方法后会转入阻塞状态。   sleep()方法指定的时间为线程不会运行的最短时间。因此,sleep()方法不能保证该线程睡眠到期后就开始执行。   notify的作用相当于叫醒睡着的人

应届生经典面试题:说说进程与线程的区别与联系

老子叫甜甜 提交于 2020-03-29 23:05:53
这问题,估计计算机专业的同学在找研发等工作的时候都会遇到过。前几天某老牌软件厂商的电话面试就提到了这一经典问题,今天招聘会上又有不少同学说在面试的时候被问到这点。在这里我就起个头,大家有啥想法意见等都欢迎回帖交流。 要了解二者的区别与联系,首先得对进程与线程有一个宏观上的了解。 进程,是并发执行的程序在执行过程中分配和管理资源的基本单位,是一个动态概念,竟争计算机系统资源的基本单位。每一个进程都有一个自己的地址空间,即进程空间或(虚空间)。进程空间的大小 只与处理机的位数有关,一个 16 位长处理机的进程空间大小为 216 ,而 32 位处理机的进程空间大小为 232 。进程至少有 5 种基本状态,它们是:初始态,执行态,等待状态,就绪状态,终止状态。 线程,在网络或多用户环境下,一个服务器通常需要接收大量且不确定数量用户的并发请求,为每一个请求都创建一个进程显然是行不通的,——无论是从系统资源开销方面或是响应用户请求的效率方面来看。因此,操作系统中线程的概念便被引进了。线程,是进程的一部分,一个没有线程的进程可以被看作是单线程的。线程有时又被称为轻权进程或轻量级进程,也是 CPU 调度的一个基本单位。 说到这里,我们对进程与线程都有了一个大体上的印象,现在开始说说二者大致的区别。 进程的执行过程是线状的 ,尽管中间会发生中断或暂停,但该进程所拥有的资源只为该线状执行过程服务

并发编程面试题

蹲街弑〆低调 提交于 2020-03-29 18:32:53
1.进程和线程还有协程之间的关系   进程:     进程简单理解就是我们平常使用的程序,如QQ,浏览器,网盘等。进程拥有自己独立的内存空间地址,     拥有一个以上的线程。   线程:     线程可以理解为轻量级的进程,是程序执行的最小单元。在某个进程启动后,会默认产生一个主线程,     主线程可以创建多个子线程,   协成:     协成,又称微线程,纤程。协成是一个线程执行,但执行有点像多线程,协成的执行效率极高     简单点说协成是进程和 线程的升级版,进程喝 线程都面临着内内核态和用户态的切换问题而     耗费许多切换时间 ,而且协成就是用户自己控制切换的时机,不再需要 陷入系统的内核态。   一个程序至少有一个进程 ,一个进项至少有一个线程。线程不能独立执行,必须依存在进程中。   协成,应用程序级别(而非操作系统)控制切换,以此来提升 效率。 2.并发和并行之间的区别   并行(parallel):         指在同一时刻,有多条指令在多个处理器上同时执行。所以无论从微观还是宏观 来看         二者都是一起执行的。            小结:     当系统有一个以上 CPU 时,则线程的操作有可能非并发。当一个 CPU 执行一个线程时,另一个      CPU 可以执行另一个线程,两个线程互不抢占 CPU 资源,可以同时进行

十三、多线程

情到浓时终转凉″ 提交于 2020-03-29 12:02:14
1、实现线程的两种方式 (1)继承Thread类   Thread构造方法:public Thread(String threadName)            public Tread()   重写run()方法,并调用start()执行线程。   语法:public static void main(String [] args){         new ThreadTest().start();       } (2)实现Runnable接口   语法:public class Thread extends Object implements Runnable   实现Runnable接口会创建Thread对象,并将Runnable对象和Thread对象相关联。   Thread构造方法:public Thread(Runnable r)            public Thread(Runnable r,String name)   步骤:1)建立Runnable对象      2)使用参数为Runnable对象的构造方法创建Thread实例。      3)调用start()方法启动线程。   创建Runnable对象:编写一个实现Runnable接口的类,然后实例化该类的对象。 2、线程的生命周期 (1)出生状态:调用start()前 (2)就绪状态

Java synchronized 关键字的实现原理

╄→尐↘猪︶ㄣ 提交于 2020-03-29 11:14:29
数据同步需要依赖锁,那锁的同步又依赖谁?synchronized给出的答案是在软件层面依赖JVM,而Lock给出的方案是在硬件层面依赖特殊的CPU指令,大家可能会进一步追问:JVM底层又是如何实现synchronized的? 本文所指说的JVM是指Hotspot的6u23版本,下面首先介绍synchronized的实现: synrhronized关键字简洁、清晰、语义明确,因此即使有了Lock接口,使用的还是非常广泛。其应用层的语义是可以把任何一个非null 对象 作为"锁",当synchronized作用在方法上时,锁住的便是对象实例(this);当作用在静态方法时锁住的便是对象对应的Class实例,因为 Class数据存在于永久带,因此静态方法锁相当于该类的一个全局锁;当synchronized作用于某一个对象实例时,锁住的便是对应的代码块。在 HotSpot JVM实现中,锁有个专门的名字:对象监视器。 1. 线程状态及状态转换 当多个线程同时请求某个对象监视器时,对象监视器会设置几种状态用来区分请求的线程: Contention List:所有请求锁的线程将被首先放置到该竞争队列 Entry List:Contention List中那些有资格成为候选人的线程被移到Entry List Wait Set:那些调用wait方法被阻塞的线程被放置到Wait Set OnDeck

线程_锁

你。 提交于 2020-03-29 08:57:05
(1)死锁 是指两个或两个以上的进程或线程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去。 此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程,如下就是死锁 #死锁 from threading import Thread,Lock,current_thread import time class MyThread(Thread): def run(self): self.f1() self.f2() def f1(self): mutexA.acquire() print("%s 抢到A锁" %current_thread().getName()) mutexB.acquire() print("%s 抢到B锁" %current_thread().getName()) print("%s 释放B锁" %current_thread().getName()) mutexB.release() print("%s 释放A锁" %current_thread().getName()) mutexA.release() def f2(self): mutexB.acquire() print("%s 抢到B锁" %current_thread().getName()) time.sleep(1) mutexA

多线程之旅(Thread)

倖福魔咒の 提交于 2020-03-29 07:53:30
在上篇文章中我们已经知道了多线程是什么了,那么它到底可以干嘛呢?这里特别声明一个前面的委托没看的同学可以到上上上篇博文查看,因为多线程要经常使用到 委托 。 源码 一、异步、同步 1.同步(在计算的理解总是要你措不及防,同步当线程做完一件事情之后,才会执行后续动作),同步方法慢,只有一个线程执行,异步方法快,因为多个线程一起干活,但是两者并不是线性增长,当我们的异步线程占有的资源越来越多了,会导致资源可能不够,其次线程过多CPU也是需要管理成本的,所以不是越多越好。 2.异步(可以同时执行多个任务,在同样的时间,执行不同的任务),同步方法卡界面(UI),因为我们的主线程(UI)忙于计算造成了堵塞了。异步方法不卡界面,计算任务交给了子线程完成。winform中体现的玲玲精致。(你品,你细品),web 可以异步的处理一起其他的任务,比如给用户发邮箱(我们的BS结构的,每次访问都是一个子线程,当我们的代码写的比较糟糕,是不是加载比较慢呢哈哈)。异步多线程无序,执行的先后无序,执行的时间不确定,结束也不确定,所以我们很难通过执行时间和先后顺序控制,异步的执行顺序。 二、初识Thread 属性名称 说明 CurrentContext 获取线程正在其中执行的当前上下文。 CurrentThread 获取当前正在运行的线程。 ExecutionContext 获取一个

C# 实现生产者消费者队列

杀马特。学长 韩版系。学妹 提交于 2020-03-28 22:59:50
开发过程中经常会碰到这样的场景:需要从一个地方获取一些数据,然后处理数据并将其保存在数据库中。 private void FetchData() {} private void SaveData() {} static void Main(string[] args) { for (int i = 0; i < 10; i++) { FetchData(); // 获取数据 SaveData(); // 处理并保存 } } 例如上述代码例子这样顺序执行,执行会很慢,原因是获取数据和处理并保存的过程都可能导致阻塞,然而FetchData()每次取数据并不需要等待上一条数据保存完成再进行。 这样的场景非常适合用 生产者消费者队列 :生产者就是FetchData(),用来生产数据;消费者SaveData(),用来消费数据。 举个实际例子,我们需要通过一个Web Api获取一些城市的天气情况,并将其保存到数据库中。 实现方式: 需要一个任务队列,生产者可以向队列中插入任务,消费者可以从任务队列中取出任务来执行。 为保证线程安全,使用一个锁来保护这个队列的访问。 制定一个退出策略,在所有任务完成时释放资源。 下边是实现的完整代码: class Program { // 任务队列 static Queue<string> _tasks = new Queue<string>(); //

第十三周学习报告

拟墨画扇 提交于 2020-03-28 15:26:37
第十三周(11.30-12.06): 一、学习目标 1. 掌握三种并发的方式:进程、线程、I/O多路复用 2. 掌握线程控制及相关系统调用 3. 掌握线程同步互斥及相关系统调用 二、学习资源 1. 教材:第十一章《网络编程》简单过一下 2. 教材:第十二章《并发编程》 并发编程 如果逻辑控制流在时间上重叠,那么他们就是并发的,这种常见的现象叫做并发。 并发不限制于内核: 访问慢速I/O 设备 与人交互 通过推迟工作以降低延迟 服务多个网络客户端 在多核机器上进行并发计算 进程 I/O多路复用 线程 12.1基于进程的并发编程 第一步:服务器接受客户端的连接请求 第二步:服务器派生一个子进程为这个客服端服务 第三步:服务器接受另一个连接请求 第四步:服务器派生另一个子进程为新的客户端服务 12.1.2关于进程的优劣 模型:共享文件表,但是不共享用户地址空间 独立的地址空间使得进程共享状态信息变得更加困难 基于I/O多路复用的并发编程 对于两个事件: 网络客户端发起的连接请求 用户在键盘上输入命令行 针对不知道等待哪个事件,一个解决办法就是I/O多路复用技术。 基本思路就是使用select函数,要求内核挂起进程,只有在一个或者多个I/O事件发生后,才将控制返回给应用程序,就像P651事例一样: 当集合{0,4}中任意描述符准备好读时返回。 当集合{1,2,7}中任意描述符准备好写时返回