高并发基础

多线程

什么是线程?
线程是指程序在执行过程中,能够执行程序代码的一个执行单元,在Java语言中,线程有四种状态:运行,就绪,挂起,结束。
线程与进程的区别?
进程是一段正在运行的程序,而线程有时也被称为轻量级进程,它是进程的执行单元,一个进程可以拥有多个线程,各个线程之间共享程序的内存空间,但是各个线程拥有自己的栈空间。
为什么使用多线程?
(1)使用多线程可以减少程序的响应时间。单线程如果遇到等待或阻塞,将会导致程序不响应鼠标键盘等操作,使用多线程可以解决此问题,增强程序的交互性。
(2)与进程相比,线程的创建和切换开销更小,因为线程共享代码段、数据段等内存空间。
(3)多核CPU,多核计算机本身就具有执行多线程的能力,如果使用单个线程,将无法重复利用计算资源,造成资源的巨大浪费。
(4)多线程可以简化程序的结构,使程序便于维护,一个非常复杂的进程可以分为多个线程执行
线程调度?
JAVA虚拟机的一项任务就是负责线程的调度,线程调度是指按照特定机制为多个线程分配CPU的使用权。

**synchronized(同步锁)
	1.锁住临界对象(当对访问当前对象限定比较低时使用)
	2.锁当前对象(当对访问当前对象锁级别高时使用)
	3.锁当前类对象(静态方法或代码块锁住的类对象)
	**同步方法太重,不建议锁;建议使用同步代码块,可以避免锁的范围太高**
	**阻塞越少就越轻**
	**缩小锁的范围能提高性能**
	加锁的目的:保证操作的原子性
	同步方法只影响其他线程调用同一个同步方法时的锁问题,不影响其他线程调用非同步方法,或调用其他锁资源的同步方法
	锁可重入:同一个线程,多次调用同步代码块,锁定同一个锁对象,可重入
	子类同步方法覆盖父类同步方法,可以指定调用父类同步方法,相当于锁重入
	锁与异常:当同步方法中发生异常时,自动释放锁,不会影响其他线程的执行
	同步代码一旦加锁后,会有一个临时的锁引用执行锁对象,和真实引用无直接关联,在锁未释放之前,修改锁对象引用不会影响同步代码的执行
	在定义同步代码块时,不要使用常量作为锁对象
**volatile(内存可见性)
	这个关键字就是告诉os底层,在访问变量的时候要去内存中查看最新值,不要看cpu中缓存的值(告诉cpu刷新缓存空间)
	这个关键字就是告诉os底层,在cpu计算过程中,都要检查内存中数据的有效性,保证最新的内存数据被使用
	只能保证可见性,不能保证原子性
	AtomicXxx:原子操作类型。其中每个方法都是原子操作,可以保证线程安全
**CountDownLatch(门闩)
	可以和锁混用,或代替锁功能,在门闩未完全开放之前等待,完全开放后执行,避免锁效率低下的问题
**reentrantLock
	重入锁:
		lock():加锁(相当于synchronized),建议使用,比synchronized效率高,量级较轻
		unlock():解锁
	尝试锁:
		trylock():尝试锁,如果有锁,无法获取锁标记,返回false。获取返回true
		unlock():解锁,在解除锁时一定要判断是否获取了锁标记,如果当前线程没有获取到锁标记,会抛出异常
		trylock(5,TimeUnit.SECONDS):阻塞尝试锁,如果超过5秒没有获取到锁标记,就不等待,直接返回false
	可打断锁:
		lockInterruptibly():可尝试打断锁,阻塞等待锁(类似于sleep),可被打断,lock的不能被打断
		interrupt():打断线程休眠,只要被打断的线程,一定会抛异常(只能打断阻塞的线程,不能打断执行的线程)
	公平锁:		
		ReentrantLock lock = new ReentrantLock(true) 这个true就是定义公平锁
		记录等待时常,不建议使用
阻塞状态包括:普通阻塞,等待队列,锁池队列
		普通阻塞:sleep(),可以被打断,调用thread.interrupt(),可以打断阻塞状态,抛出异常
		等待队列:wait()被调用,也是一种阻塞,可以被notify()唤醒,无法打断
		锁池队列:无法获取锁标记,不是所有的锁池队列都能被打断
			使用reentrantLock的lock(),获取锁标记的时候,无法被打断
			使用reentrantLock的lockInterruptibly(),获取锁标记的时候,可以被打断	
锁的种类(JVM控制):
	偏向锁,自旋锁,轻量级锁,重量级锁
锁的使用方式:
	先提供偏向锁,如果不满足,升级为轻量级锁,再不满足,升级为重量级锁。
	自旋锁是一个过渡的锁状态,不是一种实际的锁类型
	锁只能升级,不能降级
	重量级锁:
		三个线程以上,就一定使用重量级锁
	偏向锁(偏向于某个线程):
		是一种编译解释锁。如果代码中不可能出现多线程并发争抢同一个锁的时候,JVM编译代码解释执行的时候,会自动放弃同步信息。
		消除synchronized的同步代码结构,使用锁标记的形式记录锁状态,在Monitor中有变量ACC_SYCHRONIZED,当变量值使用的时候,代表偏向锁锁定
		可以避免锁的争抢和锁池状态的维护,提供效率。
	轻量级锁:
		过渡锁。当偏向锁不能满足的时候,也就是有多线程并发访问的,锁定同一个对象的时候,先提升为轻量级锁。
		也是使用标记ACC_SYCHRONIZED记录,Monitor也会记录未获取锁的线程的位置,线程的信息在栈帧上。
		就是只要两个线程争抢锁标记的时候,优先使用轻量级锁。
		两个线程也可能出现重量级锁
	自旋锁:
		一个过渡锁。是偏向锁和轻量级锁的过渡。
		当获得锁的过程中未获取到,为了提高效率,jvm自动执行若干次空循环,减少状态变更(提高效率),再次申请锁,而不是进入阻塞状态的情况。

同步容器

解决并发情况下的容器线程安全问题,给多线程环境准备一个线程安全的容器对象
线程安全的容器对象:vector、hashtable。线程安全的容器对象是用synchronized方法实现的。
concurrent包中的同步容器,大多数使用系统底层技术实现的线程安全
**Map/Set:
	ConcurrentHashMap/ConcurrentHashSet
		底层hash实现的同步Map/Set。效率高,线程安全。
		使用底层技术实现线程安全,量级较sychronied低。
	ConcurrentSkipListMap/ConcurrentSkipListSet
		底层跳表实现的Map/Set,有序,效率比ConcurrentHashMap稍低
**List:
	CopyOnWriteList
		写时复制集合。写时效率低,读时效率高,每次写入数据,都会创建一个新的底层数组,读的时候返回最新的数组,所以读的效率高。
		用在写少读多的场景
**Queue:
	ConcurrrentLinkedQueue:
		底层由链表实现,队列FIFO
		基础链表同步队列
		offer():增加数据,向后增加
		size():长度
		peek():查看queue中的首数据
		poll():获取queue中的首数据,是取出。长度会-1
	LinkedBlockingQueue:
		阻塞容器
		put()和take()自动阻塞
		put()自动阻塞,队列容量满后,自动阻塞
		take()自动阻塞,队列容量为0后,自动阻塞
	ArrayBlockingQueue:
		有界队列容器,底层数组实现的有界队列。
		自动阻塞
		根据调用api(add/put/offer)不同,有不同的特性
		容量不足的时候,有阻塞能力
		add():在容量不足的时候,抛出异常
		put():在容量不足的时候,自动阻塞
		offer():
			单参数(offer(value)):容量不足的时候,自动放弃后面的数据,返回false。默认无阻塞
			多参数(offer(value,times,timeunit)):容量不足的时候,阻塞times时长(单位为timeunit)
						如果在阻塞时长内,有容量空闲,新增数据返回true;如果无容量空闲,放弃新增数据,返回false
	DelayQueue:
		只能存Delayed类型的对象(实现Delayed接口的对象)
		无界容器
		用在定时任务,计划任务上
	LinkedTransferQueue:
		转移队列。无容量。在transfer的时候,队列直接阻塞,然后将数据交给来取数据的。可缓存数据
		转移队列,使用transfer(),实现消息的即时处理,没有消费者,就阻塞
		add():队列会保存数据,不做阻塞等待
		transfer():transferQueue的特有方法,必须有消费者(take()的调用者)
							如果没有任意线程消费数据,transfer阻塞
							一般用于处理即时消息
	SynchronousQueue:
		有界容器
		阻塞队列
		同步队列,是一个容量为0的队列。是一个特殊的transferQueue,不能缓存数据,永远为0
		必须有消费者等待,才能使用的队列
		add():无阻塞,若没有消费线程等待,则抛出异常
		put():有阻塞,若没有消费线程阻塞等待,则阻塞

线程池

Executor:
	线程池底层处理机制
	在使用线程池的时候,底层如何调用线程中的逻辑
	线程池顶级接口
	常用方法:void execute(Runnable);用于处理任务的一个服务方法,调用者提供Runnable接口的实现,线程池通过线程执行这个Runnable;
	作用就是启动线程任务
ExecutorService:
	线程池服务类型,所有的线程池类型都实现了这个接口。实现这个接口,代表可以实现线程池能力。类似于Executor的集合
	Executor的子接口
	常见方法:
		void execute(Runnable):父类方法
		Future submit(Callable):提供线程执行后的返回值
		Future submit(Runnable):不提供线程执行后的返回值	
		shutdown():优雅关闭。不是强行关闭线程池,回收线程池的资源。而是不在处理新的任务,将已接收的任务处理完毕后,在关闭。
	线程池状态:
		running:线程池正在执行中
		shutingdown:线程池正在关闭过程中,优雅关闭
		terminated:线程已经关闭
Callable:
	可执行接口。
	类似Runnable接口,也是启动线程的一个接口。
	其中定义的方法为call(),call()的作用和Runnable接口中的run()方法完全一致,但是call()有返回值。
Future:
	代表未来结果,也就是线程执行结束后的一种结果
	常用方法:
		T get():获取返回结果,阻塞等待线程执行结束并得到结果
		T get(long,timeunit):获取返回结果,阻塞固定时长等待线程执行后的结果,如果在阻塞时长内,线程未结束,抛出异常;
Executors:
	Executor的工具类。类似于Collection(集合)的Collections类(集合工具类)
	可以更简单的创建若干种线程池
	线程池是一个进程级的重量级资源。默认生命周期和jvm一致;如果手工调用shutdown(),那么线程池执行所有的任务后,线程池自动关闭
FixedThreadPool:
	定长容量线程池(固定容量),在创建线程池的时候容量固定
	ExecutorService service = Executors.newFixedThreadPool(5); //参数是最大容量
	都有一个任务队列,使用的是BlockingQueue<Runnable>作为任务的载体
	当任务数量大于线程池容量的时候,没有运行的任务保存在任务队列中,当线程有空闲的时候,自动从队列中取出任务执行
	Queued tasks:任务队列
	completed tasks:结束任务队列,(没有队列,只是计数)
	使用场景:
		大多数情况下使用的线程池,首选FixedThreadPool。因为OS系统和硬件是有线程支持上限的。不能随意无限制提供线程池
	线程池默认容量上限:Integer.MAX_VALUE
	常见线程池容量:PC-200,服务器-1000~10000
	并发处理能力大概:线程数*(10~18)
CachedThreadPool:
	缓存线程池,自动扩容
	容量管理策略:
		如果线程池中的线程数量不满足任务执行,创建新的线程
		如果线程池中的线程空闲时长达到一定的临界值(默认60秒),自动释放线程(自动销毁)
	无容量限制的线程池,最大容量为Integer.MAX_VALUE
	ExecutorService service = Executors.newCachedThreadPool();
	应用场景:内部应用或测试应用
ScheduledThreadPool:
	计划任务线程池
	底层就是delayedQueue
	ExecutorService service = Executors.newScheduledThreadPool(3); //容量为3
	方法:
		scheduledAtFixedRate(Runnable,start_limit,limit,timeunit)  阻塞式的,效率比较低下
			Runnable:要执行的任务
			start_limit:第一次任务执行的间隔
			limit:多次任务执行的间隔
			timeunit:时间单位
SingleThreadPool:
	ExecutorService service = Executors.newSingleThreadExecutor();
	线程容量永远为1,单一容量线程池
	使用场景:保证任务顺序时使用
ForkJoinPoll:
	分支合并线程池,适用于处理复杂任务
	初始化线程容量与cpu核心数相关
	递归完成复杂任务,主要用于科学计算或数据运算
	没有所谓的容量,默认都是一个线程。根据任务自动分支新的线程
	ForkJoinPoll pool = new ForkJoinPoll(); 
	ForkJoinTask(抽象类):分支合并任务,ForkJoinPoll里面能执行的任务必须是ForkJoinTask,里面就是任务处理方式
	线程池中运行的任务类型必须是ForkJoinTask的子类型(RecursiveTask,RecursiveAction),其中提供了分支和合并的能力
	RecursiveTask:有返回结果的分支合并任务(callable)
	RecursiveAction:无返回结果的分支合并任务(runnable)
	compute():任务的执行逻辑
ThreadPoolExecutor:
	线程池的底层实现
	模拟FixedThreadPool,核心线程5,最大容量5,线程生命周期无限
	ExecutorService service=new ThreadPoolExecutor(5,5,0L,TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>());
	使用场景:默认提供的线程池不符合条件时使用
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值