1、继承Thread类,重写run()方法,new一个实例对象,调用start()方法;
2、实现Runnable接口,重写run()方法,new一个实例对象,作为Thread的target来创建;
3、实现Callable接口
初生、就绪、运行、阻塞、消亡
重载是对类中同名方法采取的操作,通过改变形参数量、类型、顺序,使得同一方法可以有不同的传参形式
重写是子类对父类中同名方法的替换,返回值,方法名,参数类型必须一致,可以实现多态。
重写遵循"两同两小一大"
方法名、形参相同
返回值、抛出异常更小
权限修饰符更大
封装:限制用户对类的访问,只暴露必要的接口给用户,可以减少用户的误操作,方便开发者对内部细节的调整和迭代
继承:实现对类的拓展,减少重复代码。私有属性和方法子类无法访问
多态:实现接口或重写父类方法,程序运行时才决定实际调用的方法
String底层是用final修饰的数组,视为常量,不可变
StringBuffer底层的方法用sychonized锁,线程安全
StringBuild线程不安全
抽象类更像模板,可以有非抽象方法
接口更像行为规范,只能有抽象方法和static final变量
基本数据类型比较值,引用数据类型比较内存地址
equals未重写,与==相同,重写后一般比较对象内的值
重写equals必须重写hashcode 否则类的两个实例对象一定不会相等
只能修饰变量,不能修饰类和方法,阻止变量序列化
arrayList线程不安全,效率高
Vector线程安全,效率低
HashMap线程不安全效率高
HashTable线程安全效率低
HashMap支持nullkey nullvalue HashTable会报异常
HashMap的大小为2的幂次方,每次扩容翻倍,底层是数组+链表+红黑树,链表长度大于8时,如果数组长度大于64,链表会转换成红黑树,否则进行数组扩容
计算HashCode,不同直接加入,相同进行equals比较
JDK1.7 使用segment,分段锁,操作数据先获取对应的segment锁,效率比HashTable全表锁高
JDK1.8 数据结构和HashMap一致,改为synchronized,只锁链表或红黑树的首节点,效率进一步提高
线程是轻量级进程,线程共享统一进程的堆和方法区,每个线程有自己的程序计数器、本地方法栈、虚拟机栈
调用start方法线程进入就绪状态,等待cpu分配时间片,是真正的多线程
调用run方法会直接执行该方法,还是在当前线程
第一次判断null是为了快速返回单例对象,就像先比较hash在进行equals
第二次判断null + synchronized 是为了确保单例
volatile保证了有序性,防止指令重排
因为 singleton = new Singleton() 这句话可以分为三步:
1. 为 singleton 分配内存空间;
2. 初始化 singleton;
3. 将 singleton 指向分配的内存空间。
但是由于JVM具有指令重排的特性,执行顺序有可能变成 1-3-2。 指令重排在单线程下不会出现问题,但是在多线程下会导致一个线程获得一个未初始化的实例。例如:线程T1执行了1和3,此时T2调用 getInstance() 后发现 singleton 不为空,因此返回 singleton, 但是此时的 singleton 还没有被初始化。
使用 volatile 会禁止JVM指令重排,从而保证在多线程下也能正常执行。
线程的创建和销毁都需要消耗资源,比如内存空间,时间等
线程池可以重复利用已创建的线程,减少资源消耗
线程不需要创建,拿来即用,提高响应速度
线程池管理线程,保证线程不滥用,避免线程创建过多
Runnable不返回结果和抛出异常,更加简洁