JUC
采用源码+文档方式学习
什么是JUC
java.util工具包

并发:多线程操作操作一个资源
cpu一核,模拟多条线程
并行:多个人一起走
CPU多核,多个线程同时运行
Wait和sleep
1、来自不同的类
wait ->Object
sleep -> Thread
2、关于锁的释放
wait会释放锁,sleep睡觉了,抱着锁睡觉,不会释放
3、使用的范国是不同的
wait必须在同步代码块中
sleep可以再任何地方睡
4、是否需要捕获异常
Wait不需要捕获异常
//现在也需要了
sleep必须要捕获异常?
锁
传统 Synchronized锁
线程就是一个单独的资源类,没有任何附属的操作
资源类OOP
多线程操作同一个资源类,把资源类丢入线程
1 2 3 4 5 6 7 8 9 10 11
| class Ticket { private int number = 30; public synchronized void sale(){ if (number>0){ System.out.println(Thread.currentThread().getName()+"卖出了"+(number- -)+"票,剩余:"+number); } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| import java.time.OffsetDateTime;Lock 接口
public class SaleTicketDemo01 { public static void main(String[] args) {
Ticket ticket = new Ticket();
new Thread(()->{ for (int i = 1; i < 40 ; i++) { ticket.sale(); } },"A").start(); } }
}
|
Lock锁

实现类

默认为非公平锁

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
|
class Ticket2 {
private int number = 30; Lock lock = new ReentrantLock(); public void sale(){ lock.lock(); try { if (number>0){ System.out.println(Thread.currentThread().getName()+"卖出了"+(number--)+"票,剩余:"+number); } } catch (Exception e) { e.printStackTrace(); } finally { lock.unlock(); } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| import java.util.concurrent.locks.ReentrantLock; public class SaleTicketDemo02 { public static void main(String[] args) {
Ticket2 ticket = new Ticket2(); new Thread(()->{for (int i = 1; i < 40 ; i++) ticket.sale();},"A").start(); new Thread(()->{for (int i = 1; i < 40 ; i++) ticket.sale();},"B").start(); new Thread(()->{for (int i = 1; i < 40 ; i++) ticket.sale();},"C").start(); }
|
Synchronized 和 Lock 区别
1、Synchronized 内置的Java关键字, Lock 是一个Java类
2、Synchronized 无法判断获取锁的状态,Lock 可以判断是否获取到了锁
3、Synchronized 会自动释放锁,lock 必须要手动释放锁!如果不释放锁,死锁
4、Synchronized 线程 1(获得锁,阻塞)、线程2(等待,傻傻的等);Lock锁就不一定会等待下
去;可以设置等待超时?
5、Synchronized 可重入锁,不可以中断的,非公平;Lock ,可重入锁,可以 判断锁,非公平(可以
自己设置);
6、Synchronized 适合锁少量的代码同步问题,Lock 适合锁大量的同步代码!
锁是什么,如何判断锁的是谁!
4、生产者和消费者问题
面试的:单例模式、排序算法、生产者和消费者、死锁
生产者和消费者问题
判断要使用if,不能使用while
:见多线程笔记
lock实现
并发资源类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
| class Data2{ private int number = 0; Lock lock = new ReentrantLock(); Condition condition = lock.newCondition();
public void increment() throws InterruptedException { lock.lock(); try { while (number!=0){ condition.await(); } number++; System.out.println(Thread.currentThread().getName()+"=>"+number);
condition.signalAll(); } catch (Exception e) { e.printStackTrace(); } finally { l ock.unlock(); } }
public synchronized void decrement() throws InterruptedException { lock.lock(); try { while (number==0){ condition.await(); } number--; System.out.println(Thread.currentThread().getName()+"=>"+number); condition.signalAll(); } catch (Exception e) { e.printStackTrace(); } finally { lock.unlock(); } } }
|
并发访问
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
| package com.kuang.pc; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; public class B { public static void main(String[] args) {Data2 data = new Data2(); new Thread(()->{ for (int i = 0; i < 10; i++) { try { data.increment(); } catch (InterruptedException e) { e.printStackTrace(); } } },"A").start(); new Thread(()->{ for (int i = 0; i < 10; i++) { try { data.decrement(); } catch (InterruptedException e) { e.printStackTrace(); } } },"B").start(); new Thread(()->{ for (int i = 0; i < 10; i++) { try { data.increment(); } catch (InterruptedException e) { e.printStackTrace(); } } },"C").start(); new Thread(()->{ for (int i = 0; i < 10; i++) { try { data.decrement(); } catch (InterruptedException e) { e.printStackTrace(); } } },"D").start(); } }
|
任何一个新的技术,绝对不是仅仅只是覆盖了原来的技术,优势和补充!
Condition 精准的通知和唤醒线程
condition1.await();
condition2.signal();
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| public void printA(){ lock.lock(); try {
while (number!=1){
condition1.await(); } System.out.println(Thread.currentThread().getName()+"=>AAAAAAA");
number = 2; condition2.signal(); } catch (Exception e) { e.printStackTrace(); } finally { lock.unlock(); } }
|
8锁问题
如何判断锁的是谁!永远的知道什么锁,锁到底锁的是谁!
Callable
1、可以有返回值
2、可以抛出异常
3、方法不同,run()/ call()
1 2 3 4 5
| public Integer call() { System.out.println("call()");
return 1024; }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| public class CallableTest { public static void main(String[] args) throws ExecutionException, InterruptedException {
new Thread().start(); MyThread thread = new MyThread(); FutureTask futureTask = new FutureTask(thread); new Thread(futureTask,"A").start(); new Thread(futureTask,"B").start(); Integer o = (Integer) futureTask.get(); 最后
System.out.println(o); } } class MyThread implements Callable<Integer> { @Override public Integer call() { System.out.println("call()");
return 1024; }}
|
1、有缓存 2、结果可能需要等待,会阻塞!
常用的辅助类(必会)
CountDownLatch
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| public class CountDownLatchDemo { public static void main(String[] args) throws InterruptedException {
CountDownLatch countDownLatch = new CountDownLatch(6); for (int i = 1; i <=6 ; i++) { new Thread(()->{ System.out.println(Thread.currentThread().getName()+" Go out"); countDownLatch.countDown(); },String.valueOf(i)).start(); } countDownLatch.await(); System.out.println("Close Door"); } }
|
countDownLatch.countDown();
// 数量-1
countDownLatch.await();
// 等待计数器归零,然后再向下执行 每次有线程调用 countDown() 数量-1,假设计数器变为0,countDownLatch.await() 就会被唤醒,继续 执行!
CyclicBarrier
加法计数器
Semaphore
信号量
semaphore.acquire() 获得,假设如果已经满了,等待,等待被释放为止
semaphore.release(); 释放,会将当前的信号量释放 + 1,然后唤醒等待的线程! 作用: 多个共享资源互斥的使用!并发限流,控制最大的线程数!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| public class SemaphoreDemo { public static void main(String[] args) {
Semaphore semaphore = new Semaphore(3); for (int i = 1; i <=6 ; i++) { new Thread(()->{
try { semaphore.acquire(); System.out.println(Thread.currentThread().getName()+"抢到车 位"); TimeUnit.SECONDS.sleep(2); System.out.println(Thread.currentThread().getName()+"离开车 位"); } catch (InterruptedException e) { e.printStackTrace(); } finally { semaphore.release(); } },String.valueOf(i)).start(); } } }
|
读写锁
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78
|
public class ReadWriteLockDemo { public static void main(String[] args) { MyCache myCache = new MyCache();
for (int i = 1; i <= 5 ; i++) { final int temp = i; new Thread(()->{myCache.put(temp+"",temp+""); },String.valueOf(i)).start(); }
for (int i = 1; i <= 5 ; i++) { final int temp = i; new Thread(()->{ myCache.get(temp+""); },String.valueOf(i)).start(); } } }
class MyCacheLock{ private volatile Map<String,Object> map = new HashMap<>();
private ReadWriteLock readWriteLock = new ReentrantReadWriteLock(); private Lock lock = new ReentrantLock();
public void put(String key,Object value){ readWriteLock.writeLock().lock(); try { System.out.println(Thread.currentThread().getName()+"写入"+key); map.put(key,value); System.out.println(Thread.currentThread().getName()+"写入OK"); } catch (Exception e) { e.printStackTrace(); } finally { readWriteLock.writeLock().unlock(); } }
public void get(String key){ readWriteLock.readLock().lock(); try { System.out.println(Thread.currentThread().getName()+"读取"+key); Object o = map.get(key); System.out.println(Thread.currentThread().getName()+"读取OK"); } catch (Exception e) { e.printStackTrace(); } finally { readWriteLock.readLock().unlock(); } } }
class MyCache{ private volatile Map<String,Object> map = new HashMap<>();10、阻塞队列 阻塞队列:
public void put(String key,Object value){ System.out.println(Thread.currentThread().getName()+"写入"+key); map.put(key,value); System.out.println(Thread.currentThread().getName()+"写入OK"); }
public void get(String key){ System.out.println(Thread.currentThread().getName()+"读取"+key); Object o = map.get(key); System.out.println(Thread.currentThread().getName()+"读取OK"); } }
|
阻塞队列
什么情况下我们会使用 阻塞队列:多线程并发处理,线程池! 学会使用队列 添加、移除

线程池(重点)
线程池:三大方法、7大参数、4种拒绝策略



























12:四大函数式接口
lamdba表达式,函数式接口,链式编程,Stream流

Stream流式计算
操作:存储+计算

10


14:ForkJoin
分支合并
- jdk1.7之后出来的
- 并行执行任务,提高效率
- 跳过
15:异步回调
16:JMM
17:Volatile可见性及其非原子性验证
保证可见性
不保证原子性
ACID中就有原子性:不可分割
幻读,脏读
atomic包
使用原子类解决原子性问题
指令重排
源代码->编译器优化的重排,指令并行也可能会重排->内存系统也会重排–>执行
volatile避免指令重排
- 内存屏障.cpu指令,作用:
- 1:保证特定的操作的执行顺序
- 可以保证某些变量的内存可见性
DCL懒汉式就使用了volatile

懒汉式
- 先构造器私有
- 不为空则再为创建

- 多线程情况下失败
- 如果==null,先上锁
- 双重检测锁懒汉式
- lazayman=new lazyman()不是原子性操作
- 分配内存空间
- 执行构造方法,初始化对象
- 把对象指向空间
- 如果走成132
- 其他线程发现空间里有对象,但是实际上未初始化,返回了未初始化的对象


- 利用反射破坏单例模式

- 防止
- 给构造器加个锁,如果已经有对象,

枚举:
jdk1.5新增
本身也是一个class类
CAS
compareAndSwap:比较并交换
如果期望的值达到了,则更新,否则不更新

java无法操作内存
java可以调用C++native
C++可以操作内存
java的后门,可以通过这个操作内存

offset:内存偏移

- 内存操作,效率很高
- 自旋锁,不停旋转直到成功为止

- CAS:ABA问题(狸猫换太子)
- 比较当前工作内存中的值和主内存的值,如果之歌值是期望的,那么执行操作,如果不是就一直循环
- 好处:自带原子性
- 缺点 :
- 循环会耗时
- 一次性只能保证一个共享变量的原子性
- UnSafe类
ABA问题
CAS时捣乱线程改过又改回去了
原子引用
TimeUnit.secondes.sleep()来延时
像SQl插入后版本号提升1

原子类型改为原子引用,加上了时间戳
乐观锁有CAS和版本号两种解决方式

此处需要敲代码,还有单例模式这些
21:对各种锁的理解
公平锁,非公平锁
- 公平锁暖插队,公平锁可以插队
- 非公平锁,可以插队(默认都是非公平)
- 3s的不等待很多秒的
- Lock =new RestrantLock()
- 参数传入True则为公平锁
可重入锁/递归锁
Re en trant:Reentrant
001
