个人学习笔记
生产者和消费者
- 传统的生产者和消费者的问题
public class Test1 {
public static void main(String[] args) {
Data data = new Data();
new Thread(()->{
for (int i = 0; i < 10; i++) {
try {
data.incr();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
try {
data.incr();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
try {
data.desc();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
try {
data.desc();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
}
}
class Data{
private int num =0;
public synchronized void incr() throws InterruptedException {
while (num!=0){ //放着虚假唤醒
this.wait();
}
System.out.println(Thread.currentThread().getName()+"->"+num);
num++;
this.notifyAll();
}
public synchronized void desc() throws InterruptedException {
while(num==0){ // 1
this.wait();
}
System.out.println(Thread.currentThread().getName()+"->"+num);
num--;
this.notifyAll();
}
}
问题:随机的状态
Condition精准的通知和唤醒线程
public class LockTest {
public static void main(String[] args) {
Data2 data2 = new Data2();
new Thread(()->{
for (int i = 0; i <10 ; i++) {
data2.A();
}
},"A").start();
new Thread(()->{
for (int i = 0; i <10 ; i++) {
try {
data2.B();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"B").start();
new Thread(()->{
for (int i = 0; i <10 ; i++) {
try {
data2.C();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"C").start();
}
}
class Data2{
private int num =0;
Lock lock = new ReentrantLock();
Condition condition = lock.newCondition();
Condition condition2 = lock.newCondition();
Condition condition3 = lock.newCondition();
public void A() {
lock.lock();
try{
while (num!=0){ //0 1
condition.await();
}
System.out.println(Thread.currentThread().getName()+"->aaa"+num);
num=1;
condition2.signal(); //精确通知 condition2执行
}catch (Exception e){
e.printStackTrace();
}finally {
lock.unlock();
}
}
public void B() throws InterruptedException {
lock.lock();
while (num!=1){ //0 1
condition2.await();
}
System.out.println(Thread.currentThread().getName()+"->BBB"+num);
num=2;
condition3.signal();
lock.unlock();
}
public void C() throws InterruptedException {
lock.lock();
while (num!=2){ //0 1
condition3.await();
}
System.out.println(Thread.currentThread().getName()+"->CCC"+num);
num=0;
condition.signal();
lock.unlock();
}
}
锁现象
- 当call()延迟1秒,先打印 打电话 还是.发短信
/**
* 当call()延迟1秒,先打印 1.打电话 2.发短信
*/
public class Test2 {
public static void main(String[] args) {
Person person = new Person();
new Thread(()->{
person.call();
}).start();
new Thread(()->{
person.message();
}).start();
}
}
class Person{
// synchronized 锁的对象是方法的调用者
// 两个方法用的是同一个锁,谁先拿到谁执行,所以先打印“打电话”
public synchronized void call(){
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("打电话");
}
public synchronized void message(){
System.out.println("发短信");
}
}
- 当synchronized锁的是静态方法,先打印哪个
public class Test3 {
public static void main(String[] args) {
Person2 person = new Person2();
new Thread(()->{
person.call();
}).start();
new Thread(()->{
person.message();
}).start();
}
}
class Person2{
// synchronized 锁的对象是方法的调用者
// static 静态方法
// 静态方法,类一加载就有了,静态方法属于Class,所以锁的是Class,全局只有一个Class模板,所以先打印打电话
public static synchronized void call(){
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("打电话");
}
public static synchronized void message(){
System.out.println("发短信");
}
}
集合类安全问题
常用的辅助类
CountDownLatch 减法计数器
CountDownLatch countDownLatch = new CountDownLatch(6);
for (int i = 0; i < 6; i++) {
int finalI = i;
new Thread(()->{
System.out.println(finalI);
countDownLatch.countDown();
}).start();
}
countDownLatch.await(); // 当程序计数器为0之后所有等待线程被释放
System.out.println("jieshu");
运行结果
0
1
2
4
3
5
jieshu
CyclicBarrier 加法计数器
CyclicBarrier cyclicBarrier = new CyclicBarrier(7,()->{
System.out.println("结束");
});
for (int i = 1; i <= 7; i++) {
int finalI = i;
new Thread(()->{
System.out.println(finalI);
try {
cyclicBarrier.await();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
}).start();
}
运行结果
1
2
3
4
5
6
7
结束
Semaphore 信号计数器
一般用于限流
//线程数量,例子停车,限流
Semaphore semaphore = new Semaphore(3);
for (int i = 0; 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();
}
阻塞队列
四组api
方式 | 抛出异常 | 有返回值,不抛异常 | 阻塞等待 | 超时等待 |
---|---|---|---|---|
添加 | add | offer() | put() | offer(“1”,2, TimeUnit.SECONDS) |
移除 | remove | poll() | take() | poll(2, TimeUnit.SECONDS) |
判断队列首 | element() | peek() |
抛出异常:当队列满了或为0了再 添加/读取 会抛异常
ArrayBlockingQueue queue = new ArrayBlockingQueue(3);
queue.add(1);
queue.add(2);
queue.add(3);
System.out.println(queue.element());//返回队首
//IllegalStateException: Queue full
queue.add(4);
queue.remove();
queue.remove();
queue.remove();
//NoSuchElementException
queue.remove();
有返回值,不抛出异常
ArrayBlockingQueue queue = new ArrayBlockingQueue(3);
System.out.println(queue.offer(1));
System.out.println(queue.offer(2));
System.out.println(queue.offer(3));
System.out.println(queue.offer(4));
System.out.println(queue.peek()); // 查看队首
System.out.println(queue.poll());
System.out.println(queue.poll());
System.out.println(queue.poll());
System.out.println(queue.poll());
/** 返回结果
* true
* true
* true
* false
* 1
* 2
* 3
* null
*/
阻塞 ,等待
ArrayBlockingQueue queue = new ArrayBlockingQueue(3);
queue.put(1);
queue.put(2);
queue.put(3);
//queue.put(4); 会一直等待阻塞
queue.take();
queue.take();
queue.take();
//queue.take(); 会一直等待阻塞
超时等待 ,超过时长取消等待
ArrayBlockingQueue queue = new ArrayBlockingQueue(3);
queue.offer(2);
queue.offer(3);
queue.offer(4);
queue.offer(1,2, TimeUnit.SECONDS);
System.out.println("jjjjjjjjjjjjjjjj");
queue.poll();
queue.poll();
queue.poll();
queue.poll(2,TimeUnit.SECONDS);
System.out.println("kkkkkkkkkkkkkkkkkk");
同步队列
SynchronousQueue 不存储队列,只能放一个取一个,再放一个
BlockingQueue<String> queue =new SynchronousQueue<String>();
new Thread(()->{
try {
queue.put("a");
System.out.println(Thread.currentThread().getName()+"插入一条数据a");
queue.put("b");
System.out.println(Thread.currentThread().getName()+"插入一条数据b");
queue.put("c");
System.out.println(Thread.currentThread().getName()+"插入一条数据c");
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
new Thread(()->{
try {
TimeUnit.SECONDS.sleep(2);
System.out.println(Thread.currentThread().getName()+"取出一条数据"+ queue.take());
TimeUnit.SECONDS.sleep(2);
System.out.println(Thread.currentThread().getName()+"取出一条数据"+ queue.take());
TimeUnit.SECONDS.sleep(2);
System.out.println(Thread.currentThread().getName()+"取出一条数据"+ queue.take());
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
/**执行结果
* Thread-0插入一条数据a
* Thread-1取出一条数据a
* Thread-1取出一条数据b
* Thread-0插入一条数据b
* Thread-0插入一条数据c
* Thread-1取出一条数据c
*/
线程池
四种拒绝策略
ThreadPoolExecutor.CallerRunsPolicy //哪里来得去哪里,哪个方法调用的哪个方法执行
ThreadPoolExecutor.AbortPolicy() //满了之后不执行,抛出异常
ThreadPoolExecutor.DiscardOldestPolicy //队列满了尝试和最早的竞争
ThreadPoolExecutor.DiscardPolicy //队列满了,丢掉任务,不抛异常
四大函数式接口
Function 函数式接口
/* Function<String,String> function = new Function<String, String>() {
@Override
public String apply(String s) {
return s;
}
};*/
//简化
Function<String,String> function = (s) ->{return s;};
System.out.println(function.apply("abc"));
断言型接口,返回布尔型
//判断是否为空
/* Predicate<String> predicate= new Predicate<String>() {
@Override
public boolean test(String s) {
return s.isEmpty();
}
};*/
Predicate<String> predicate=(s)->{return s.isEmpty();};
System.out.println(predicate.test("jj"));
消费型接口 ,没有返回值
/* Consumer<String> consumer = new Consumer<String>() {
@Override
public void accept(String s) {
System.out.println(s);
}
};*/
Consumer<String> consumer = (s)->{
System.out.println(s);
};
consumer.accept("jjj");
供给型函数,只有返回值
/* Supplier<String> supplier = new Supplier<String>() {
@Override
public String get() {
return "ddd";
}
};*/
Supplier<String> supplier = ()->{return "ddd";};
System.out.println(supplier.get());
异步回调
CompletableFuture
//没有返回值的 异步回调
CompletableFuture<Void> completableFuture = CompletableFuture.runAsync(()->{
try {
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("调用完成");
});
completableFuture.get(); //获取阻塞执行结果
System.out.println("kkkkkkkkkkkkkkkkk");
//有返回值的
CompletableFuture<Integer> completableFuture2 = CompletableFuture.supplyAsync(()->{
System.out.println("supplyAsync");
return 1024;
}).whenCompleteAsync((u, t)->{
System.out.println("u="+u); //正常的返回结果
System.out.println("t="+t); //错误信息
}).exceptionally((e)->{
System.out.println("m="+e.getMessage());
return 400;
});
System.out.println(completableFuture2.get());
JMM
什么事JMM:java内存模型不存在的东西,概念,约定
关于JMM的一些同步约定
- 线程解锁前必须把变量_立刻_刷回主存
- 线程加锁前,必须读取主存中的最新值到工作内存中
- 加锁和解锁是同一把锁
内存交互操作有8种,虚拟机实现必须保证每一个操作都是原子的,不可在分的(对于double和long类型的变量来说,load、store、read和write操作在某些平台上允许例外)
- lock (锁定):作用于主内存的变量,把一个变量标识为线程独占状态
- unlock (解锁):作用于主内存的变量,它把一个处于锁定状态的变量释放出来,释放后的变量才可以被其他线程锁定
- read (读取):作用于主内存变量,它把一个变量的值从主内存传输到线程的工作内存中,以便随后的load动作使用
- load (载入):作用于工作内存的变量,它把read操作从主存中变量放入工作内存中
- use (使用):作用于工作内存中的变量,它把工作内存中的变量传输给执行引擎,每当虚拟机遇到一个需要使用到变量的值,就会使用到这个指令
- assign (赋值):作用于工作内存中的变量,它把一个从执行引擎中接受到的值放入工作内存的变量副本中
- store (存储):作用于主内存中的变量,它把一个从工作内存中一个变量的值传送到主内存中,以便后续的write使用
- write (写入):作用于主内存中的变量,它把store操作从工作内存中得到的变量的值放入主内存的变量中
JMM对这八种指令的使用,制定了如下规则:
- 不允许read和load、store和write操作之一单独出现。即使用了read必须load,使用了store必须write
- 不允许线程丢弃他最近的assign操作,即工作变量的数据改变了之后,必须告知主存
- 不允许一个线程将没有assign的数据从工作内存同步回主内存
- 一个新的变量必须在主内存中诞生,不允许工作内存直接使用一个未被初始化的变量。就是怼变量实施use、store操作之前,必须经过assign和load操作
- 一个变量同一时间只有一个线程能对其进行lock。多次lock后,必须执行相同次数的unlock才能解锁
- 如果对一个变量进行lock操作,会清空所有工作内存中此变量的值,在执行引擎使用这个变量前,必须重新load或assign操作初始化变量的值
- 如果一个变量没有被lock,就不能对其进行unlock操作。也不能unlock一个被其他线程锁住的变量
- 对一个变量进行unlock操作之前,必须把此变量同步回主内存
代码实例
public static int num =0;
public static void main(String[] args) throws InterruptedException {
new Thread(()->{
while (num == 0){
}
}).start();
TimeUnit.SECONDS.sleep(2);
num = 1;
System.out.println(num);
}
程序不知道主内存中的值已经被修改过了,当num=1后,线程仍然不停止,引出volatile
volatile
保证可见性
//当num=1,线程知道主内存中的值已经被修改过了。
public volatile static int num =0;
public static void main(String[] args) throws InterruptedException {
new Thread(()->{
while (num == 0){
}
}).start();
TimeUnit.SECONDS.sleep(2);
num = 1;
System.out.println(num);
}
不保证原子性
//输出结果 19998
public volatile static int num =0;
public static void main(String[] args) throws InterruptedException {
for (int i = 0; i < 20000; i++) {
new Thread(()->{
add();
}).start();
}
while (Thread.activeCount()>2){
Thread.yield();
}
System.out.println(num);
}
public static void add(){
num++;
}
怎么保证原子性
- 加锁synchronized,lock
- 使用原子类解决
public static AtomicInteger num = new AtomicInteger();
public static void main(String[] args) throws InterruptedException {
for (int i = 0; i < 20000; i++) {
new Thread(()->{
add();
}).start();
}
while (Thread.activeCount()>2){
Thread.yield();
}
System.out.println(num);
}
public static void add(){
num.getAndIncrement();
}
避免指令重排
- 保证特定操作的执行顺序
- 可以保证某些变量的内存可见性
单例模式
懒汉模式
public class Lazy {
private Lazy(){
System.out.println("实例化");
}
static volatile Lazy lazy= null;
public static Lazy getLazy(){
if(lazy==null){
synchronized (Lazy.class){
if(lazy==null){
lazy = new Lazy();
}
}
}
return lazy;
}
}
问题:可以通过反射实例化
public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
getLazy();
Constructor<Lazy> constructor = Lazy.class.getDeclaredConstructor(null);
constructor.setAccessible(true);
Lazy lazy = constructor.newInstance();
}
查看newInstance()源码,不可以反射枚举类
if ((clazz.getModifiers() & Modifier.ENUM) != 0)
throw new IllegalArgumentException("Cannot reflectively create enum objects");
EnumSingle.class
public enum EnumSingle {
INSTANCE;
private EnumSingle getInstance(){
System.out.println("shijili");
return INSTANCE;
}
}
运行反射
public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
EnumSingle enumSingle2= EnumSingle.INSTANCE;
Constructor<EnumSingle> constructor = EnumSingle.class.getDeclaredConstructor(String.class,int.class);
constructor.setAccessible(true);
EnumSingle enumSingle = constructor.newInstance();
}
// 错误信息IllegalArgumentException: Cannot reflectively create enum objects
CAS 理解
AtomicInteger atomicInteger = new AtomicInteger(2020);
//如果期望值达到了,就更新,否则就不更新,CAS 是CPU的并发原语
System.out.println(atomicInteger.compareAndSet(2020,2021)); // true
System.out.println(atomicInteger.get()); //2021
System.out.println(atomicInteger.compareAndSet(2020,2021)); //false
System.out.println(atomicInteger.get()); // 2021
CAS:比较当前工作内存中的值和主内存中的值,如果这个值是期望的,那么则执行操作,如果不是就一直循环
缺点
- 循环会耗时
- 一次性只能保证一个共享变量的原子性
- ABA问题。