java多线程之并发编程

时间:2022-09-01 18:04:29
1.并发不一定比串行更快 因为并发有线程创建和上下文切换的开销
2.java的并发采用内存共享模型
3.单线程中重排序不会影响到结果 但多线程中重排序可能会影响到结果
4.votaile变量 当线程A修改votatile变量会更加主内存发送信息给B线程 修改的值刷新到主内存 被通知的线程将本地内存的votaile值设为无效 读取主内存更新的值 但只能保证其线程的可见性 不能保证原子性
5.votaile相比锁是轻量级的同步 简易性和可收缩性 不会造成线程阻塞 也就不会造成死锁问题 性能更高 votaile读操作和非votaile读操作性能一样 但写操作比非votaile写操作性能更低 如果线程变量读操作远多于写操作 voatile提高比锁提高更低的效率 适用 状态标记 boolean 一次性发布 在整个项目运行中不会存在这个变量的非原子性操作
6.CAS和votaile是ReentantLock和concurrent包的基石
7.乐观锁和悲观锁 独占锁就是悲观锁 sychronized就是独占锁 让需要锁的线程挂起等待持有锁线程的
释放乐观锁的实质是采用CAS 在不加锁 假设不冲突的情况下完成某项操作 如果冲突则重试直到成功 为止内部实现是通过java native 方法(JNI) CAS的缺陷 第一 ABA问题 当某个值为A 修改为B 但又改为 A 通过加版本的方式解决(时间戳)第二 循环时间长开销大 通过CPU暂停指令解决 第三只能对一个 共享变量进行原子性操作 可以采用多个变量合并的方式
8.JMM的核心是happens_before 对于锁如果只有单个线程访问则可以将锁消除 votaile变量如果只有单个线程访问则可以将votaile变量当成普通变量
9.越是追求性能的处理器 内存模型设计就越弱
10.守护线程不能通过finally中的输出语句来判断程序执行完毕和内存资源的释放 因为程序执行完毕也不一定会执行try finally中的语句
11.在开启线程使最好给线程取好线程名 以便能够快速查找问题所在
12.在获取同步状态时 同步器会维护一个同步队列 获取同步状态失败的线程被加入到同步队列中进行自旋 获取同步状态成功的线程也就是同步队列的头结点出队或者被中断就会被唤醒自己结点的后继结点获取到同步状态
13.synchronized能隐式的重入锁 lock方法则不可以但可以显式再调用lock方法获取锁
14.排他锁 非公平锁可能造成线程饥饿 但减少了线程的切换 保证了更大的吞吐量 效率更高
15.读写锁 写锁是可重进入的排他锁 读锁是可重进入的共享锁 一个int变量记录读写锁的状态 读锁占
高16位写锁占低16位(1<<16) 读写锁 锁降级是否必要 答案是必要的因为 虽然获取读锁 有写锁获取时 则会被进入等待状态 但写锁获取之前可能有读锁获取到读锁 所以需要锁降级再次获取读锁进行数据同步
16.LockSupport类主要是用来阻塞和唤醒队列 ReentantLock内部主要是通过votaile技术和CAS技术实现同步 内部有同步器对同步队列和等待队列的操作 获取同步状态时 同步器会将获取失败的线程放入同步队列中 当同步队列头结点即获取到同步状态的被中断或者是被移除同步队列 会唤醒后继结点获取同步状态 LockSupport可以将线程在等待队列和同步队列之间切换
17.ConcurrentHashMap内部是segment类和HashEntry数组 Segment继承了ReentantLock类实现同步操作 将map中的数据分为多段 每段的长度会不一样 并加上锁 put方法是在segment上进行扩容加数据 采用的是哈希散列的方式再计算多段数据长度和时需要同步modCount方法判断容器是否有修改
18.ConcurrentLinkedQueue的入队方法永远返回的是true 不能通过入队方法判断是否入队成功
19.java是不能直接访问操作系统底层 只能通过本地方法访问 Unsafe是硬件级别的原子操作
第一 可以分配内存和释放内存 第二 阻塞和恢复线程(LockSupport) 第三CAS操作 第四 可以定位某对象的字段内存位置和修改对象字段值 即使是私有的
20.可以原子的更新引用类型 字段类 基本类型 数组 (AtomicReference)
21.CountDownLatch就是计数器当减到0时 await就不会阻塞当前线程了和CyclicBarrir的
22.Excharger类实现线程直接的交换数据
23.线程池的优点 第一 降低资源消耗 第二 缩短响应时间 第三提高对线程的管理性 线程的分配 调优 监控
24.线程池 当有个新任务进入线程池 没满则会创建新线程处理 如果线程池的基本数量的核心线程已经满了 会判断工作队列是否满了 没满则会放入阻塞队列中 如果满了 会让线程池创建新的线程处理这个任务 如果线程数量已经达到最大数量 则会执行饱和策略 默认是会报错 任务执行完了会去查看工作队列是否有任务如果有则会取出执行这个任务 如果工作队列空 并且当前线程池的线程数量大于基本数量则会被销毁 shutdown 和 shutdownNow方法 shutdownNow方法
25.建议使用有界的阻塞队列 需要有线程池的监控以方便知道线程各种情况 比如最大线程数
26.Excutors 方法fixThradPool CachedThreadPool是大小*的线程池 适用于短期异步执行的小程序 负载较轻的服务器
27.FutureTask未启动 时调用futureTask.cancel会导致任务不会被执行 FutureTask启动时 futureTask.cancel(true)会以中断的方式使任务 任务已经执行完 futureTask.cancel(*)会返回false
28.for(;;)比如while(1)好的原因是指令少 不怎么占用寄存器 没有判断跳转
29.AQS实现的同步器于 ReentantLock ReentantReadWriteLock Semphore CountDownLatch FutureTask
30.多线程项目是比较难调试 所以需要打印好日志信息 以便及时查找问题所在 异常处理打印日志
和同步linux命令查看系统cup等相关的信息
31.ps -eLf}grep java -c 查看线程数是否增长
netstat -nat|grep 3306 -c 查看指定端口的数据库连接数
cat /proc/net/dev 查看网络流量
cat /proc/loadavg 查看系统平均负载
cat /proc/meminfo 查询系统内存使用情况
cat /proc/stat 查看CPU的利用率
32.根据任务类型或者任务的优先级进行不同线程池的处理