集合
Map接口和Collection接口是所有集合框架的父接口
- Collection接口的子接口包括Set接口和List接口
- Map接口的实现主要有HashMap、Hashtable、ConcurrentHashMap、Properties等
- Set接口的实现类主要有HashSet、TreeSet、LinkedHashSet等
- List接口的实现类主要有ArrayList、LinkedList、Stack和Vector等
各集合类的特点
- List集合:有序容器(存入和取出的元素顺序相同),元素可以重复,可以插入多个null,元素都有索引
- Set集合:无序容器(存入和取出元素顺序可能不一致),不可以存储重复元素,只允许存储一个null元素,必须保证元素的唯一性
- Map:是键值对集合。key无序,唯一。value不要求有序,允许重复
集合框架底层数据结构
- List
- ArrayList:Object数组,非线程安全
- Vector:Object数组,线程安全
- LinkedList:双向循环链表,非线程安全
- Set
- HashSet(无序、唯一):基于HashMap实现,采用HashMap保存元素,非线程安全
- LinkedHashSet:继承HashSet,基于LinkedHashMap实现,非线程安全,可以有null值
- TreeSet(有序、唯一):红黑树(自平衡的排序二叉树)
- Map
- HashMap:无序且key可以为null,非线程安全,由数组+链表+红黑树实现
- Hashtale:无序,key不能为null,线程安全,由数组+链表实现
- LinkedHashMap:有序且key可以为null,非线程安全,由数组+链表+红黑树+双重链接列表实现
- TreeMap:有序但key不能为null,非线程安全,由红黑树实现
如何实现一个IOC容器
- 写一个配置文件,配置文件中指定需要扫描的包路径
- 定义一些表示访问控制层,服务层,数据持久层以及依赖注入和获取配置文件的注解
- 从配置文件中获取需要扫描的包路径,将当前路径下所有以.class结尾的文件添加到一个set集合中进行存储
- 遍历这个集合,获取在类上有指定注解的类,并将其交给ioc容器,定义一个安全的map用来存储这些对象
- 遍历这个ioc容器,获取到每一个类的实例,判断里面是否有依赖其他类的实例,然后进行递归注入
什么是多态机制,是如何实现的
多态指的是程序中定义的引用变量所指向的具体类型和通过该引用变量发出的方法调用在编程时并不确定,只有当程序运行时才能确定。
- 编译时多态:静态的,主要指方法重载,在运行时谈不上多态
- 运行时多态:动态的,通过动态绑定实现,是我们所说的多态性
Java实现多态有三个必要条件:继承、重写和向上转型
面向对象的五大基本原则
- 单一职责原则:类的功能要单一
- 开放封闭原则:模块对于扩展是开放,对于修改是封闭
- 里氏替换原则:子类可以替换父类出现在父类能出现的任何地方
- 依赖倒置原则:高层次模块不应该依赖低层次的模块,应该依赖于抽象,抽象不依赖于具体实现,具体实现依赖于抽象
- 接口分离原则:将通用接口拆分成几个特定功能的接口
IO
- 字节输入流InputStream
- 字符输入流Reader
- 字节输出流OutputStream
- 字符输出流Writer
BIO、NIO、AIO
- BIO是同步阻塞式IO,简单方便,但并发处理能力低
- NIO是同步非阻塞式IO,客户端和服务端通过通道通讯,是实现多路复用的基础
- AIO是异步非阻塞式IO,也叫NIO2,该操作基于事件和回调机制
反射
**定义:**在运行过程中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能调用这个对象的任意一个属性和方法;这种动态获取信息以及动态调用对象的方法的功能就是Java的反射机制
优缺点:
- 优点是运行期类型的判断,动态加载类,提高代码的灵活性
- 缺点是会出现性能瓶颈:反射相当于一系列解释操作,通知JVM要做的事,性能比直接使用Java代码要慢很多
反射机制的应用场景
反射是框架设计的灵魂
- 使用jdbc连接数据库时使用的Class.forName()通过反射加载数据库的驱动程序
- Spring通过xml配置模式装载Bean也是通过反射完成
获取反射的三种方法
- 通过new对象实现反射机制
- 通过路径实现反射机制
- 通过类名实现反射机制
1 public class Student {
2 private int id;
3 String name;
4 protected boolean sex;
5 public float score;
6 }
1 public class Get {
2 //获取反射机制三种方式
3 public static void main(String[] args) throws ClassNotFoundException {
4 //方式一(通过建立对象)
5 Student stu = new Student();
6 Class classobj1 = stu.getClass();
7 System.out.println(classobj1.getName());
8 //方式二(所在通过路径-相对路径)
9 Class classobj2 = Class.forName("fanshe.Student");
10 System.out.println(classobj2.getName());
11 //方式三(通过类名)
12 Class classobj3 = Student.class;
13 System.out.println(classobj3.getName());
14 }
15 }
线程
创建线程的5种方式
- 继承于Thread类
- 实现Runnable接口
- 实现Callable接口
- 使用线程池
- 使用匿名类
package atguigu.java;
//1.创建一个继承于Thread类的子类(其他方式也大差不差的,先继承或者实现,重写run(Callable是实现call())方法,然后new这个对象后start)
class MyThread extends Thread {
//2.重写Thread类的run()
@Override
public void run() {
for (int i = 0; i < 100; i++) {
if (i % 2 == 0) {
System.out.println(Thread.currentThread().getName() + ":" + i);
}
}
}
}
public class ThreadTest {
public static void main(String[] args) {
MyThread t1 = new MyThread();
t1.start();
}
}
package com.atguigu.java2;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
//使用线程池来创建线程
class NumberThread implements Runnable {
@Override
public void run() {
//遍历100以内的偶数
for (int i = 0; i <= 100; i++) {
if (i % 2 == 0) {
System.out.println(Thread.currentThread().getName() + ": " + i);
}
}
}
}
class NumberThread1 implements Runnable {
@Override
public void run() {
//遍历100以内的奇数
for (int i = 0; i <= 100; i++) {
if (i % 2 != 0) {
System.out.println(Thread.currentThread().getName() + ": " + i);
}
}
}
}
public class ThreadPool {
public static void main(String[] args) {
//1. 提供指定线程数量的线程池
ExecutorService service = Executors.newFixedThreadPool(10);
//输出class java.util.concurrent.ThreadPoolExecutor
System.out.println(service.getClass());
ThreadPoolExecutor service1 = (ThreadPoolExecutor) service;
//2. 执行指定的线程的操作。需要提供实现Runnable接口或Callable接口实现类的对象
service.execute(new NumberThread());//适用于Runnable
service.execute(new NumberThread1());//适用于Runnable
//3. 关闭连接池
service.shutdown();
}
}
//使用匿名内部类
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
// 线程需要执行的任务代码
System.out.println("子线程开始启动....");
for (int i = 0; i < 30; i++) {
System.out.println("run i:" + i);
}
}
});
thread.start();
线程池的好处
- 降低资源消耗。通过重复利用已创建的线程降低线程的创建和销毁造成的消耗
- 提高响应速度。但任务到达时,任务可以不用等待线程创建就能立即执行
- 提高线程的可管理性。使用线程池可以统一进行分配、调优和监控
线程池的实现原理
- 当提交一个任务到线程池中时,先判断线程池里的核心线程是否都在执行任务,如果不是,则创建一个新的工作线程来执行任务。如果核心线程都在执行任务,则进入下一个流程
- 线程池判断工作队列是否已满,如果没满,将新任务存储在这个工作队列中。如果已满,进入下一个流程
- 判断线程池中的线程是否都处于工作状态,如果没有,则创建一个新的工作线程来执行任务。如果已满,则交给饱和策略来处理这个任务
executor框架的两级调度模型
在HotSpot VM模型中,Java线程被一对一映射为本地操作系统线程,Java线程启动时会创建本地操作系统线程,当Java线程终止时,对应的操作系统线程也会被销毁回收,而操作系统会调度所有线程并将他们分配给可以用的cpu。在上层,Java程序会将应用分解为多个任务,然后使用应用级调度器将这些任务映射成固定数量的线程。在底层,操作系统内核会将这些线程映射到硬件处理器上
Executors可以创建3种类型的线程池
-
SingleThreadExecutor:单线程线程池。核心线程数和线程数都是1,工作队列是无界队列。单线程工作,只能一个个任务执行
//创建方式 ExecutorService threadPool = Executors.newSingleThreadExecutor(); //源码 public static ExecutorService newSingleThreadExecutor() { return new FinalizableDelegatedExecutorService (new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>())); }
-
FixedThreadExecutor:固定大小线程池。与单线程类似,只是创建了固定大小的线程数量
ExecutorService threadPool = Executors.newFixedThreadPool(5); public static ExecutorService newFixedThreadPool(int nThreads) { return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>()); }
-
CachedThreadPool:无界线程池。无界线程池意味着没有工作队列,任务进来就执行。与前两个的区别是,空闲的线程会被回收掉,空闲时间是60s。适用于执行短期异步小程序或者负载较轻的服务器
ExecutorService threadPool = Executors.newCachedThreadPool(); public static ExecutorService newCachedThreadPool() { return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>()); }
创建线程池的几种方式
- ThreadPoolExecutor
- ThreadScheduledExecutor
- ForkJoinPool
线程什么时候会出现僵死
僵死进程是指子进程退出时,父进程未对其发出的sigchld信号进行适当处理,导致子进程在僵死状态等待父进程为其收尸
什么是线程安全,如何实现
线程执行过程中不会产生共享资源的冲突,则线程安全
如果有多个线程同时在操作主内存中的变量,则线程不安全
实现方式:
- 互斥同步锁(悲观锁,也叫阻塞同步锁):是可重入锁,好处是不会锁死,但因为涉及到核心态和用户态的切换,比较耗性能
- 非阻塞同步(乐观锁):原子类(CAS),会先进行资源更新根据与主存中的旧值对别确定是否被其他线程更新过,如果比较结果相同,就将新值写回内存,否则一直试到成功,这是不可重入的锁,否则会被锁死
- 无同步方案:
- 可重入代码,在执行的任何时刻都能中断,重入执行而不会产生冲突。特点是不依赖堆上的共享资源
- ThreadLocal:线程本地的变量,每个线程获取一份共享线程的拷贝,单独处理
- 线程本地存储:如果一个共享资源一定要被多个线程共享,可尽量让一个线程完成所有处理操作