JDK:java开发工具包 包括了 Java 运行环境 JRE、Java 工具和 Java 基础类库。
JER:java运行时环境 包含java虚拟机和java程序的一些核心类库。
JVM:java虚拟机(java实现跨平台)能够运行以 Java 语言写作的软件程序。
JDK>JER>JVM
什么是java? Java是一种可以撰写跨平台应用程序的,面向对象的程序设计语言
1.配置环境变量?
属性----高级系统设置----环境变量----系统变量----ComSpec
2.配置环境变量的步骤:
1.新建系统变量JAVA_HOME
2.选择系统变量Path (新建一个值,路径为JDK的bin目录)
3.新建系统变量CLASS_PATH(路径为默认路径,值为lib目录)
3.面向对象三大特征:
1)封装:隐藏对象的属性和实现细节,对外提供一个公共访问方式,如类和方法
关键字:private
2)继承:子类调用父类的属性和方法(从父类中新生一个子类,具有父类的属性和行为,而且可以拓展新的能力),只能单继承
关键字:extends
3)多态:多种形态
分两部分
1.功能多态:一个行为多种实现方式:重写与重载
2.形态多态:一个类型的多种表现形式 e.g.动物的多种表现形式
两种实现形式:
1.将父类的类型作为方法的形参 在调用方法时给他传递具体的子类对象
2.将父类作为方法的返回值类型来实现多态(计算器(工厂模式))
应用:
1. 向上转型:父类引用指向子类对象
2.向下转型:父类引用显式类型转换为子类对象。
前提:继承和方法的重写
编译看左边(Java源代码 .java文件),运行看右边(JVM运行 .class)
4)抽象 :
Java中可以定义被abstract关键字修饰的方法,这种方法只有声明,没有方法体,叫做抽象方法.
Java中可以定义被abstract关键字修饰的类,被abstract关键字修饰的类叫做抽象类
4.对象三个特点:
属性:用来描述对象的基本特征。
功能:用来描述对象的可以完成的操作,
标识:是指每个对象在内存中都有一个唯一的地址值用于与其他对象进行区分,类似于我们的身份证号
5.方法:被命名的代码块
格式:修饰符 返回值类型 方法名 (参数列表){方法体}
public static void main (String[] args) {.....}
6.构造方法
主要功能:完成对象创建和初始化
创建对象会自动调用构造方法
构造方法可以和普通方法一样可以重载
与类同名且没有返回值类型
会在创建对象时候立即执行
默认创建无参构造,但如果自定义含参构造,默认无参构造会被覆盖,需要手动添加
7.重写的要求:两同两小一大
两同:方法名 ,参数列表完全一致
两小:子类的 返回值类型 ,抛出的异常 要小于等于父类
一大:子类方法的修饰符权限大于等于父类被重写方法的修饰符权限
8.this 和 super 区别?
this是本类对象引用 指向调用该方法的对象
super 是父类存储空间标识
9.重载和重写的区别?
重载:在一个类中的现象:同一个类中,存在方法名相同,参数列表不同的方法
重写:是指建立了继承关系以后,子类对父类的方法不满意,可以重写,遵循两同两小一大原则
重载的意义:是为了外界调用方法时方便,不管传入什么样的参数,都可以匹配到对应的同名方法
重写的意义:在不修改源码的情况下,进行功能的修改与拓展(OCP原则:面向修改关闭,面向拓展开放)
10.执行顺序:静态代码块 --> 构造代码块 --> 构造方法 --> 局部代码块
11.数组:Array ,标志[ ].用于存储多个相同数据类型数据的集合,获取元素可以通过下标来获取,数组下标从0开始,下标的最大值是数组的长度减一。
两种状态:静态数组两种:int [ ] a= new int [ ]{1,2,3,4,5};
int [ ] b= {1,2,3,4,5};
动态数组一种 int [ ] c=new int[5];
12.面向对象:是一种编程思想,只关注结果,不关注过程,过程由工具API来完成。
.迭代集合/遍历集合
/**迭代步骤:
* 1.获取集合的迭代器 c.iterator();
* 2.判断集合中是否有下一个可迭代的元素 it.hasNext()
* 3.获取当前迭代到的元素 it.next()*/
Iterator<Integer> it = c.iterator();
while(it.hasNext()){
Integer num = it.next();
System.out.println(num);
}
工具类:1.Arrays.toString(数组) 把数组里的数据,用逗号连接成一个字符串[值1,值2]
2.Arrays.sort(数组):对数组进行排序,对于基本类型的数组使用的是优化后的快速排序算法,效率高
对引用类型数组,使用的是优化后的合并排序算法
3.Arrays.copyOf(数组,新的长度)
---------------------------------------------------------------
线程:
Java线程生命周期:新建--就绪--运行--阻塞--死亡
创建线程的三个方法:
1.继承Thread类创建方式
1)创建Thread类的子类,并重写该类的run方法,该run方法的方法体就代表了线程要完成的任务。因此把run()方法称为执行体。
2)创建了Thread子类的实例,即创建了线程对象。
3)调用线程对象的start()方法来启动该线程。
package com.thread;
public class Thread01 {
public static void main(String[] args) {
MyThread01 t1=new MyThread01();
MyThread01 t2=new MyThread01();
t1.start();
t2.start();
}
}
class MyThread01 extends Thread{
@Override
public void run() {
System.out.println("线程名:"+currentThread().getName());
}
}
2.实现Runable接口
1)定义runnable接口的实现类,并且重写该接口的run()方法,该run()方法的方法体同样是该线程的线程执行体
2)创建Runnable实现类的实例,并且依此实例作为Thread 的target来创建thread对象,该Thread对象才是真正的线程对象。
3)调用线程对象的start()方法来启动该线程。
线程的执行流程:当执行代码strart()的时候,就会执行对象中重写的void run()方法,方法执行完后线程消亡。
package com.thread;
public class Thread02 {
public static void main(String[] args) {
MyThread02 target=new MyThread02();
Thread t1=new Thread(target);
Thread t2=new Thread(target);
t1.start();
t2.start();
}
}
class MyThread02 implements Runnable{
@Override
public void run() {
System.out.println("线程名:"+Thread.currentThread().getName());
}
}
3.通过Callable 和 Future创建线程
1)创建Callable接口实现类,并实现call()方法,该call()方法作为线程执行体,并且有返回值
2)创建Callable实现类的实例,使用FutureTask类来包装Callable 对象,FutureTask 对象封装了Callable 对象的call()方法的返回值。
3)使用FutureTask 对象作为Thread对象的target创建并且启动新进程。
4) 调用FutureTask对象的get ()方法来获得子线程执行结束后的返回值。
package com.thread;
import java.util.concurrent.*;
public class Thread03 {
public static void main(String[] args) throws ExecutionException, InterruptedException, TimeoutException {
Callable callable=new MyThread03();
FutureTask task1=new FutureTask(callable);
FutureTask task2=new FutureTask(callable);
new Thread(task1).start();
new Thread(task2).start();
Thread.sleep(10);//等待线程执行结束
//task.get() 获取call()的返回值。若调用时call()方法未返回,则阻塞线程等待返回值
System.out.println(task1.get());
System.out.println(task2.get());
//get的传入参数为等待时间,超时抛出超时异常;传入参数为空时,则不设超时,一直等待
System.out.println(task1.get(10L, TimeUnit.MILLISECONDS));
System.out.println(task2.get(10L, TimeUnit.MILLISECONDS));
}
}
class MyThread03 implements Callable{
@Override
public Object call() throws Exception {
System.out.println("线程名:"+Thread.currentThread().getName());
return "实现callable:"+Thread.currentThread().getName();
}
}
线程池原理:
1)由提交者提交任务,判断核心线程池是否已满,如果不满,创建线程执行当前任务,如果已满
2)判断阻塞队列是否已满,如果未满,将任务储存在队列中,等待执行,如果已满
3) 判断线程池是否已满,如果未满,创建线程执行任务,如果已满,按照饱和策略进行处理
创建线程池的四种方法:
(1)newCachedThreadPool 创建一个可缓存的线程池,如果线程池长度超过处理需求,可灵活回收空闲线程,若无可回收,则新建线程。
(2)newFixedThreadPool 创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待
(3)newScheduledThreadPool 创建一个定长线程池,支持定时及周期性任务执行
(4)newSingleThreadExecutor 创建一个单线程化的线程池,它只会唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO,LIFO,优先级)执行
------------------------------------------------------
抽象类:被abstract关键字修饰的方法,叫做抽象方法。(只有声明,没有方法体)
被关键字abstract修饰的类叫做抽象类
如果一个类含有抽象方法,它一定是抽象类,其中要实现抽象方法需要子类完成
抽象方法格式:权限修饰符 abstract 返回值类型 方法名(参数列表);
特点:1.abstract可以修饰方法和类
2.抽象类中可以没有抽象方法
3.如果抽象类中有抽象方法,那么该类必须定义为抽象类
4.子类继承抽象类以后,要么还是一个抽象类,要么把父类所有抽象方法都重写
5.应用于多态中
6.抽象类不可以被实例化
类可以 implements(实现) 接口
Java虚拟机的类加载器?
启动类加载器:扩展类加载器 应用程序类加载器 自定义类加载器
类加载过程:加载,验证,准备,解析,初始化,(验证,准备,解析统称为连接)
接口定义:Java中也是一种抽象类型,接口中的内容是抽象形成的需要实现的功能,
格式 :interface 接口 {代码}
特点:通过interface关键字来定义接口
通过implements让子类来实现接口
接口中的方法全部都是抽象方法(JAVA8)
可以把接口理解成一个特殊的抽象类(但接口不是类!!!)
类描述的是一类事物的属性和方法,接口则是包含实现类要实现的方法
接口突破了java单继承的局限性,一个接口可以继承多个接口
接口和类之间可以多实现,接口与接口之间可以多继承
接口是对外暴露的规则,是一套开发规范
接口提高了程序的功能拓展,降低了耦合性
总结 :没有成员变量,都是常量
接口和抽象类的区别:
接口是一种用interface定义的类型,用implement实现接口,抽象类是一种用class定义的类型,抽象类的子类用extend来继承
接口的方法都是抽象方法,还有默认和静态方法,抽象类的方法不限制
接口中的都是静态常量,抽象类可以写普通成员变量
接口没有构造方法,不能实例化,抽象类有构造方法,同样不能实例化
接口可以多继承,抽象类可以单继承
接口中的方法默认使用public修饰;抽象类中的方法可以任意访问修饰符
接口不能有main方法,抽象类可以有main 方法,并且可以运行
------------------------------------------
11.异常:封装错误信息的对象,
组成部分:异常的类型,提示信息,报错的行号提示
一般分为运行异常和编译异常还有逻辑错误,可修改的是编译异常
继承Trowable后,分为两个Error 和 Exception
Error:错误,程序无法处理
Exception:可以编码修复的错误:
RunTimeException--运行时异常
ArithmeticException --算数异常
InputMismatchException --输入不匹配异常
异常处理方式:捕获或者向上抛出
捕获代码:try {需要捕获的代码}catch(异常类型 异常名){处理方案}
向上抛出:
throw是和throws?
throw:用在方法声明处,后面跟着异常类的名字,会抛出异常,需要由本方法的调用者处理异常,但是只是一种可能性,异常不一定会发生
throws:用在方法内部,后面跟着是异常对象的名字,表示此方法出现异常,由方法体内的语句处理,注意:执行throws一定抛出了某种异常。
运行时异常:
NullPointerException - 空指针引用异常
ClassCastException - 类型强制转换异常。
IllegalArgumentException - 传递非法参数异常。
ArithmeticException - 算术运算异常
ArrayStoreException - 向数组中存放与声明类型不兼容对象异常
IndexOutOfBoundsException - 下标越界异常
NegativeArraySizeException - 创建一个大小为负数的数组错误异常
NumberFormatException - 数字格式异常
SecurityException - 安全异常
UnsupportedOperationException - 不支持的操作异常
--------------------------------------------------------
内部类:定义一个类后,又在这个类里再定义一个类
特点:
1)可以直接访问外部类成员,包括私有成员
2)外部类要想访问内部类成员,要先建立内部类对象
3)在成员(局部)位置的内部类是成员(局部)内部类
API:是一些预先定义的函数,也可以理解为一种通用功能集
常用的方法:
toString()-----用于返回对应对象的字符串表示
hashCode()----返回对应对象的哈希码值
equals()---------用于指示其他某个对象是否与当前对象“相等”
SpringAPI总结:
int hashCode() 获取集合对象的哈希码值
length() 返回字符串的长度
indexOf(String str) 返回指定字符在此字符串第一次出现处的索引
lastIndexOf(String str)返回指定字符在此字符串中最后一次出现处的索引
String toString () 返回此对象本身
toUpperCase() 字符转大写
toLowerCase() 字符转小写
concat()把指定字符串拼接到此字符串的结尾
String[] split(String str)根据给定元素来分割此字符串
trim() 返回去除首尾空格字符串
substring( int beginIndex)返回一个新子串,从指定下标处开始,包含指定下标
substring( int beginIndex, int endIndex)返回一个新子串,从指定下标开始,到结束下 标结束 ,(含头不含尾)
static String valueOf(int i )把int转化为String
Empty () 判断指定字符串是否为空
size()返回指定字符串的个数
boolean equals(Object anObject)将此字符串与指定对象的对象比较,比较的是重写后的串的具 体内容
startsWith(String prefix)测试字符串是否以指定的元素开头
endsWith(String suffix) 测试字符串是否以指定字符串结束
char charAt(int index) 返回指定索引/下标处的char 值/字符
decimal 这个数据类型比double准确
基本数据类型:数据都存在栈上
String类不可以被继承,因为使用final修饰
---------------------------------------------------
流:
BIO : Block IO 同步阻塞式 IO,平常使用的传统 IO | 模式简单使用方便,并发处理能力低。 |
NIO: New IO 同步非阻塞 IO,是传统 IO 的升级 | 客户端和服务器端通过 Channel(通道)通讯,实现了多路复用。 |
AIO: Asynchronous IO 是 NIO 的升级,也叫 NIO2 | 实现了异步非堵塞 IO ,异步 IO 的操作基于事件和回调机制。 |
输入流读取---in 输出流====out
TIPS:磁盘到JAVA内存 为in
IO流继承结构:
字节流:针对二进制 InputStream OutputStream
字符流:针对文本文件 Reader Writer
File文件类:
封装一个磁盘路径字符串,,对这个路径可以执行一次操作
可以封装的有文件路径,文件夹路径,不存在的路径
字节流读取
字节流是由字节组成的,字符流是由字符组成的.
Java里字符由两个字节组成.字节流是基本流,主要用在处理二进制数据。
所以字节流是比较常用的,可以可以处理多种不同种类的文件,比如文本文档/音频/视频等等
1.InputStream抽象类:表示字节输入流的所有类的超类/抽象类,不可以创建对象
常用方法:
abstract int read() 从输入流中读取数据的下一个字节
int read(byte[] b) 从输入流中读取一定数量的字节,并将其存储在缓冲区数组 b 中
int read(byte[] b, int off, int len) 将输入流中最多 len 个数据字节读入 byte 数组,off表示存时的偏移量
void close() 关闭此输入流并释放与该流关联的所有系统资源
2.FileInputStream子类:直接插在文件上,直接读取文件数据
3. BufferedInputStream子类:BufferedInputStream 为另一个输入流添加一些功能,在创建BufferedInputStream 时,会创建一个内部缓冲区数组(默认8k大小)。在读取或跳过流中的字节时,可根据需要从包含的输入流再次填充该内部缓冲区,一次填充多个字节。
字符流读取:
常用于处理了处理纯文本数据,读写容易出现乱码现象,在读写的时候最好指定编码集UTF-8
Reader抽象类:
----------------------------------------
反射:JAVA开发语言特征之一,在运行状态下可以知道任何一个类的方法和属性,对于任何一个对象可以知道他的方法,是一种动态获取信息以及动态调用对象的方法的功能。
反射用到的API:
Class.forName("类的全路径");
类名.class
对象.getClass();
常用方法:
获取包名 类名
clazz.getPackage().getName()//包名
clazz.getSimpleName()//类名
clazz.getName()//完整类名
获取成员变量定义信息
getFields()//获取所有公开的成员变量,包括继承变量
getDeclaredFields()//获取本类定义的成员变量,包括私有,但不包括继承的变量
getField(变量名)
getDeclaredField(变量名)
获取构造方法定义信息
getConstructor(参数类型列表)//获取公开的构造方法
getConstructors()//获取所有的公开的构造方法
getDeclaredConstructors()//获取所有的构造方法,包括私有
getDeclaredConstructor(int.class,String.class)
获取方法定义信息
getMethods()//获取所有可见的方法,包括继承的方法
getMethod(方法名,参数类型列表)
getDeclaredMethods()//获取本类定义的的方法,包括私有,不包括继承的方法
getDeclaredMethod(方法名,int.class,String.class)
反射新建实例
clazz.newInstance();//执行无参构造创建对象
clazz.newInstance(666,”海绵宝宝”);//执行含参构造创建对象
clazz.getConstructor(int.class,String.class)//获取构造方法
反射调用成员变量
clazz.getDeclaredField(变量名);//获取变量
clazz.setAccessible(true);//使私有成员允许访问
f.set(实例,值);//为指定实例的变量赋值,静态变量,第一参数给null
f.get(实例);//访问指定实例变量的值,静态变量,第一参数给null
反射调用成员方法
Method m = Clazz.getDeclaredMethod(方法名,参数类型列表);
m.setAccessible(true);//使私有方法允许被调用
m.invoke(实例,参数数据);//让指定实例来执行该方法
#{}和${}的区别?
#作为一个参数占位符,sql预编译 防止sql注入, 变量替换是在DBMS 中,变量替换后 ,对应的变量会自动➕单引号
$作为一个拼接符,sql拼接 不能防止sql注入,变量替换是在DBMS 外 不会加单引号
集合:
Map接口和Collection 接口是所有集合框架的父接口
List: 数据是有序的 list集合有下标
ArrayList底层是数组:有下标 查询快 增删慢
LinkedList底层是链表:有下标 查询慢 增删快
set: 数据是无序的 Set集合没有下标
数据不可以重复,常用来给数据去重,
-----------------------------------------------------------
HastSet:底层维护的是哈希表,包装了HashMap,相当于向HashSet中存入数据时,会把数据作为K,存入内部的HashMap中。当然K仍然不许重复。
TreeSet :底层维护的是TreeSet,也是红黑树的形式,便于查找数据
HashMap底层原理:
jdk1.7之前是数组+链表
jdk1.8后是数组+红黑树
-----------------------------------------------
hashmap为什么使用红黑树?
再JDK1.8版本后,java对hashmap做了改进,在链表长度大于8的时候,后面的数组 存在红黑树中,用来加快检索速度,红黑树本质是一颗二叉直找树
深拷贝和浅拷贝:
浅拷贝:对基本类型进行值拷贝,对引用类型拷贝引用;
深拷贝:基本类型进行值拷贝,对引用类型对象不但拷贝对象的引用,还拷贝对象的相关属性和方法。不同在于:深拷贝创建了一个新的对象。
事务的底层:是一个连接,Connection
数据库外键以及主键作用?
主键保证了数据的唯一性,外键保证了数据的完整性。
主键是能确定一条记录的唯一标识,比如,一条记录包括身份正号,姓名,年龄。 身份证号 是唯一能确定你这个人的,其他都可能有重复,所以, 身份证号 是主键。
外键用于与另一张表的关联。是能确定另一张表记录的字段,用于保持数据的一致性。比如,A表中的一个字段,是B表的主键,那他就可以是A表的外键
------------------------------------------------
JDK1.8新特性
1.Lambda表达式:允许把函数作为一个方法的参数。
new Thread (()->System.out.printIn("abc")).start();
2.方法的引用
ArrayList<String> list = new ArrayList<>();
list.add("a");
list.add("b");
list.add("c");
list.forEach(System.out::printIn);
System.out::printIn 方法作为静态资源来引用
3.函数式接口
有且仅有一个抽象方法的接口叫做函数式接口,函数式接口可以被隐式转换为 Lambda 表达式。通常函数式接口 上会添加@FunctionalInterface 注解。
4.接口允许定义默认方法和静态方法
JDK8开始,允许接口中存在一个或者多个默认非抽象方法和静态方法
5.Stream API :
新添加的String API(Java.util.stream)把真正的函数式编程风格引入到Java中,这种风格将要处理的元素集
6.日期/时间类改进
JDK1.7之前的自带的日期处理类非常不方便,我们处理的时候经常是使用的第三方工具包,比如 commons-lang包等。
JDK1.8后出现之后这个改观了很多,比如日期时间的创建、比较、调整、格式化、时间间隔等,这些类都在 java.time 包下,LocalDate/LocalTime/LocalDateTime。
7.Optional类
Optional 类是一个可以为 null 的容器对象。如果值存在则 isPresent()方法会返回 true,调用 get()方法会返回该对象。
8.Java8 Base64 实现
Java8 内置Base64 编码的编码器和解码器。
----------------------------------------------------
ThreadLocal的原理
ThreadLocal:为共享变量在每个线程中创建一个副本,每个线程都可以访问自己内部的副本变量。通过threadlocal保证线程的安全性。
其实在ThreadLocal类中有一个静态内部类ThreadLocalMap(其类似于Map),用键值对的形式存储每一个线程的变量副本,ThreadLocalMap中元素的key为当前ThreadLocal对象,而value对应线程的变量副本。
ThreadLocal 本身并不存储值,它只是作为一个 key保存到ThreadLocalMap中,但是这里要注意的是它作为一个key用的是弱引用,因为没有强引用链,弱引用在GC的时候可能会被回收。这样就会在ThreadLocalMap中存在一些key为null的键值对(Entry)。因为key变成null了,我们是没法访问这些Entry的,但是这些Entry本身是不会被清除的。如果没有手动删除对应key就会导致这块内存即不会回收也无法访问,也就是内存泄漏。
使用完ThreadLocal之后,记得调用remove方法。 在不使用线程池的前提下,即使不调用remove方法,线程的"变量副本"也会被gc回收,即不会造成内存泄漏的情况。
-------------------------------------------------
关于锁:
synchronized 底层实现原理?
synchronized可以保证方法或者代码块在运行时,同一时刻只有一个方法可以进入到临界区,同时它还可以保证共享变量的内存可见性。
Java中每一个对象都可以作为锁,这是synchronized实现同步的基础:
普通同步方法 | 锁是当前实例对象 |
静态同步方法 | 锁是当前类的.class对象 |
同步方法块 | 锁是括号里的对象 |
synchronized和 volatile区别?
synchroized | volatile |
synchronized则是锁定当前变量,只有当前线程可以访问该变量,其他线程被阻塞住 | volatile本质是在告诉jvm当前变量在寄存器(工作内存)中的值是不确定的,需要从主存中读取 |
synchronized则可以使用在变量、方法、和类级别的 | volatile仅能使用在变量级别 |
volatile仅能实现变量的修改可见性,不能保证原子性 | synchronized则可以保证变量的修改可见性和原子性。 |
synchronized可能会造成线程的阻塞 | volatile不会造成线程的阻塞 |
synchronized标记的变量可以被编译器优化。 | volatile标记的变量不会被编译器优化 |
synchronized 和 Lock 有什么区别?
synchronized是java内置关键字,在jvm层面,Lock是个java类; synchronized无法判断是否获取锁的状态,Lock可以判断是否获取到锁;
synchronized会自动释放锁(a 线程执行完同步代码会释放锁 ;b 线程执行过程中发生异常会释放锁),Lock需在finally中手工释放锁(unlock()方法释放锁),否则容易造成线程死锁;
用synchronized关键字的两个线程1和线程2,如果当前线程1获得锁,线程2线程等待。如果线程1阻塞,线程2则会一直等待下去,而Lock锁就不一定会等待下去,如果尝试获取不到锁,线程可以不用一直等待就结束了;
synchronized的锁可重入、不可中断、非公平,而Lock锁可重入、可判断、可公平(两者皆可)
Lock锁适合大量同步的代码的同步问题,synchronized锁适合代码少量的同步问题。
同步锁、死锁、乐观锁、悲观锁?
同步锁 | 当多个线程同时访问同一个数据时,很容易出现问题。为了避免这种情况出现,我们要保证线程同步互斥,就是指并发执行的多个线程,在同一时间内只允许一个线程访问共享数据。Java 中可以使用 synchronized 关键字来取得一个对象的同步锁 |
死锁 | 就是多个线程同时被阻塞,它们中的一个或者全部都在等待某个资源被释放。 |
乐观锁 | 总是假设最好的情况,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号机制和CAS算法实现。乐观锁适用于多读的应用类型,这样可以提高吞吐量,像数据库提供的类似于write_conditio机制,其实都是提供的乐观锁。在Java中java.util.concurrent.atomic包下面的原子变量类就是使用了乐观锁的一种实现方式CAS实现的。 |
悲观锁 | 总是假设最坏的情况,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会阻塞直到它拿到锁(共享资源每次只给一个线程使用,其它线程阻塞,用完后再把资源转让给其它线程)。传统的关系型数据库里边就用到了很多这种锁机制,比如行锁,表锁等,读锁,写锁等,都是在做操作之前先上锁。Java中synchronized和ReentrantLock等独占锁就是悲观锁思想的实现 |
------------------------------------------------
码流 / 码率
码流(Data Rate)是指视频文件在单位时间内使用的数据流量,也叫码率或码流率,通俗一点的理解就是取样率,是视频编码中画面质量控制中最重要的部分,一般我们用的单位是kb/s或者Mb/s。一般来说同样分辨率下,视频文件的码流越大,压缩比就越小,画面质量就越高。码流越大,说明单位时间内取样率越大,数据流,精度就越高,处理出来的文件就越接近原始文件,图像质量越好,画质越清晰,要求播放设备的解码能力也越高。
当然,码流越大,文件体积也越大,其计算公式是文件体积=时间X码率/8。例如,网络上常见的一部90分钟1Mbps码流的720P RMVB文件,其体积就=5400秒×1Mb/8=675MB。
通常来说,一个视频文件包括了画面及声音,例如一个RMVB的视频文件,里面包含了视频信息和音频信息,音频及视频都有各自不同的采样方式和比特率,也就是说,同一个视频文件音频和视频的比特率并不是一样的。而我们所说的一个视频文件码流率大小,一般是指视频文件中音频及视频信息码流率的总和。
以以国内最流行,大家最熟悉的RMVB视频文件为例,RMVB中的VB,指的是VBR,即Variable Bit Rate的缩写,中文含义是可变比特率,它表示RMVB采用的是动态编码的方式,把较高的采样率用于复杂的动态画面(歌舞、飞车、战争、动作等),而把较低的采样率用于静态画面,合理利用资源,达到画质与体积可兼得的效果
Spring boot启动流程
maven作用:在每一台机器上面建立一个本地仓库,通过镜像仓库远程调运jar包,把项目中依赖的的jar包作统一管理,用坐标做唯一标识,减少系统冗余,方便jar包。
时间戳:一个能表示一份数据在某个特定时间之前已经存在的、 完整的、 可验证的数据,通常是一个字符序列,唯一地标识某一刻的时间,说白了就是,表示某一刻的时间;
作用:时间戳主要用于清理缓存,大多数用于版本更新