
获得徽章 5
- 生产者消费者模型
多线程交互的经典案例
成员:
生产单位
临界资源
缓冲区
生产者
消费者
流程:
生产者每隔一段时间向缓冲区写入数据 如果缓冲区数据满则挂起线程 等待消费
成功写入数据 唤醒所有正在等待的线程
消费者每隔一段时间向缓存区读出数据 如果缓冲区没有数据则挂起线程 等待生产
成功读出数据 唤醒所有正在等待的线程
一般都是同时开启多个生产者和消费者线程同时进行
消费者过多或生产者过多都会出现部分线程的饥饿的现象展开评论点赞 - 单例模式为什么同时用volatile和synchronized 加锁?为什么两次判空?
1.volatile在synchronized保证同步的前提下 避免了指令重排造成的问题
2.第一次判空是因为需要对象为空 才进行初始化的判断
第二次判空是因为synchronized锁住了类对象 如果在第一次判空后多线程操作将其初始化 那么未初始化的线程进入后会再创建一个对象造成重复展开评论点赞 - 网络专题
TCP的状态
1.CLOSED 关闭的 未打开的
2.LISTEN 表示服务器上的某个SOCKET处于监听状态 可以接受连接
4.SYN_SENT 表示客户端发送请求报文SYNC 进入此状态并等待服务器端发送ACK
3.SYN_RCVD 表示服务器接收到请求连接的SYNC报文
5.ESTABLISHED 经过三次握手后 两端建立连接
6.FINAL_WAIT_1 表示socket处于连接状态 但有一端主动提出断开连接 并发送FIN报文 随后进入该状态 当对方返回ACK后 则进入FIN_WAIT_2状态
7.FIN_WAIT_2 等待四次挥手 执行完成 断开连接
8.CLOSING 双方同时请求关闭连接
9.CLOSE_WAIT 服务器正等待关闭 当客户端发送FINAL 报文服务端马上返回一个ACK 然后判断自身是否还有数据要发送给对方 如果没有则发送FIN报文给客户端表示自身也要关闭
如字面上的关闭等待
10.LAST_ACK 服务器发送FIN 报文后的等待状态 等待最后ACK回应 断开连接进入CLOSED状态
11.TIME_WAIT 客户端接受服务端的FIN报文并发送ACK 随后等待固定时间 变为CLOSED状态
三次握手、四次挥手。为啥是三次不是两次 为啥是四次而不是三次?
三次握手是因为建立连接双方都必须请求 建立一个全双工的通道 传输数据
所以服务器发送ACK确认和SYNC请求报文 可以合在一起发送 避免多次发送 耗时
而四次挥手同理 需要双方都确认自己不再传输对方数据 则可断开 有可能出现 服务器在回复客户端的FINAL报文发送ACK后 数据还没传输完
还继续向客户端发送数据 不能同时断开 需要分别走完整流程 无法简化 所以为四次挥手
TCP/IP模型分层
精简的分为4层
应用层 对应HTTP FTP(文件的操作) DNS(地址的解析)
传输层 对应TCP(安全可靠的连接) UDP(不可靠的)
网络层 对应IP ICMP(消息控制报文)
数据链路层 对应
TCP 建立连接后,发包频率是怎样的?
TCP会利用窗口进行拥塞控制 窗口处于发送缓冲区中 只有在窗口的数据才能被发送
窗口可以增大 或者进行移动 来控制发包频率展开评论点赞 - 多线程编程补充3
CAS机制 CompareAndSwap 有3个值 原值 期望值 实际值 具体判断条件是实际值是否等于期望值
可以在线程中执行此操作 如果成功表示 线程空闲 如果失败表示线程被占用
ABA问题
线程12同时从内存中取出数据 A 但 B线程 将自身数据改为B 但把原内存地址数据改为A 这就导致 线程1认为内存地址的数据是没有发生变化的 导致CAS继续运行
在栈的场景下 例如AB 希望通过CAS 将栈顶改为B 但另外线程将AB出栈push DCA 经过CAS后 栈顶为B 但B是游离对象 next为空
解决办法 在CAS时添加引用检查 和标志检查 用来检查是否发生变化
ReentrantLock 默认为非公平锁
锁拥有synchronized相同的并发性和内存语义 也提供了其他功能 中断锁等候、条件变量等
实现基于AbstractQueuedSynchronizer(AQS)和LockSupport
AQS主要利用硬件原语指令CAS 来实现轻量级多线程同步机制 并且不会引起CPU上文切换和调度,同时提供内存可见性和原子化更新保证
AQS的本质上是一个同步器/阻塞锁的基础框架,其作用主要是提供加锁、释放锁,并在内部维护一个FIFO等待队列(使用链表作为队列volatile修饰变量),用于存储由于锁竞争而阻塞的线程。
ReentrantLock提供了公平锁实现和非公平锁实现
lock
通过CAS判断操作是否成功 则持有当前线程 否则尝试获取
acquire
首先尝试获取锁进入对应的TryAcquire函数 如果获取失败 将当前线程封装为Node 尾插入等待队列单链表中
判断当前节点的线程是否应该等待(队列前面的线程是否获取锁) 需要挂起当前节点LockSupport 然后中断该线程
nonfairTryAcquire函数
1.先通过CAS判断线程是否空闲 空闲则占用此线程 否则尝试获取
2.获取锁 1)先判断锁状态是否空闲 空闲但CAS操作失败则获取锁失败 成功则占用 2))否则,检查当前线程与锁拥有者线程是否相等 如果相等表示重入该锁 维护锁的状态
3)如果不是当前线程 说明锁被其他线程占用
非公平锁在实现上 线程在对列头部 或者 线程队列为空 而锁状态为0 那么可以获取锁展开评论点赞 - 多线程编程补充2
锁
这种锁需要自己定义加锁和解锁的时机 操作不当可能造成死锁
但使用锁接口 可以定义锁的实现 粒度更小 更灵活 但没有synchronized使用方便
锁有很多分类
1.非公平锁/公平锁
公平锁 加锁前先查看是否有排队等待的线程,有的话优先处理排在前面的线程,先来先得
非公平锁:线程加锁时直接尝试获取锁,获取不到就自动到队尾等待。
更多的是直接使用非公平锁:非公平锁比公平锁性能高5-10倍,因为公平锁需要在多核情况下维护一个队列,如果当前线程不是队列的第一个无法获取锁,增加了线程切换次数。
2.可重入锁 (表示一个线程已经获得该锁,再次要求该锁,这种情况叫可重入锁)
同一个线程每进入一次,锁的计数器都自增1,所以要等到锁的计数器下降为0时才能释放锁==
3.乐观锁/悲观锁
每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据
使用于多线程读取场景 增加并发量
悲观锁:总是假设最坏的情况,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会阻塞直到它拿到锁
适合写操作场景 保证同步效率
4.读写锁
可以被多线程读 单线程写
5.分段锁
ConcurrentHashMap采用此种设计 将整个hashmap分为多个segment 一个segment维持一个锁 提供相同的线程安全 默认为16 运行16个线程同时操作 提升效率
6.自旋锁
获取锁的线程 不会阻塞 而是采用循环的方式 减少线程间的切换消耗 但会消耗CPU展开评论点赞 - 多线程补充1
内存分为主存和工作内存
工作内存包括线程私有栈和 寄存器(对主存变量拷贝)
每个线程都有自己的工作内存 保存的是对主存内容的拷贝
voliate保证内存的可见性 线程修改其他线程必须知道
所以voliate修饰的变量修改之后会被刷新到主存 其他线程使用前必须从主存中刷新
又能禁止指令重排序 指令执行时的相对顺序不会变
其不能保证原子性
synchronized 依赖于JVM 保证了同一时刻只有一个线程在作用范围内操作 保证了可见,有序和原子 三种同步特性
作用范围取决于修饰什么
synchronized
修饰代码块 被称作同步语句块 同步范围为代码块内 作用的对象为调用者 代码块为临界区 互斥区
修饰方法 称作同步方法 同步范围为方法内 作用的对象为调用者
修饰静态方法 作用的范围是整个静态方法 作用的对象是这个类的所有对象
修饰类 其作用的范围是synchronized后面括号括起来的部分,作用的对象是这个类的所有对象。
synchronized 修饰实例方法和修饰静态方法有啥不一样?
修饰实例方法作用为这个实例调用这个方法 是同步的
修饰静态方法 作用的是这个类所有实例调用这个方法是同步的展开评论点赞 - 多线程补充1
内存分为主存和工作内存
工作内存包括线程私有栈和 寄存器(对主存变量拷贝)
每个线程都有自己的工作内存 保存的是对主存内容的拷贝
voliate保证内存的可见性 线程修改其他线程必须知道
所以voliate修饰的变量修改之后会被刷新到主存 其他线程使用前必须从主存中刷新
又能禁止指令重排序 指令执行时的相对顺序不会变
其不能保证原子性
synchronized 依赖于JVM 保证了同一时刻只有一个线程在作用范围内操作 保证了可见,有序和原子 三种同步特性
作用范围取决于修饰什么
synchronized
修饰代码块 被称作同步语句块 同步范围为代码块内 作用的对象为调用者 代码块为临界区 互斥区
修饰方法 称作同步方法 同步范围为方法内 作用的对象为调用者
修饰静态方法 作用的范围是整个静态方法 作用的对象是这个类的所有对象
修饰类 其作用的范围是synchronized后面括号括起来的部分,作用的对象是这个类的所有对象。
synchronized 修饰实例方法和修饰静态方法有啥不一样?
修饰实例方法作用为这个实例调用这个方法 是同步的
修饰静态方法 作用的是这个类所有实例调用这个方法是同步的
锁
这种锁需要自己定义加锁和解锁的时机 操作不当可能造成死锁
但使用锁接口 可以定义锁的实现 粒度更小 更灵活 但没有synchronized使用方便展开评论点赞