学习笔记 2021.12.6
之前的时间由于课程原因没有进行继续的学习,这里在两周之类将并发、数据库初阶和JAVAweb全部啊解决。
2021.12.6
JUC
CPU密集型和IO密集型
都是为了描述前面七个参数中最大线程数得到。
CPU密集型其实就是几核的处理器,可以保持CPU的效率最高
IO密集型:判断程序中十分耗IO的线程,设置最大线程大于这个值即可
四大函数式接口
即只有一个方法的接口,好处在于可以简化编程模型。
而四大函数式接口即四个单单词的接口,不是复合单词的接口。
- 函数式接口

import java.util.function.Function;
public class Interface1 {
public static void main(String[] args) {
Function ff = (str)->{return str;};
System.out.println(ff.apply("fd"));
}
}
- 断定型接口
有一个输入参数,但是返回参数只能是布尔值。
public class Interface2 {
public static void main(String[] args) {
Predicate<String> pp = (str)->{return str.isEmpty();};
System.out.println(pp.test("s"));
}
}
此时的接口作用就是判断输入的字符是否为空。
- 消费者接口
只有输入,没有返回值

public class interface3 {
public static void main(String[] args) {
Consumer<String> con = (str) -> {
System.out.println(str);
};
con.accept("sdfdsf");
}
}
作用即是提供一个接口用于处理输入的东西。
- 供给型接口
没有参数,只有返回值
public class interfaces4 {
public static void main(String[] args) {
Supplier ss = () -> {return 1024;};
System.out.println(ss.get());
}
}
即提供一个接口用于解决输出的东西。
Stream流式计算
即专门用来计算的一个东西。用来处理用于存储数据的容器,比如说集合之类的东西。
先贴代码以及实现的结果:
package JUC.Demo14;
import java.util.Arrays;
import java.util.List;
public class Demo14 {
public static void main(String[] args) {
Eprson e1 = new Eprson(1,"a",21);
Eprson e2 = new Eprson(2,"b",22);
Eprson e3 = new Eprson(3,"c",24);
Eprson e4 = new Eprson(4,"d",25);
Eprson e5 = new Eprson(5,"e",26);
//集合用来存储
List<Eprson> ll = Arrays.asList(e1,e2,e3,e4,e5);
//计算交给流
ll.stream()
.filter((eprson)->{return eprson.getId()%2==0;})
.map((eprson)->{return eprson.getName().toUpperCase();})
.sorted((uu1,uu2)->{return uu2.compareTo(uu1);})
.forEach(System.out::println);
}
}

从上面可以看到,流式计算实现了对容器中的成员的具体操作。
总结下使用的感受吧:
- 首先是lamda表达式的使用,不要管具体的变量名称,只要将对应的容器转化成流过后,默认的就是对其中成员变量的操作,此时只要清除成员的类型即可。
- 只要管方法对应的函数式接口的实现准则,而不要去管方法本身的实现方式。就比如说filter方法的使用可能就是需要你写一个判断具体成员的方法体,只要是true的它都给你筛选出来。map方法就是让你写出集合内对应成员的处理方式,因此也才回去重写funtion接口。其他的都是一样的道理,不要被形式上的使用所迷惑了。
ForkJoin
用于并行执行任务提高效率的。大概的示意图如下

Forkjoin 的特点:工作窃取。

即能够让多核的情况下一个线程执行完了的话,可以去操作另外一个没有执行完毕的线程。实行的机制是基于这些都是双端队列。
具体的用法:
感觉不太普遍,这里就放截图了。理解有这么个过程就好。

知道为了处理大数据,有这么一个forkjoin的拆分过程就好。
异步回调
先略吧,后面学到了服务器和客户端以及相互之间的通信的话应该有助于这里的理解了。
Future
JMM
首先Volatile是JAVA虚拟机提供的轻量级的同步机制
- 保证可见性
- 不保证原子性。
- 禁止指令重排。
什么是JMM
JAVA内存模型,是一个不存在的东西,是一个约定。一些常见的同步约定:
- 线程解锁前,必须把共享变量立刻刷回主存。
- 线程加锁前,必须读取主存中的最新值到工作内存中去。
- 加锁和解锁必须是同一把锁。
简单理解就是一个在线程、线程内存和主内存之间的规定。具体有八种操作。示意图如下所示

此时问题在于线程B修改了值的话线程A无法实现同步更新。
具体的误差的测试:
package JUC;
//volatile的测试相关
import java.util.concurrent.TimeUnit;
//没有vlatile时VMM的bug
public class Demo16 {
private static int num = 0 ;
public static void main(String[] args) throws InterruptedException {
new Thread(()->{
while(num==0)
{
}
}).start();
TimeUnit.SECONDS.sleep(1);
num = 1;
System.out.println(num);
}
}
上面的代码就会因为在新线程中的num的值没有实现更新,从而导致程序一直没有办法停止。
Volatile的性质体现
能够体现可见性
解决上面问题的方法:
private volatile static int num = 0 ;
就把这一步改一下即可。
以此就体现了volatile的可见性,使得变量的实时更新对所有的线程都是可见的。
不能够体现原子性。
原子性:不可分割的性质
简单理解就是不能保证同步。
因此在不使用同步方法的前提下保证原子性的方法:使用原子类。

当然这个类本身也有着很多配套的实现比如说加减之类的方法。这些的底层实现都是跟操作系统相关。
更多的细节还需要后面的持续学习。
避免指令重排
指令重排:即写的程序不会按照理想中的顺序去执行。
单例模式
狂神这讲的有点马,先搁置吧,后面项目用到或者面试用到的话再去具体了解和学习。
单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。
这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。
注意:
- 1、单例类只能有一个实例。
- 2、单例类必须自己创建自己的唯一实例。
- 3、单例类必须给所有其他对象提供这一实例。
这里就记住以上内容即可。
深入理解CAS
CAS:比较并交换,输入参数包括期望的值和更新的值。是CPU的并发原语。
unsafe类,即JAVA中提供的能够通过调用c++去操作内存的类。
总结:
- 如果比较不同就会一直循环
- 循环会耗时
- 一次性只能保证一个变量的原子性。
ABA问题

反正就是其实中间有了变化了,但是有一方的操作却不知情。
解决方法就是引入带版本号的原子操作。即每次操作过后版本号都会进行更新。从而让其他线程知晓进行了操作。
各种锁
公平锁和非公平锁,即锁的对象能不能插队的区别,一般都是默认为非公平锁的。
可重入锁
自旋锁
死锁
完结(暂时的。)
浙公网安备 33010602011771号