多线程视频学习笔记

脏读:

在我们对一个对象方法加锁的时候,需要考虑业务的整体性 既为setValue get value 方法同时加锁synchronized 同步关键字 保证业务的院子性 不然会出现业务错误。

synchronized 也可以进行重入锁

synchronized修饰的方法可以调用synchronized修饰的方法

synchronized修饰的子类方法可以调用父类synchronized修饰的方法 并且是线程安全的

对象里面的属性发生变化时 不影响锁

volatile概念

volatile关键字主要作用使变量在多个线程间可见。

在java中,每一个线程都会有一块工作内存区,其中存放着所有线程共享住内存中的变量值的拷贝。当线程执行时,他在自己的工作内存中操作这些变量,为了存取一个共享变量,一个线程通常先获取锁定并清除它的内存工作区,把这些共享变量从所有线程的共享内存区中正确的装入他自己所在的工作区中,当线程解锁时 保证该工作区内存区中变量的值写回到共享内存中。

一个线程可以执行的操作有 使用(use) 赋值(assign) 装载(load) 存储(store) 锁定(lock) 解锁

而主内存可以执行的操作有 读(read) 写(write) 锁定(lock) 解锁(unlock) 每个操作都是原子性的。

volatile的作用就是强制线程到住内存(共享内存)里去读变量,而不去现场工作内存区里去读取 从而实现了多个线程间变量可见。也就是满足现场安全的可见性

 

线程之间通信

线程是操作系统中独立的个体,单这些个体如果不经过特殊处理就不能成为一个整体,线程通信就是成为整体的必用方式之一。

使用wait/notify方法实现线程间的通讯这两个方法都是object的类的方法

1.wait和notify必须配合synchronized关键字使用

2.wait释放锁 notify方法不释放锁

想释放锁可以使用CountDownLatch的countDown();await()方法 一个同步辅助类,在完成一组正在其他线程中执行的操作之前,它允许一个或多个线程一直等待。

构造方法参数指定了计数的次数

countDown方法,当前线程调用此方法,则计数减一

await方法,调用此方法会一直阻塞当前线程,直到计时器的值为0

ThreadLocal 线程局部变量

是一种多线程间并发访问变量的解决方案 使变量在多线程中完全独立

 

单例模式

饥饿模式在类加载时实例出来 懒汉模式 在使用时加载

出来

多线程中

静态内部类单利 static inner class

 

同步类容器

同步类容器都是线程安全的 内部实现都是在方法上加synchronized关键字

 

并发类容器

ConcurrentMap实现原理

ConcurrentHashMap 理解 hashMap

ConcurrentSkipListMap 理解treeMap

内部使用段(Segment) 来表示这些不同部分每个段就是一个小的hashTable 它们有自己的锁

只要多个修改操作发生在不同的端上 它们就可以并发进行 。多线程场景时减小锁的粒度从而降低锁竞争的一种方案 并且内部大量使用volatile关键字命名 目的是第一时间获取修改的内容 性能很高

CopyOnWriteArrayList实现原理

写时赋值的容器

往容器添加元素时 不直接在当前容器添加 而是先将当前容器copy 复制出一个新的容器然后新的容器里添加容器 添加完容器后 再将原容器的引用指向新的容器 这样做的好处是我们可以对容器并发的读 而不需要加锁 因为当前容器不会添加任何元素 所以 copyOnWrite容器也是一种读写分离的思想 读和写在不同的容器。

Queue队列

MQ 写慢 读快

tcp直连通信 写读速度快

选择技术的时候 应该正确的选择一个技术 然后开发人员去开发

架构师作用 敏捷开发 带领团队选择正确方向开发 水平扩展性要强

哪种场景下用什么技术 都是要架构师来思考的

 

高性能无阻塞无界队列

ConcureentLinekedQueue

高并发场景下的队列 通过无锁的方式实现并发状态下的高性能队列

add增加一个元素

remove 移除一个元素

element 返回队列头部的元素

offer 添加一个元素并返回true

poll 移处并返回队列头部元素

peek 返回队列头部元素

put 添加一个元素

take 移除并返回队列头部元素

ArrayBlockingQueue 在构造时需要指定容量, 并可以选择是否需要公平性,如果公平参数被设置true,等待时间最长的线程会优先得到处理。通常,公平性会使你在性能上付出代价,只有在的确非常需要的时候再使用它。它是基于数组的阻塞循环队 列,此队列按 FIFO(先进先出)原则对元素进行排序。

PriorityBlockingQueue 是一个带优先级的 队列,而不是先进先出队列。元素按优先级顺序被移除,该队列也没有上限(看了一下源码,PriorityBlockingQueue是对 PriorityQueue的再次包装,是基于堆数据结构的,而PriorityQueue是没有容量限制的,与ArrayList一样,所以在优先阻塞 队列上put时是不会受阻的。虽然此队列逻辑上是无界的,但是由于资源被耗尽,所以试图执行添加操作可能会导致 OutOfMemoryError),但是如果队列为空,那么取元素的操作take就会阻塞,所以它的检索操作take是受阻的。另外,往入该队列中的元 素要具有比较能力。

DelayQueue 是一个存放Delayed 元素的无界阻塞队列,只有在延迟期满时才能从中提取元素。该队列的头部是延迟期满后保存时间最长的 Delayed 元素。如果延迟都还没有期满,则队列没有头部,并且poll将返回null。当一个元素的 getDelay(TimeUnit.NANOSECONDS) 方法返回一个小于或等于零的值时,则出现期满,poll就以移除这个元素了。此队列不允许使用 null 元素。 下

Feture设计模式

接收请求 返回假数据 异步线程执行返回真实数据

Master-Worker设计模式

Master接收分配任务

worker处理子任务

处理完成后返回给master master总结归纳 然后返回客户端

容器归纳job

使用ConcurrentLinkenQueue承装所有的任务

work 实现Runnable

使用HashMap<String,Thread>去装work对象

 

使用concurrentHashMap<String,Object>承装每一个worker并发处理任务的结果集

 

JDK多任务执行框架

 

 

Executor框架线程工厂 通过它可以创建特定功能的线程池

newFixedThreadPoll() 返回一个固定数量的线程池该方法的线程数始终不变 当有一个任务提交时 若线程池中空闲则立即执行 若没有 则会被暂缓在一个任务列队中等待有空闲的线程去执行

实现: Executors.newFixedThreadPoll(10)

new ThreadPoolExecutor(10,10,0,单位,LinkedBlockingQueue<Runnable>);

传入几 线程池的数量就是几 线程永远存在

无界队列 线程池满了 存入linkedBlockingQueue

newSingleThreadExecutor()方法 创建一个线程的线程池 若空闲则执行 否则等待执行

内部写死1个线程

new ThreadPoolExecutor(1,1,0,单位,LinkedBlockingQueue<Runnable>);

newCacheThreadPoll() 返回一个可根据实际情况调整线程个数的线程池 不限制最大线程数量 若用空闲的

线程执行任务 若无任务则不创建线程 并且每一个空闲线程会在60秒后自动回收

核心线程0 不限制数量

只要任务来了 就创建线程

new ThreadPoolExecutor(0,Integer.Max_VALUE,60L,TimeUnit.SECONDS, SynchronousQueue<Runnable>);

使用SynchronousQueue 没有任何容量的 来了一个任务 不经过queue 直接创建一个线程去执行

60秒 如果60秒内没有线程执行则回收 如果有 则用此线程执行

 

newScheduleThreadPoll() 返回一个SchededExecutorService对象 但该线程可以知道线程的数量

与newFixedThreadPoll()类似 里面的每个线程都有定时器的功能

传入线程池数量

new ScheduledThreadPoolExecutor(int corePoolSize);

super(corePoolSize,Integer.MAX_VALUE,0,时间单位, newDelayedWorkQueue());

DekayedWorkQueue 带有延迟时间的Queue 元素到时间了 则自动把元素移除

 

 

 

ThreadPoolExecutor() 传入的参数不同 实现的功能不同 可以自定义线程池

参数

int corePoolSize 核心线程数

int maximumPoolSize 最大线程数

long keepAliveTime 保持存活的空闲时间

TimeUnit 存活时间单位

BlockingQueue 任务队列 当线程池满了 如果还有线程进来 则进入任务队列

ThreadFactory 线程工厂,线程生成器;所有线程通过这个工厂创建(通过 addWorker方法)

任何调用都要做好可能由系统或者用户设置的线程数限制策略导致的创建失败;

虽然失败不能被当做错误处理,但是创建线程失败可能导致新任务被拒绝,已经存在任务继续阻塞在队列里等待执行;

RejectedExecutionHandler 拒绝策略 当线程满了 怎么处理再来的线程

任务拒绝策略;负责处理当线程饱和后、线程池正在关闭时的新提交任务;

ThreadPoolExecutor内部有实现4个拒绝策略:

(1)、CallerRunsPolicy,由调用execute方法提交任务的线程来执行这个任务;

(2)、AbortPolicy,抛出异常RejectedExecutionException拒绝提交任务;

(3)、DiscardPolicy,直接抛弃任务,不做任何处理;

(4)、DiscardOldestPolicy,去除任务队列中的第一个任务,重新提交;

(5)、RejectedExecutoionHandler 自定义拒绝策略

 

 

Concurrent.util工具类详细讲解和使用

 

CyclicBarrier:

N个线程相互等待,任何一个线程完成之前,所有的线程都必须等待。

CountDownLacth:

一个线程(或者多个), 等待另外N个线程完成某个事情之后才能执行。

 

区别: CountDownLacth 一个线程等待 其他N个线程通知 完成后 一个线程执行

CyclicBarrier 多个线程都参与阻塞 当完成某个条件 再一起执行多个线程

 

Callable和 Future使用

Callable 异步执行任务 可以有返回值

Future可以拿到异步执行任务的返回值

 

ExecutorService executor里submit和future的区别 submit可以传入实现Callable接口的实例对象 submit方法有返回值

Future = executor.submit(futureTask)

f.get 任务处理完成 返回null 判断当前任务执行完毕

futureTask.get 异步获取执行结果

 

Semaphore信号量

限制线程访问 标识 只能多少线程进行使用 线程必须拿到许可才能访问此代码 如果线程量超过许可个数 则阻塞

semaphore.acquire 获取许可

 

锁Lock

常用的 重入锁 读写锁

 

重入锁:ReentrantLock 在需要进行同步的代码部分加上锁定 但不要忘记最后一定要释放锁定 不然会造成锁无法释放

使用lock的时候 使用Condition 去做通知和等待

condition 针对某一把锁 进行释放 等待

condition.await() 等待通知

condition.signal()信号 唤醒

多condition

一个锁可以创建多个condition

每个condition 只能等待唤醒 自己的锁

 

公平锁 非公平锁

ReentrantLock(true)公平

ReentrantLock(false)非公平

公平锁 按照顺序对线程上锁 浪费性能 需要维护顺序

非公平 cpu自动分配对线程上锁

 

tryLock()尝试获得锁 结果返回true false

转载于:https://my.oschina.net/u/3446994/blog/1557050

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值