2021-07-05总结java基础(面向面试)

JAVA基础

JVM虚拟机

img

  • Java虚拟机中有哪些内存空间
    • 栈内存(stack):方法运行的时候会进入栈内存,里面有一些局部变量,
      • 每个线程一个栈内存空间,
      • 虚拟机栈(JVM Stack):java方法每个方法有个 栈帧,会有一个程序计数器记录栈方法执行到了哪里
      • 本地方法栈(native Stack)
    • 堆内存(heap):每个对象new 的时候会存在里面,有属性还有成员方法的引用地址
      • 新生代(1/3内存)
        • Eden区(伊甸园区 8/10):
        • S1(1/10):
        • S2(1/10):
      • 老年代(2/3内存)
    • 方法区(元数据区:MetaSpace😃:存储一些字节码文件,类元信息,静态区存放静态方法或者变量,JDK8以后移除出堆内存
      • 常量池:String产生的对象都会在常量池
    • 本地方法区:调用底层操作系统的内存区
    • DirectBuffer:JDK1.4 NIO 调用JVM内存以外的操作系统内存
    • 寄存器:交给CPU进行使用的
  • 其中堆内存中:有老年代
线程内存模型(JMM)

堆内存是唯一的,栈内存是每个线程独有的,每个线程都有自己的堆内存,共享数据是在堆里面,将共享数据临时存储到线程中,叫做变量副本,每次使用都会优先从变量副本获取.理解:线程获取数据先复制一份,然后修改了然后就会重新覆盖共享数据;

类加载器加载机制
  • 类的加载:
    • 就是将class文件加载进内存,并创建一个java.lang.Class对象
    • 任何类被使用的时候都会为它创建一个java.lang.Class对象
  • 类的连接
    • 验证阶段:类的结构是否正确
    • 准备阶段:负责为变量分配内存
    • 解析阶段:将类的二进制数据中的符号引用替换为直接引用
  • 类的初始化
    • 对变量进行初始化
  • 初始化步骤
    • 还没被加载,连接,就先连接
    • 如果父类没有被初始化,先初始化父类
    • 有初始化语句,系统会依次初始化语句
  • 类的初始化时机
    • 创建类的实例
    • 调用类的方法
    • 访问类或者接口变量
    • 使用反射创建
    • 初始化子类
    • 使用java.exe运行某个子类
  • 类加载器的作用和分类
    • 作用:
      • 负责将.class文件加载到内存中,并为之产生对应的java.lang.Class对象
    • JVM的类加载机制:
      • 全盘负责:当一个类加载器加载某个CLass的时候,其他所有相关联的类都由他加载
      • 父类委托:当需要加载某个class的时候,会先请求父类,如果父类无法加载,则自己加载
      • 缓存机制:保证所有被加载过的Class都会被缓存,当程序需要使用某个CLass对象时,类加载器会先去缓存区搜索该Class,只有不存在才会去加载该类的二进制文件
    • java内置加载器:
      • 自定义加载器
      • 系统类加载器
      • 平台类加载器
      • 虚拟机内置加载器
垃圾回收机制
  • 什么样的对象会被回收
    • 当一个对象地址没有被引用的时候,就会被回收
  • 通知垃圾回收器
    • System.gc()
  • 垃圾回收算法
    • 引用计数算法(java不使用)
      • 计数器,每次对象被使用计数器就会+1,但是在循环使用的时候会出现问题,比如相互调用
    • 根搜索节点(JVM通常使用的算法)
      • 从一个节点开始搜索,一直往下寻找,没有被引用的就是垃圾对象
      • image-20210713150805770
  • 应用:
    • 尽量不要创建很大的对象
      • 原因:GC回收算法从来不对大对象(>85000字节)堆进行内存压缩整理,在堆中大的内存块会浪费很多CPU时间
    • 不要频繁的new生命周期很短的对象
      • 这样频繁的垃圾回收频繁压缩有可能会导致产生很多内存碎片
== ,hashCode(),equals()
  • hashCode()和equals()的区别
    • equals()相等,hashCode绝对相等,但是HashCode()相等,equals()不一定相等,如果HashCode相同再进行equals()比较不然效率很低()
  • == 和 equals的区别
    • == 号如果比较的是基本数据类型,那就比较的是那个值,如果比较的是一个引用对象,那么比较的就是地址值
    • equals() 是Object类的方法,Object类的equals方法用于判断对象的内存地址引用是不是同一个地址(是不是同一个对象),如果覆盖了equals方法,就要根据具体代码来实现,一般覆盖后的都是通过对象的内容来判断对象是否相等
基本数据类型:
  • 8种基本数据类型:byte,short,int,long,float,double,char,boolean
  • int/float 占4个字节 short/char占2个字节 long 占8个字节 byte/boolean占1个字节
String,StringBuffer,StringBuilder
  • String:

    • String定义的对象都在常量池里面

    • String常用的方法

      • 方法名说明
        public boolean equals(Obejct obj)比较字符串内容是否相同
        public int length()获取字符串的长度,就是字符个数
        public boolean contains(String s)判断是否包含传入的字符串
        public String subString(int start)从start开始截取字符串
        public boolean equalsignoreCase(String str)忽略大小写比较
        public String toLowerCase()字符串变成小写
        public String toUpperCase()字符串变成大写
        public String[] split(String str)分割字符串
        public char[] toCharArray()String变成Char数组
        public int indexof(String)字符串首次出现的位置
        concat(String)字符串连接
        replace(String,int)替换字符串
        trm()去掉多余的空格
  • String和StringBuilder的区别:

    • String是一个不可改变的字符序列:

      • String string = "ads";
              string = "sda";  //这一步只是记录了一个新的对象地址,就是new了一个String,他的底											层是一个final修饰的字符数组
        
    • StringBuilder是一个可以改变的字符序列

      • StringBuilder stringBuilder = new StringBuilder("告诉你:");
        stringBuilder.append("这是一个字符串");
        stringBuilder.delete(0, 2);
        System.out.println(stringBuilder);
        
  • 字符串拼接:

    • String+String的原理:其实是String底层内部在使用StringBuilder在进行拼接
    • StringBuild 拼接字符串原理:会创建一个缓冲区,进行操作,只要创建2个对象
  • StringBuilder和StringBuffer的区别

    • StringBuffer是线程安全的
    • StringBuilder是线程不安全的
    • 他们2个内容都是样的
先进内存的不能调用后进内存的
对多态的理解

同一个操作作用于不同的对象,可以有不同的解释,产生不同的执行结果.

优点:可以解耦,灵活应对复杂情况,

集合类
单列集合Collection
  • Collection的方法:
方法名说明
.add添加数据
.remove删除数据
.removeif根据条件删除(使用lambda的表达式)
.clear()清除数据
.contains()判断集合中是否有存在的元素
.isEmpty()是否为空
  • Iterator迭代器

    • terator iterator():返回i集合中的迭代器对象,该迭代器对象默认指向当前集合0索引处.

      • Iterator<Object> iterator = objects.iterator();
             while (iterator.hasNext()) {
                 Object next = iterator.next();
                 System.out.println(next);
             }
        
    • Iterator常用方法:

      • boolean hasNext():判断当前位置是否元素可以取出来
      • E next():获取当前位置的元素,将迭代器对象向下一个索引位置
List集合 (可以重复,有序的)

特有方法:add remove set(替换) get

    1. ArrayList: 线程不安全 允许存在 null 值 底层是数组,创建的话一般是10个,查询快,增删慢了,多线程修改会报错
    • 创建安全的List:

      •  List<Object> objects = Collections.synchronizedList(new ArrayList<>());
        
    • 使用for循环遍历删除的话,连续的会只删掉一个 原因:因为删除一个元素之后后面的会往前移动,然后导致指针直接跳过 解决方法:使用 i-- 删除的话可以使用iterator 的remove方法

    • CopyOnWriteArrayList:原理:(写时复制技术),读的时候可以并发读,写的时候复制一份原来的然后写,只能有一个线程写,独立写,最后合并覆盖

    1. LinkedList 线程不安全 底层是双向链表
    • 特有方法

      • 方法名说明
        void addLast(E e)将指定元素追加到末尾
        void addFirst(E e)将指定元素插入到表头
        E getFirst()返回链表第一个元素
        E getLast()返回最后一个元素
        E removeFirst()删除并返回第一个元素
        E removeLast()删除并返回在最后一个元素
    1. Vector 线程安全 底层是数组,每次创建默认都是10,如果空参的话就是0,如果超出会扩容10个,复制原来的到新的数组实现扩容;可变数组;(比较古老)
    1. Stack 线程安全,他是继承Vector, push () 加入方法 ,peek()查看栈顶的值,不删除,pop()删除栈顶的值,相当于出栈
  • 遍历方式:

    • .for循环
    • .foreach循环(底层就是Iterator)
    • 使用迭代器
  • 新特性遍历:

    • //新特性遍历java8遍历
      objects.forEach(s -> System.out.println(s));
      objects.forEach(System.out::println);
      
Set集合 不可重复

​ 如果没有实现HashCode就不会去除重复的不一样,没有索引,不能通过索引增删改查,可以使用迭代器,增强for循环

  • HashSet 他的底层是哈希表结构,实际是一个HashMap的实例,线程不安全,(数据+链表),不能保证存取顺序一致,没有索引

    • 可以使用Collection所有的方法,哈希值是JDK更具对象的地址或者属性值算出来的,int类型的整数,如果重写了hashCode就会根据对象属性计算出来
    • Tips::JDK8之前底层是数组+链表结构,数组长度默认是16,默认加载因子为0.75的数组,数组名为table;添加的位置是计算出来的,之后回和里面原有的数据使用equals进行比较; JDK8之后链表的长度等于8的时候,链表会变成红黑树
  • **TreeSet 😗*它的底层是红黑树组成的,线程不安全 ,可以将元素按照规则进行排序, Comparable要实现这个接口,不然添加的时候会报错!返回 1 表示存右边,返回-1表示存左边

    • //创建线程安全的TreeSet
      Collections.synchronizedList(new TreeSet<>());
      
      
  • CopyOnWriteSet:线程安全,写时复制技术,线程安全

Map集合 双列集合

键值对形式,键只能有一个,不能重复,值可以重复,

  • map的方法
方法说明
String put(String,String)向map添加数据,如果键存在会覆盖原先的值,把原先的值返回值返回
String remove(Object)根据键删除键值对
void clear()清楚所有元素
boolean containsKey(Object )判断集合是否包含指定的键
boolean containsValue(Object)判断集合是否包含指定的值
boolean isEmpty()判断集合是否为空
int size()集合的长度,也就是集合中键值对的个数
Set keySet()获取所有的键的集合
V get(Object key)根据键获取值
Set <Map.Entry<K,V>>entrySet()获取所有键值对的集合
K getKey()获得键
V getValue获得值
  • HashMap: 是JDK1.2出现的,允许存储null键和null值,线程不安全,如果键要存储自定义对象,就要重写hashCode和equals方法,防止键重复,Hash Map是支持fail -fast(如果当前线程正在遍历,其他线程对此做了修改,就会报错)

  • HashTable: 是jdk1.0出现的,线程安全,不允许存储null键和null值,他会锁上整个表

  • TreeMap : 和TreeSet一样都是红黑树结构,底层用Entrye对象,只根据键来排序,必须要进行排序,不然插入会报错,和值没有关系

  • **ConcurrentHashMap:**线程安全的,它只会锁上数组所在的链表,第一次添加元素创建hash表;

  • Map的遍历:

    • 1.获取所有key(keySet())然后获取所有key的值
    • 2.获取所有的键entrySet()值对,然后增强for遍历,
工具类Collections
  • Collections.sort(排序一个List) 底层是归并排序算法,如果是对象的话就要实现Comparaable, Array.sort 它是快速排序;
  • max .min 获取最大值和最小值
  • .binarySearch(list,89): 二分查找
  • .shuffle 洗牌算法:打乱里面的顺序
IO流

字节流:

类名说明
OutPutStream输出流
InputStream输入流
ZipOutPutStreamZIp压缩流

字符流:

类名说明
Reader字符读取流
Writer字符输出流

转换流:

类名说明
InputStreamReader输入转换流
OutputStreamWriter输出转换流

BIO—同步并阻塞的IO

AIO—处理高并发流,适合连接数较多且连接时间长的应用

NIO—同步不阻塞的IO;一般用于连接数较大,连接时间短的应用,如聊天

NIO

image-20210712193004088

NIO三要素:

capacity:容量(长度)

limit:界限(最多能读/写到哪)

posotion:位置(读/写哪个索引)

主要方法:

方法名说明
flip()写变读模式,将postition指针移到最前面,limit移到后面image-20210712195516276
get()读取一个字节
get(byte[ ]dst)读取多个字节
rewind()将position设置围为0可以重复读
clear()数据读写完毕(读变成写)
array()将缓冲区的转换成字节数组返回
Properties

底层是一个Map集合,不可以指定泛型,键不能重复,值可以,相同的话会被覆盖,和map方法一样

Properties和IO流结合的方法

方法名说明
void load (InputStream inStream)从输入字节流读取属性列表(键和元素对)
void load(Reader reader)从输入字符流读取属性列表(键和元素对)
void store(OutputStream out,String comments)将此属性列表(键和元素对)写入Properties表中,以结合于使用load(InputStream)方法的格式写入输出字节流(将集合写到文件)
void store(Writer writer,String comments)将此属性列表(键和元素对)写入此Properties表中,以适合使用load(Reader)方法的格式写入输出字符流(将集合写到文件))

示范代码:

public class PropertiesDemo {
    public static void main(String[] args) {
        Properties properties = new Properties();
        try {
            FileReader reader = new FileReader("src/main/java/JAVA基础/IO流/test.properties");
            properties.load(reader);
            reader.close();
            System.out.println(properties.getProperty("name"));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
反射

==tips:==用反射调创建对象,用反射调用成员变量,用凡是调用成员方法,利用反射的时候调用它类的属性和方法时,无视修饰符

Class文件有2个阶段

源代码阶段就是:java文件编译成Class文件(字节码文件)还在硬盘里,这时候就要通过调用加载器的ClassName()方法加载类,类加载器然后就将此class文件加载进内存,会创建class对象

获取Class对象的三种方式:
  1. 通过Class.forName(全限定名)
Class<?> aClass = Class.forName("JAVA基础.反射.StudentFS");
  1. 通过 类.class() 来获取
Class<StudentFS> studentFSClass = StudentFS.class;
  1. 通过 对象.getClass() 获取
StudentFS studentFS = new StudentFS();
Class<? extends StudentFS> aClass = studentFS.getClass();

示例代码:

   Properties properties = new Properties();
        InputStream resourceAsStream = ClassLoader.getSystemClassLoader().getResourceAsStream("test.properties");
        InputStreamReader inputStreamReader = new InputStreamReader(resourceAsStream, "UTF-8");
        try {
            properties.load(inputStreamReader);
            String classname = properties.getProperty("classname");
            Class<?> aClass = Class.forName(classname);
            //获取构造器
            Constructor<?> constructor = aClass.getConstructor();
            //获取带参构造器
            Constructor<?> constructor1 = aClass.getConstructor(String.class, int.class);
            System.out.println("constructor1"+constructor1);
            //用构造器获取一个对象
            Object o = constructor.newInstance();
            //获取所有公共的构造方法
            Constructor<?>[] constructors = aClass.getConstructors();
            for (int i = 0; i < constructors.length; i++) {
                System.out.println(constructors[i]);
            }
            //获取所有的构造方法,私有的也可以
            Constructor<?>[] declaredConstructors = aClass.getDeclaredConstructors();
            for (Constructor de : declaredConstructors
            ) {
                System.out.println(de);
            }
            //获取方法
            Method method = aClass.getMethod(properties.getProperty("method"));
            //调用方法
            method.invoke(o);


            //获取变量
             //aClass.getField();  这个方法只能获取公共的变量!!
            Field name = aClass.getDeclaredField("name");
            System.out.println(name);
            //设置可访问的
            name.setAccessible(true);
            name.set(o, "lzy");
            System.out.println(o.getClass().getMethod("getName").invoke(o));
        } catch (Exception e) {
            e.printStackTrace();
        }
JUC(java.util.concurrent)
Java wait方法和sleep方法的区别:
  • 基本使用
    • wait():来自于Object类,必须由锁对象调用
    • sleep():来自于Thread类,是Thread的静态方法,可以类名直接调用
线程和进程,并发,并行基本概念
  • 进程:是程序的一个实例,是系统分配资源的基本单位;

  • 线程:线程是进程任务调度和执行的基本单位

  • **并发:**同一时刻多个线程访问同一资源,多线程对一个点

  • **并行:**多项工作一起执行,然后汇总

  • 管程: Monitor监视器,同步的机制,就是锁的意思,JVM同步基于进入和退出,使用管程对象实现

  • 用户线程:用户自定义的线程,没有运行完JVM不会结束

  • 守护线程:守护在后台,比如垃圾回收,如果JVM都是守护线程JVM就会结束

创建线程的四种方式(线程池):
  • 继承Thread类

  • 实现Runnable接口

  • 线程池

    • 概念:线程池就是一个维护线程的池子,里面很多线程;线程过多会带来调度开销,进而影响缓存局部性和整体性能.而线程池可以充分利用内核,防止过分调度,有点降低资源消耗,提高线程可管理性

    • 线程池架构:Java中的线程池是Executor框架实现的

    • 使用的几种方式:

      • 一池N线程:FixedThreadExecutor()特点:如果达到容量上限,就会在队列等待;

      • 一池一线程:SingleThreadExecutor()

      • 根据需求创建线程池,可扩容,遇强则强:CachedThreadExecutor,最大是int的最大值,默认60秒

      • 自定义线程池:

        • public ThreadPoolExecutor(    int corePoolSize,    //核心线程数量,常驻线程数量,就是固定的数量
                                        int maximumPoolSize,  //最大线程数量
                                        long keepAliveTime,   //保持存活时间
                                        TimeUnit unit,		//存活单位
                                        BlockingQueue<Runnable> workQueue, //阻塞队列
                                        ThreadFactory threadFactory,  //线程工厂
                                        RejectedExecutionHandler handler) //拒绝策略,多种拒绝策略
          
      • 原理:底层都是new ThreadPoolExecutor(),new 线程池对象的时候不会创建线程,只有在execute()的时候才会创建,常驻线程为2,阻塞队列为3的情况下,第三个-第5个会在队列等候,6-8个会开启新线程执行,并不会队列没满就开启新线程,如果线程满了,队列满了,就会执行拒绝策略;

      • 拒绝策略:

        • AbortPolicy默认策略:直接抛异常
        • CallerRunPolicy调用者模式,退回到调用者
        • DiscardOldesPolicy抛弃队列中等待最久的任务
        • DiscardPolicy最好的策略:不做任何处理,也不抛异常
  • 实现Callable接口 会有返回值,使用FutureTask()接口

    • 可以用来创建线程,可以得到返回值,类似于Runnerable, 有一个 **call()**方法,而不是run()方法,没有返回值就会抛出异常;

      实现一个Callable的接口,要是使用FutureTask,使用new Thread不太行

      FutureTask():

      FutureTask的方法: get()方法,获取线程的返回值,还有isDone() 任务是否完成

      原理:他就是一个Runnable实现类

==Tips:==start()的执行,可能不会马上创建线程,由操作系统决定

Synchronized:
  • 说明:是java的关键字,一种同步锁,可以修饰代码块,一个方法,上锁和解锁是自动完成的

  • 锁对象:对于普通方法,锁是当前实例对象(this),对于静态同步方法.,锁的是当前类的.Class对象,同步代码块就是锁的括号里面对象

  • 同步代码块执行流程;

    1. 线程获得锁
    2. 清空自己线程的所有变量副本
    3. 拷贝共享变量最新的值到变量副本中
    4. 执行代码
    5. 将修改的变量副本中的值赋给 堆 里面的共享数据
    6. 释放锁
  • Synchronized底层原理:

    JDK早期的时候,底层实现是重量级的,重量级到这个synchronized要到操作系统去申请锁,效率非常低,

    后来有了锁升级概念: 当只有一个线程的时候,只是记录在这个线程的ID(偏向锁),如果偏向锁有竞争的话**,就会升级为自旋锁**,然后自旋转锁10圈左右变成重量级锁

Lock锁:

​ 使用这个可以实现手动解锁,上锁,syn发生异常时会自动释放锁,Lock是可重入锁,与Synchronized比较,Lock可以获得更广泛的锁操作,功能更强大,Lock不是java内置的,Lock是一个类,通过这类可以实现同步访问

private final ReentrantLock lock = new ReentrantLock();
  • 实现卖票:

    • @Data
      @AllArgsConstructor
      @NoArgsConstructor
      public class TicketsLock {
          private int number;
          private String ticketName;
          private final ReentrantLock lock = new ReentrantLock();
      
          public void sale() {
              //判断是否还有票
              lock.lock();
              try {
                  if (number > 0) {
                      System.out.println(Thread.currentThread().getName() + ":卖出" + (number--) + "剩下" + number);
      
                  }
              } finally {
                  lock.unlock();
              }
      
          }
      }
      
  • Condition: lock.newCondition()(详情参见线程间的通信)

LockSupport和Synchronized的好在哪
  • wait()需要释放锁,所以必须在Synchronized中使用,否则会抛出异常
  • notify()方法也必须在Synchronized中使用,并且需要指定对象
  • synchronized(),wait(),notify()对象必须一致,一个Syn代码块中只能有一个线程调用wait()
Synchronized和Lock的区别
  • syn是java自带的,是java的一个关键字,而Lock是一个类需要创建
  • syn在发生异常是,会自动释放所占有的锁,不会出现死锁,而Lcok在发生异常的时候,如果没有主动通过unLock去释放锁,就会很可能造成死锁现象,注意使用finally释放锁
  • Lock锁可以让等待锁的线程中断,而syn不行,是哟个syn的时候,所有的线程会一直等待下去,不能响应中断
  • 通过Lock可以知道有么日游成功获取锁,而syn无法办到
  • Lock可以提高多个线程的读取操作的效率,当有大量竞争时Lock性能大于syn
线程间的通信方式
    1. 使用synchronized实现通信
    • 问题:使用if会出现虚假唤醒(从哪里睡就会从哪里醒来,使用if的话不会进行判断,要使用while)

    • public class Share {
          //初始值
          private int number = 0;
      
          public synchronized void incr() throws InterruptedException {
              if (number != 0) {
                  this.wait();
              }
              number++;
              System.out.println(Thread.currentThread().getName() + "::" + number);
              this.notifyAll();
          }
      
          public synchronized void decr() throws InterruptedException {
              if (number != 1) {
                  this.wait();
              }
              number--;
              System.out.println(Thread.currentThread().getName() + "::" + number);
              this.notifyAll();
          }
      }
      
    1. lock.newCondition() 获得一个Condition对象,用于实现线程的等待和唤醒,但是必须在lock.lock()和lock.unlock之间才可以使用
    • Condition的方法:

      方法名说明
      void await()当前线程等待
      void signal()唤醒一个线程
      void singalAll()唤醒所有线程
    • lock原理:???

LockSupport和AQS(java锁和同步器框架的核心AQS)
  • 底层:是通过调用LockSupport.park()和LockSupport.unpark()来实现阻塞和唤醒,实际上调用的是Unsafe类的底层操作.

  • 使用: 直接使用LockSupport.park()没有加锁的限制,不用在同步代码块里

  • LockSupport可以唤醒具体线程

    • Thread t=new Thread({
      	LockSupport.park()
      }).start();
      
      //唤醒指定线程
      LockSupport.unpark(t)
      
      
      
Volatile,原子性,CAS算法
Volatile:

强制线程每次在使用的时候,都会看一下共享区域的最新值;他不能保持Volatile的原子性

作用:

  • 保证线程的可见性:高速缓存一致协议
  • 禁止指令重排:
    • 指令重排 :Cpu比内存快很多,cpu读取内存的时候,等待时间会干其他的,导致执行顺序不一样,意思就是:cpu在执行的过程中保证结果顺序是对的,不保证执行的顺序
    • image-20210715084059235
原子性:

​ 一次和多次操作中,要么所有的操作全部得到了执行,并且不会受到任何因素的干扰而中断,要么所有操作都不执行,多个操作是一个不可分割的整体

==tips:==使用synchronized锁解决原子性,但是效率比较慢,可以使用AtomicInteger()(更新一个整数还有其他的类型)

  • AtomicInteger 保证原子对象的类型
方法名说明
int get()获取值
int getAndIncrement()以原子方式先获取再增加+1
int IncrementAndtget()以原子方式先增加1再返回值
int addAndGet()以原子方式,加上传入的值,再返回结果
int GetAndadd()以原子方式,返回原来的值,再相加
CAS算法:

理解:2个线程 读取堆里面的数据,然后复制到变量副本,并且保存读取的值,修改完之后重新赋值给堆内存,然后比较旧值和现在堆的数据是不是一样的,是一样的我就修改,不一样的就重新读取再修改,这一步也称自旋

image-20210710165657201

Volatile中CAS算法源码解析:

实际上也是赋值给了一个Volatile修饰的int类型变量,最底层还是调用了本地方法(就是cpu的方法native修饰),本质上是使用了cpu 的一个叫做 高速缓存 一致性协议(MESI)

悲观锁和乐观锁:synchronized 是一种悲观锁,读写只能自己操作,乐观锁,别人可以看我,改的话要和原来的值比较

注意:并不CAS效率就比系统锁要高,要区分实际情况:

  • 执行时间短(加锁代码),线程数少,用自旋
  • 执行时间长,线程数多,用系统锁
公平锁,非公平锁,可重入锁,死锁,乐观锁,悲观锁
  • 非公平锁和公平锁
    • 非公平锁:线程会饿死,效率高
    • 公平锁:机会相等,效率相对较低
	//  没有参数的话默认就是非公平锁,或造成线程被饿死.
 	ReentrantLock lock =new ReentrantLock()
    //公平锁,写上TRUE
    ReentrantLock lock =new ReentrantLock(true)
  • 可重入锁:Lock和syn都是可重入锁,lock是显式 syn是隐式可重入锁,递归锁,一把钥匙进入多个区域,就是syn代码块内层嵌套.同步方法自己调用自己
  • 死锁:两个两个以上的进程争夺资源而造成一种互相等待的现象,如果没有外力感受,无法执行
    • 可能死锁原因:系统资源不足,进程运行推进顺序不合适,资源分配不当
  • 乐观锁和悲观锁
    • 乐观锁:每次获得锁都有个版本号,提交的时候和数据库比较版本号,版本号相同才可以修改
    • 悲观锁:只能一个人操作,效率很低,不支持并发操作
  • 表锁和行锁
    • 表锁:锁上整张表
    • 行锁:锁一行
  • 读写锁
    • 读锁:共享锁
    • 写锁:独占锁
    • 缺点:很容易造成锁饥饿,可能造成没有写操作,一个资源可以被多个线程访问,只可以被一个线程访问写操作,读写互斥,读和写不能同时存在,写的时候可以读,读的时候不能写
JUC中的辅助类
  • CountDownLatch类:构造方法可以设置一个计数器,然后使用countDown()方法来进行减1的操作,使用await()方法等待,计数器等于0,然后继续执行await方法之后的语句

    • 理解:班长关门,所有同学出去了才能锁门

      • public class CountDownLatchDemo {
            public static void main(String[] args) throws InterruptedException {
                CountDownLatch countDownLatch = new CountDownLatch(6);
                for (int i = 0; i < 6; i++) {
                    new Thread(() -> {
                        System.out.println(Thread.currentThread().getName() + "离开了");
                        countDownLatch.countDown();
                    }, String.valueOf(i)).start();
                }
                countDownLatch.await();  //这里
                System.out.println("班长锁门了");
            }
        
        }
        
        ----结果-----
        1离开了
        4离开了
        5离开了
        0离开了
        3离开了
        2离开了
        班长锁门了
        
        Process finished with exit code 0
        
  • CyclicBarrier类:循环栅栏,就是循环阻塞,阻塞的线程到了一定数量,就会完成,鼓噪方法和await方法

    • 理解方法:集齐 7颗龙珠

    • 代码:

      • //集齐7颗龙珠召唤神龙
        public class CyclicBarrierDemo {
        
            private static final int NUMBER = 7;
        
            public static void main(String[] args) {
                //创建CyclicBarrier
                CyclicBarrier cyclicBarrier = new CyclicBarrier(NUMBER, () -> {
                    System.out.println("集齐7颗龙珠召唤神龙");
                });
                //集齐7颗龙珠过程
                for (int i = 0; i < 7; i++) {
                    new Thread(() -> {
                        System.out.println(Thread.currentThread().getName() + "♥龙珠");
                        try {
                            cyclicBarrier.await(); //注意这里
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }, String.valueOf(i)).start();
                }
        
            }
        }
        
        
  • Semaphore类 :他是一个计数信号量,acquire()获取许可,release()释放一个许可

    • 理解:6辆车停3个停车位

    • 代码:

      //6台车3个停车位
      public class SemaphoreDemo {
      
          public static void main(String[] args) {
              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(new Random().nextInt(5));
                          System.out.println(Thread.currentThread().getName() + "--------离开了车位");
      
                      } catch (InterruptedException e) {
                          e.printStackTrace();
                      } finally {
                          semaphore.release();
                      }
                  }, String.valueOf(i)).start();
              }
          }
      }
      
      
  • Exchanger类:

  • 描述:Exchanger是2个线程之间进行交换数据

    • 创建方法:

      • Exchanger<String> exchanger = new Exchanger();
        
    • 使用方法:

    • new Thread(()->{
          String s="t1";
          try {
              System.out.println(Thread.currentThread().getName()+":S="+s);
               s = exchanger.exchange(s);
              System.out.println(Thread.currentThread().getName()+":S="+s);
          } catch (InterruptedException e) {
              e.printStackTrace();
          }
      
      },"t1").start();
      new Thread(()->{
          String s="t2";
          try {
              System.out.println(Thread.currentThread().getName()+":S="+s);
              s = exchanger.exchange(s);
              System.out.println(Thread.currentThread().getName()+":S="+s);
          } catch (InterruptedException e) {
              e.printStackTrace();
          }
      
      },"t2").start();
      
阻塞队列 BlockingQueue

使用这个类我们不要关心什么时候阻塞线程,什么时候唤醒线程

常见的队列说明
ArrayBlockingQueue(常用)基于数组实现的,长度固定的
LinkedBlockingQueue(常用)基于链表的阻塞队列
DelayQueue使用优先级队列实现的延迟无界队列,只有延迟的时间到了才能获取元素
PriorityBlockingQueue支持优先级排序的无界阻塞队列
SynchronousQueue不存储元素的队列,单个元素的队列
LinkedTransfeQueue由链表组成的无界阻塞队列
LinkedBlockingDeque由链表组成的双向队列

阻塞队列的方法

方法类型无法完成就抛出异常特殊值阻塞超时
插入add(e)offer(e)put(e)offer(e,time,unit)
移除remove()poll()take()poll(time,unit)
检查element()peek()不可用不可用
异步回调Completable

实现了Future接口,有返回值 ,mp消息队列都是异步回调

  • 同步与异步回调

    • public static void main(String[] args) throws ExecutionException, InterruptedException {
              //同步调用
              CompletableFuture<Void> completableFuture = CompletableFuture.runAsync(() -> {
                  System.out.println(Thread.currentThread().getName() + " CompletableFuture.runAsync");
              });
              completableFuture.get();
      
      
              //异步调用
              CompletableFuture<Integer> completableFuture1 = CompletableFuture.supplyAsync(() -> {
                  System.out.println(Thread.currentThread().getName() + " CompletableFuture.supplyAsync");
                  return 1024;
              });
      
              //完成之后   t是返回结果,如果有异常,u就是异常信息
              completableFuture1.whenComplete((t, u) -> {
                  System.out.println(u);
                  System.out.println(t);
              }).get();
          }
      

计算机网络

网络7层模型

img

  • 主机:

    • 应用层:为网络应用或者操作系统程序访问的服务接口可以通过这些接口进行数据的传输(FTP,HTTP,SNMP,DNS)
    • 表示层:解密和加密,图片解密和编码,数据的压缩,和解压缩常见:常见URL加密,口令加密,图片解码
    • 会话层:建立端连接并提供访问验证和会话管理;例如:使用校验点可使会话在通信失效的时候从校验点恢复通信;常见:服务器验证用户登录,断点续传
    • 传输层:提供应用进程之间的逻辑通信;例如:建立连接,处理数据包错误,数据包次序;常见:TCP,UDP,SPX,进程,端口(socket)
  • 网络:

    • 网络层:数据在节点之间传输创建逻辑链路,并分组发转发数据;例如:子网间的数据包进行路由选择常见:路由器,多层交换机,防火墙
    • 链路层:在通信的实体间建立数据链路连接;常见:数据分帧,物理寻址等;例如:网卡网桥,二层交换机
    • 物理层:为数据端设备提供原始比特流的传输的通路;例如网络中的数据传输介质;例如:网线,集线器
HTTP协议
  • 请求
    • 请求行:第一行,包含:请求方式,URI,版本协议,换行符,Get请求没有请求体
    • 请求头:服务端地址,客户端信息,压缩格式,浏览器可识别的语言
    • 请求空行:
    • 请求体:
  • 响应:
    • 响应行:版本协议,响应状态码,状态消息
    • 响应头:响应头名称,Content-type 告诉浏览器响应的类型image/png 前面是大范围,后面是小范围
    • 响应空行:
    • 响应体:
UDP和TCP
  • UDP
    • 三种通信方式
      • 单播 1:1 点对点发送
      • 组播 一对多,一个发送端,通过路由器发给多个服务器
      • 广播 一对所有,一个发送端,通过路由器发给所有服务器
  • TCP
    • 点对点可靠的连接

数据结构:

  • 二叉树(子节点最多只有2个节点的树,树的高度就是树有多少层;)
    • 二叉查找树 :(二叉排序树,二叉搜索树) 左边节点小于自己,右边节点大于自己

    • 平衡二叉树: 二叉树左右2个子树的高度不超过1 任意节点的左右俩个子树都是一颗平衡二叉树,左旋就是根节点向左移动,右旋 左侧结点太多就要像右边移动

    • 红黑树:也叫平衡二叉B树,根节点必须位黑色,每一个节点是黑色或者红色,如果一个节点没有子结点或者父节点那必须是黑色,不能出现2个红色节点相连,对每一个节点,从该节点到期所有后代叶节点的简单路径上,均包含相同数目的黑色节点

      红黑树规则:

      1. 节点必须是红色或者黑色
      2. 根节点必须是黑色
      3. 叶子节点是黑的空节点
      4. 不能有2个连续的红色节点
      5. 从任意节点到每个叶子节点的所有路径都包含相同数目的黑色节点
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

码码哈哈0.0

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值