一、数据类型
- 基本数据类型
- String
三、运算符
- 算术运算符(+、-、*、/)
- 关系运算符(==、!=、>、<、>=、<=)
- 逻辑运算符(&&、||、!)
- 位运算符(&、|、~、^)
- 移位运算符(<<、>>、>>>)
- 条件运算符/三目运算符(表达式 1 ? 表达式2 : 表达式3)
四、逻辑控制
- 顺序结构
- 分支结构(if,switch)
- 循环结构(while、for、foreach、do while)
五、重载与重写
六、抽象类和接口
- super 和 this
- 抽象类和接口的异同
相同
- 抽象类和接口都不能实例化
不同
- 构造方法:抽象类可以有构造方法;接口没有
- 成员方法:抽象类可以是抽象的,也可以是非抽象的;(jdk1.8 之后)接口成员方法可以是 default 或者 static 开头
- 成员变量:抽象类可以是变量也可以是常量;接口只能是常量,默认修饰符是 public static final
七、进程与线程
- 并行与并发
并行: 例如一台电脑有 8 个 cpu,可以同时执行 8 个程序
并发: 从微观角度看,并发是串行的,执行完当前进程,再执行下一个进程;从宏观角度看,并发是并行的,因为每个进程执行的时间都很快,可以理解为并行。
虽然并行与并发概念并不一样,但是一般都用并发代指并行+并发
-
用户态和内核态
内核态:操作系统内核执行任务,一旦某个任务进入内核态执行,就变得不可控,往往意味着比较低效;
用户态:应用程序执行任务 -
进程与线程的区别
a. 进程包含线程,一个进程可以有多个线程
b. 进程是资源分配的基本单位,线程是系统调度的基本单位。也就是说,进程得到一部分资源后,一个或者多个线程共享这部分资源
c. 进程与进程之间是相互独立的,一个进程挂掉之后不会影响另外一个进程;但是线程不一样,线程共享进程申请的资源,如果一个线程挂了,可能影响另外一个线程,以至于危害到整个进程
d. 进程是资源分配的最小单位,线程是调度执行的最小单位。 -
创建线程的方式
a. 继承 Thread 类重写 run 方法
b. 实现 Runnable 接口重写 run 方法,实例化实现接口的类,然后将引用传递给 Thread 实例化对象
Runnable r2 = new Create2();
Thread c2 = new Thread(r2);
c2.start();
c. 匿名内部类,实际上还是继承 Thread 类
Thread c3 = new Thread() {
@Override
public void run() {
System.out.println("匿名内部类创建");
}
};
c3.start();
d. lambda 表达式创建
Thread c4 = new Thread(() ->
System.out.println("lambda 表达式创建"));
c4.start();
e. Callable 接口创建
class Create5 {
public static void main(String[] args) throws ExecutionException, InterruptedException {
// Callable 泛型参数与下面返回值的类型相同
Callable<Object> callable = new Callable<Object>() {
@Override
public Integer call() throws Exception {
// doSomething
System.out.println("Callable 创建线程");
return null;
}
};
// 使用 FutureTask 把 Callable 包裹起来, 传 task 对象给 Thread
// FutureTask 起到的作用就是 "未来能获取到一个结果"
// 也就是 Callable 重写方法中执行的结果
FutureTask<Object> task = new FutureTask<>(callable);
Thread thread = new Thread(task);
thread.start();
// 通过 FutureTask 的 get 方法获取到结果
// 如果线程阻塞, 对应的线程没有执行完, get 就等待阻塞
Integer res = (Integer) task.get();
}
}
-
Runnable 和 Callable 的区别
a. Runnable 和 Callable 中都只有一个重写方法,Runnable 是 run 方法,Callable 是 call 方法
b. run 方法没有返回值,不抛异常;call 方法有返回值,抛异常
c. 使用 Callable 创建线程需要使用 FutureTask 类来接受返回结果,Runnable 不用 -
Thread 和 Runnable 的区别
本质上没有区别
a.Runnable 和 Thread 创建线程的方式不一样
b.Runnable 是接口,Thread 是实体类,Thread 实现了 Runnable 接口
c. 如果是复杂的工作,使用 Thread,简单的使用 Runnable -
wait 和 sleep 的区别
a. wait 使用之前需要请求锁,也就是等待锁释放;sleep 无视锁的状态
b. wait 是 Object 的方法;sleep 是 Thread 的静态方法 -
线程安全
首先我们要了解线程不安全的根本原因是线程的抢占式执行
保证线程安全的方法
- 保证原子性
- 内存可见性
- 禁止指令重排序
synchronized 能保证线程安全
而 volatile 不能保证原子性
- 锁的设计
- 乐观锁和悲观锁
- 读写锁
- 轻量级锁(自旋锁)和重量级锁
- 公平锁与非公平锁
- 可重入锁和不可重入锁
synchronized :非公平锁、可重入锁、即是悲观锁也是乐观锁
- 死锁
产生死锁的原因
- 互斥条件:一个资源每次只能被一个进程使用
- 请求与保存条件:一个进程因请求支援而阻塞时,对已得到的资源不释放
- 不剥夺条件:进程已获得的资源,在使用完之前,不能强行剥夺
- 循环等待条件:若干个进程之间形成一种头尾相接的循环等待资源关系
解决的办法
- 不要再加锁的代码中尝试获取其他锁
- 约定顺序加锁
-
CAS 与自旋锁
-
JUC (Java.util.concurrent)
九、JVM
- 什么是 JVM
- JDK、JRE、JVM
- JVM 内存划分
- JVM 的作用
- 双亲委派模型
- 垃圾回收
- 回收算法