Java 基础知识
1.多线程
1.1 创建线程的方法有那些
java所有的线程对象都必须是Thread类或其子类的实例 | |
---|---|
继承Thread类创建线程 | 创建Thread子类的实例 |
实现Runnable接口创建线程 | 通过调用线程实例对象的start()方法来启动线程 |
使用Callable和Future创建线程 | call()方法可以有返回值、可以声明抛出异常 |
使用线程池例如用Executor框架 |
class MyThread1 extends Thread{ @Override public void run() { super.run(); } } class MyThread2 implements Runnable { //实现Runnable接口 @Override public void run() { } } class MyThread3 implements Callable { //实现Runnable接口 @Override public Object call() throws Exception { return null; } } public static void main(String[] args) { //Future创建线程 FutureTask
<object>task = new FutureTask<>((Callable
<object>)()->{return new Object();}); FutureTask
<object>task1 = new FutureTask<>(Object::new); Object o = null; try { o = task.get(); //返回Callable里方法的返回值,调用这个方***导致程序阻塞,必须等到子线程结束后才会得到返回值 task.isDone(); //若Callable任务完成,返回True task.isCancelled(); //如果在Callable任务正常完成前被取消,返回True } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } System.out.println(o); } public static void main(String[] args) { //使用线程池例 ExecutorService poolExecutor = Executors.newFixedThreadPool(10); ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor(); ExecutorService cachedThreadPool = Executors.newCachedThreadPool(); ExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(10); }
</object></object></object>
线程池 | |
---|---|
newCachedThreadPool() | -缓存型池子,先查看池中有没有以前建立的线程,如果有,就 reuse.如果没有,就建一个新的线程加入池中 -缓存型池子通常用于执行一些生存期很短的异步型任务 因此在一些面向连接的daemon型SERVER中用得不多。但对于生存期短的异步任务,它是Executor的首选。 -能reuse的线程,必须是timeout IDLE内的池中线程,缺省 timeout是60s,超过这个IDLE时长,线程实例将被终止及移出池。 注意,放入CachedThreadPool的线程不必担心其结束,超过TIMEOUT不活动,其会自动被终止。 |
newFixedThreadPool(int) | -newFixedThreadPool与cacheThreadPool差不多,也是能reuse就用,但不能随时建新的线程 -其独特之处:任意时间点,最多只能有固定数目的活动线程存在,此时如果有新的线程要建立,只能放在另外的队列中等待,直到当前的线程中某个线程终止直接被移出池子 -和cacheThreadPool不同,FixedThreadPool没有IDLE机制(可能也有,但既然文档没提,肯定非常长,类似依赖上层的TCP或UDP IDLE机制之类的),所以FixedThreadPool多数针对一些很稳定很固定的正规并发线程,多用于服务器 -从方法的源代码看,cache池和fixed 池调用的是同一个底层 池,只不过参数不同: fixed池线程数固定,并且是0秒IDLE(无IDLE) cache池线程数支持0-Integer.MAX_VALUE(显然完全没考虑主机的资源承受能力),60秒IDLE |
newScheduledThreadPool(int) | -调度型线程池 -这个池子里的线程可以按schedule依次delay执行,或周期执行 |
SingleThreadExecutor() | -单例线程,任意时间池中只能有一个线程 -用的是和cache池和fixed池相同的底层池,但线程数目是1-1,0秒IDLE(无IDLE) |
1.2 线程池的原理
1.2.1 线程池工作原理:
1.2.2 阻塞队列
生产者 与 消费者模式
1.2.3 饱和拒绝策略
ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出 RejectedExecutionException异常。 默认
ThreadPoolExecutor.DiscardPolicy:丢弃任务,但是不抛出异常。
ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,然后重新提交被拒绝的任务
ThreadPoolExecutor.CallerRunsPolicy:由调用线程(提交任务的线程)处理该任务
1.3 线程生命周期
2 锁
CAS compareAndSwap
- ABA 问题
- 循环时间、CPU开销大
- 一个共享变量原子操作
AQS
AQS 定义了两种资源共享方式:
Exclusive:独占,只有一个线程能执行,如ReentrantLock
Share:共享,多个线程可以同时执行,如Semaphore、CountDownLatch、ReadWriteLock,CyclicBarrier
+------+ prev +-----+ +-----+ head | | <---- | | <---- | | tail //双向的链式队列 +------+ +-----+ +-----+ volatile int state; 共享变量<summary style="color:#F00">AQS 的一个实现类 ReentrantLock 独占锁</summary>
final void lock() {
if (compareAndSetState(0, 1)) // 首先尝试获取锁
setExclusiveOwnerThread(Thread.currentThread()); //设置当前线程为执行线程
else
acquire(1); //其他方式获取锁
}
public final void acquire(int arg) {
// 再次尝试获取锁 成功则不用后续操作
// 失败则 addWaiter(Node.EXCLUSIVE),并且中断线程
if (!tryAcquire(arg) &&acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
// tryAcquire(arg) 非公平的实现
final boolean nonfairTryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
if (compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0) // overflow
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
private Node addWaiter(Node mode) {
//这个函数比较简单,就是将node放到队列末尾,mode表示是独占锁还是共享锁以后再讨论
Node node = new Node(Thread.currentThread(), mode);
// Try the fast path of enq; backup to full enq on failure
Node pred = tail;
if (pred != null) {//如果tail不是null,表示队列已被初始化则将node 放到队尾
node.prev = pred;
//cas将tail指向node,如果cas失败表示有多个线程都要放到队尾,这个时候会走到 enq函数,该还是会再去cas放到队尾
if (compareAndSetTail(pred, node)) {
pred.next = node;
return node;
}
}
enq(node); //初始化队列或者再次cas队尾
return node;
}
private Node enq(final Node node) {
//该函数会初始化队列(如果队列未被初始化)
for (;;) {
Node t = tail;
if (t == null) { // Must initialize 初始化队列
if (compareAndSetHead(new Node())) //将head设置为一个空node。这个空node很重要,aqs的队头一定是空节点,用来表示正在执行的那个线程,想一下当执行线程结束后只有这个空节点才能去唤醒下一个节点,假如队头节点就是等待线程,谁能去唤醒他呢
tail = head;
} else {
node.prev = t;
if (compareAndSetTail(t, node)) {//cas队尾,如果还失败看到这个是死循环会一直去放,直到放到队尾为止
t.next = node;
return t;
}
}
}
}
#### synchronized - 修饰实例方法,作用于当前实例加锁,进入同步代码前要获得当前实例的锁
- 静态方法,作用于当前类对象加锁,进入同步代码前要获得当前类对象的锁
- 修饰代码块,指定加锁对象,对给定对象加锁,进入同步代码库前要获得给定对象的锁。
3 集合
ArrayList object[] 数组、元素移动、扩容(1.5)、查找(折半)、遍历效率高(for最高)
LinkedList 双向链表、查找较慢(从前或后 ?size/2)
6 IO
BIO、NIO、AIO
多路复用器
常见的算法有三种:
select
是采用轮询的方式poll
由于其就绪队列由链表实现 与select几乎相同epoll
采用回调方式实现对内核进程状态的获取:一旦内核进程就绪,其就会回调epoll多路复用器,进入到多路复用器的就绪队列(由链表实现)。也称为epoll事件驱动模型
,使用mmap零拷贝机制,大大降低了系统开销。
DMA 直接内存地址
mmap零拷贝
7 JVM
7.1 jvm 内存结构
JVM详细架构图
JVM详细架构图
7.2 类的加载器
7.3 运行时数据区
7.4 垃圾回收算法
8 tomcat
8.1 tomcat 的模块架构
8.2 tomcat启动流程
8.3 tomcat 请求接收
8.4 tomcat 性能优化
以server模式启动
堆内存的分配 -Xms== -Xmx 堆内存的初始大小和最大大小相等,避免JVM运行期间重新调整堆内存的大小 (可用内存*80%)
垃圾收集器
9 23种设计模式
- 单例(Singleton)模式:某个类只能生成一个实例,该类提供了一个全局访问点供外部获取该实例,其拓展是有限多例模式。
- 原型(Prototype)模式:将一个对象作为原型,通过对其进行复制而克隆出多个和原型类似的新实例。
- 工厂方法(Factory Method)模式:定义一个用于创建产品的接口,由子类决定生产什么产品。
- 抽象工厂(AbstractFactory)模式:提供一个创建产品族的接口,其每个子类可以生产一系列相关的产品。
- 建造者(Builder)模式:将一个复杂对象分解成多个相对简单的部分,然后根据不同需要分别创建它们,最后构建成该复杂对象。
- 代理(Proxy)模式:为某对象提供一种代理以控制对该对象的访问。即客户端通过代理间接地访问该对象,从而限制、增强或修改该对象的一些特性。
- 适配器(Adapter)模式:将一个类的接口转换成客户希望的另外一个接口,使得原本由于接口不兼容而不能一起工作的那些类能一起工作。
- 桥接(Bridge)模式:将抽象与实现分离,使它们可以独立变化。它是用组合关系代替继承关系来实现,从而降低了抽象和实现这两个可变维度的耦合度。
- 装饰(Decorator)模式:动态的给对象增加一些职责,即增加其额外的功能。
- 外观(Facade)模式:为多个复杂的子系统提供一个一致的接口,使这些子系统更加容易被访问。
- 享元(Flyweight)模式:运用共享技术来有效地支持大量细粒度对象的复用。
- 组合(Composite)模式:将对象组合成树状层次结构,使用户对单个对象和组合对象具有一致的访问性。
- 模板方法(TemplateMethod)模式:定义一个操作中的算法骨架,而将算法的一些步骤延迟到子类中,使得子类可以不改变该算法结构的情况下重定义该算法的某些特定步骤。
- 策略(Strategy)模式:定义了一系列算法,并将每个算法封装起来,使它们可以相互替换,且算法的改变不会影响使用算法的客户。
- 命令(Command)模式:将一个请求封装为一个对象,使发出请求的责任和执行请求的责任分割开。
- 职责链(Chain of Responsibility)模式:把请求从链中的一个对象传到下一个对象,直到请求被响应为止。通过这种方式去除对象之间的耦合。
- 状态(State)模式:允许一个对象在其内部状态发生改变时改变其行为能力。
- 观察者(Observer)模式:多个对象间存在一对多关系,当一个对象发生改变时,把这种改变通知给其他多个对象,从而影响其他对象的行为。
- 中介者(Mediator)模式:定义一个中介对象来简化原有对象之间的交互关系,降低系统中对象间的耦合度,使原有对象之间不必相互了解。
- 迭代器(Iterator)模式:提供一种方法来顺序访问聚合对象中的一系列数据,而不暴露聚合对象的内部表示。
- 访问者(Visitor)模式:在不改变集合元素的前提下,为一个集合中的每个元素提供多种访问方式,即每个元素有多个访问者对象访问。
- 备忘录(Memento)模式:在不破坏封装性的前提下,获取并保存一个对象的内部状态,以便以后恢复它。
- 解释器(Interpreter)模式:提供如何定义语言的文法,以及对语言句子的解释方法,即解释器。