《码出高效》读书笔记

面向对象

接口

1.接口方法默认public abstract。
接口属性访问控制符默认public statistatic final。

2.接口支持多重继承,抽象类只能单继承。

3.接口可以继承接口。

方法

1.方法参数必须做校验。比如判空。

2.构造方法不能被继承,不能被重写。

3.getter和setter中不要添加业务逻辑。

否则程序出了问题,非常难排查。

如果变量是 boolean 类型,那么 getter 方法可以命名为 isXxx()。

4.异步用于处理耗时操作,处理完毕还可以返回处理结果。

5.在pojo中,属性变量最好设置成包装类,这样在调用时没有经过setter,直接getter会报错空指针,便于发现问题。

public class User {
private Integer age;
private Boolean vip;
//getter、setter..
}

如果age设置成int,当调用时没有先setter,直接getter,会得到默认值0,但是不会报错。

6.构造方法不可以被继承。

7.static代码块只运行一次。

static

0.静态方法如果使用可修改的对象,那在并发时会存在线程安全的问题。

1.阿里巴巴java规范:SimpleDateFormat是线程不安全的,不要定义为static变量。如果定义为static变量,必须加锁。

声明SimpleDateFormat的时候,如果使用的是static定义的,那么这个SimpleDateFormat就是一个共享变量,随之,SimpleDateFormat中的calendar也就可以被多个线程访问到,就会出现线程安全问题。

2.静态代码块只运行一次,在第二次对象实例化时,不会运行。

代码风格

1.魔法值,也就是直接以具体的数值或字符出现在代码中,随处可见,不易管理。魔法值应该改成常量。

2.方法控制在80行内。

3.if else超过三层以上,使用卫语句或状态模式。

4.在if判断中,过长的表达式直接改为一个布尔类型变量表示。

异常

1.受检异常(check)和非受检异常(uncheck)。还有运行时异常(RuntimException)

2.Error属于严重错误,比如OOM,StackOverflowError。

2.受检异常可以通过try和catch捕获。运行时异常是编程错误引起的,需要程序员自己处理。

并发与多线程

线程

1.线程是CPU调度和分派的基本单位,为了更充分地利用CPU资源,一般都会使用多线程进行处理。

2.线程可以拥有自己的操作栈,程序计数器,局部变量表等资源,它与同一进程内的其他线程共享该进程的所有资源。

3线程的生命周期分为NEW(新建状态),RUNNABLLE(就绪状态),RUNNING(运行状态),BLOCKING(阻塞状态),DEAD(销毁状态)。

线程安全

保证高并发场景下的线程安全,可以从以下维度考量:

1.数据单线程内可见。

通过限制数据仅在单线程内可见,可以避免数据被其他线程篡改。最典型的就是线程局部产量,它存储在独立虚拟机栈的局部变量列表,与其他线程毫无瓜葛。ThreadLocal就是用这种方式保证线程安全的。

2.只读对象。

只读对象是线程安全的。一个对象想要拒绝任何写入,必须满足:final修饰,避免被继承;没有任何更新方法;返回值不能为可变对象。

3.线程安全类。比如StringBuffer。

4.同步与锁机制。

线程安全的核心是“要么只读,要么加锁”。

Lock

Lock利用了volatile的可见性。

ReentrantLock对于Lock接口的实现主要依赖了Sync,而Sync继承了AbstractQueuedSynchronizer(AQS),它是JUC包实现同步的基础工具。在AQS中,定义了一个volatile int state 变量作为共享资源,如果线程获取资源失败,则进入同步FIFO队列中等待,如果成功获取资源就执行临界区代码。执行完释放资源时,会通知同步队列中的等待线程来获取资源后出列执行。

继承AQS后,可以使用acquire(),release()方法获取、释放permit。

Synchronized

Synchronized的底层是通过监视锁(monitor)实现的。监视锁是每个对象都有的隐藏字段。如果线程进入同步方法或代码块时,会获取该方法或代码块所属对象的monitor,进行加锁判断。

如果monitor为0,表示线程可以持有代码,monitor加一。如果当前线程已经持有monitor,则monitor继续加一。

volatile

解决的是多线程共享变量的可见性问题。但不具备Synchronied的互斥性。
也不具有原子性。

进行非原子操作,不具备线程安全。

volatile适合一写多读的并发场景。

数据结构与算法

1.ArrayList,HashMap这些集合在初始化时,如果要放入的数据量过大,最好指定初始容量,避免多次扩容。

2.HashMap 在容量不够进行 resize 时由于高并发可能出现死链,导致 CPU 飙升 。HashMap扩容后死链是如何形成的?

疑问:

1.双重检查锁如何理解?

2.操作数栈是什么?

原文地址:https://www.cnblogs.com/expiator/p/11349407.html