java面试题:


Git仓库的常用命令
1.git  clone  网络路径:把远程仓库的项目复制到本地仓库
2.git  status:查看本地仓库内容中的状态
3.git add *:添加到暂存区
4.git commit  -m  “提交的信息”


常用软件的端口号
Mysql:3306
Redis:6379
Tomcat:8080
Nacos:8848
Kibana:5601
Elasticsearch:9200
Nginx:80

HashMap的底层原理?
1.7 数组 + 链表
1.8 数组 + (链表 | 红黑树)


ArrayList 扩容规则
1. ArrayList() 会使用长度为零的数组,不指定长度的情况下
2. ArrayList(int initialCapacity) 可以指定容量的数组
3. add(Object o) 首次扩容为 10,再次扩容为上次容量的 1.5 倍
4. addAll(Collection c) 没有元素时,扩容为10, 实际元素个数,再次括容量 为原来的容量的1.5 倍
5.数据进行扩容,会将老数组中的元素重新拷贝一份到新的数组中,每次数组容量的增长大约是其原容量的1.5倍

LinkedList 对比 ArrayList 的区别?
相同:都是list接口的实现类

不同:
LinkedList
1. 基于链表,数据添加删除效率高,只需要改变指针指向即可
2. 查询数据的效率低,需要对链表进行遍历


 ArrayList
1. 基于数组,需要连续内存,查询快,增删慢
2. 往数组尾部添加元素的效率高,使用add的效率比linkedList慢

Threadlocal的作用?
1.可以实现【资源对象】的线程隔离,让每个线程各用各的【资源对象】,避免争用引发的线程安全问题
2.同时实现了线程内的资源共享

Threadlocal的原理?
每个线程内有一个 ThreadLocalMap 类型的成员变量,用来存储资源对象


Linux的常用命令?
cd  路径:进入到某个文件夹
pwd:显示当前所在的路径
docker logs  -f  容器名:查看当前容器的日志
tail -f 文件路径:查看文件的内容
vi 文件路径:编辑或修改文件
ps -ef |grep -a(某个软件名称):查看进程
Kill -9  进程编号:杀死进程
docker start 容器名:重启容器
rm -rf 文件路径:强制删除文件
docker volume prune :删除所有的数据卷
tail -f  文件路径:查看日志
docker rm  -f  容器名称:删除指定的容器   
docker ps -a :查看所有的容器包括没有启动的

rabbitMQ和elasticsearch、redis的区别
redis:遵守BSD协议是一个高性能的key-value数据库,轻量级,低延迟,高并发,低可靠性;

rabbitMQ:重量级,高可靠,异步,不保证实时性;rabbitMQ是一个专门的AMQP协议队列,他的优越性就在于提供可靠的队列服务,并且可做到异步,而redis主要用于缓存的,redis的发布订阅模块,可用于实现及时性,且可靠性低的功能。

ES的特点,正如其名,那就是搜索。严格的说,ES不是一个数据库,而是一个搜索引擎,ES的方方面面也都是围绕搜索设计的,最显著的特征有倒排索引,根据词条进行查询文档数据


stringbuffer和stringbuilder的区别
1.线程安全
StringBuffer:线程安全,StringBuilder:线程不安全。因为 StringBuffer 的所有公开方法都是 synchronized 修饰的,而 StringBuilder 并没有 synchronized 修饰。

2.缓冲区
StringBuffer 每次获取 toString 都会直接使用缓存区的 toStringCache 值来构造一个字符串。
StringBuilder 则每次都需要复制一次字符数组,再构造一个字符串。
所以, StringBuffer 对缓存区优化,不过 StringBuffer 的这个toString 方法仍然是同步的。

3.性能
既然 StringBuffer 是线程安全的,它的所有公开方法都是同步的,StringBuilder 是没有对方法加锁同步的,所以毫无疑问,StringBuilder 的性能要远大于 StringBuffer。


java反射和序列化区别
Java的反射机制是指在运行状态中,对于任意一个类都能够知道这个类所有的属性和方法;并且对于任意一个对象,都能够调用它的任意一个方法;这种动态获取信息以及动态调用对象方法的功能成为Java语言的反射机制

序列化: 序列化是将对象转换为容易传输的格式的过程,比如转化为二进制、xml、json从而在网络中传输,一般和反序列化搭配使用


Springboot的自动配置原理?
@springbootApplication注解包含三个注解:
1. @SpringBootConfiguration :实际上是一个配置类
2. @ComponentScan:扫描当前启动类所在的包,以及子包
3. @EnableAutoConfiguration:开启自动配置,就不需要把对象放入容器,直接注入对象就行


class.forname和classloader的区别
class.forName()前者除了将类的.class文件加载到jvm中之外,还会对类进行解释,执行类中的static块。而classLoader只干一件事情,就是将.class文件加载到jvm中,不会执行static中的内容,只有在newInstance才会去执行static块。


两个对象值相同 (x.equals(y) == true) ,但却可有不同的 hashCode,这句 
话对不对?
不对,如果两个对象 x 和 y 满足 x.equals(y) == true,它们的哈希码(hashCode)应当相同。 
Java 对于 eqauls 方法和 hashCode 方法是这样规定的:
1.如果两个对象相同(equals 方法返回 true),那 么它们的 hashCode 值一定要相同;
2.如果两个对象的 hashCode 相同,它们并不一定相同。当然,你未必要按照 要求去做,但是如果你违背了上述原则就会发现在使用容器时,相同的对象可以出现在 Set 集合中,同时增加新元素的效率会大大下降

抽象类(abstract class)和接口(interface)有什么异同?
抽象类:
1. 抽象类使用abstract修饰
2. 抽象类不能实例化,即不能使用new关键字来实例化对象
3. 含有抽象方法(使用abstract关键字修饰的方法)的类是抽象类,必须使用abstract关键字修饰
4. 抽象类可以含有抽象方法,也可以不包含抽象方法,抽象类中可以有具体的方法
5. 如果一个子类实现了父类(抽象类)的所有抽象方法,那么该子类可以不必是抽象类,否则就是抽象类
6. 抽象类中的抽象方法只有方法体,没有具体实现

接口:
1. 接口使用interface修饰;
2. 接口不能被实例化;
3. 一个类只能继承一个类,但是可以实现多个接口;
4. 接口中方法均为抽象方法 ;
5. 接口中不能包含实例域或静态方法(静态方法必须实现,接口中方法是抽象方法,不能实现)

区别
1. 抽象类可以提供成员方法的实现细节 ,而接口中没有;但是JDK1.8之后,在接口里面可以
定义default方法,default方法里面是可以具备方法体的。
2. 抽象类中的成员变量可以是 各种类型 的,而接口中的成员变量只能是 public static final 类型;
3. 接口中每一个方法也是隐式指定为 public abstract,不能含有静态代码块以及静态方法,而抽象类可以有静态代码块和静态方法
4. 一个类只能继承一个抽象类,而一个类却可以实现 多个接口。


 

HashMap和HashTable的区别?
1.HashMap采用了数组+链表的数据结构,能在查询和修改方便继承了数组的线性查找和链表的寻址修改
2.HashMap是非synchronized,所以HashMap比HashTable更快
3.HashMap可以接受null键和值,而Hashtable则不能 (原因就是equlas0方法需要对象,因为HashMap是后出的API经过处理才可以)
4.HashMap和HashTable都是用于存储键值对的数据结构
5.最主要的区别是HashMap是非线程安全的,而HashTable是线程安全的

HashMap的工作原理?
1.HashMap 的存储机制 
a. 在 Java 1.8 中,如果链表的长度 超过了 8 ,那么链表将转化为 红黑树链表长度低 于6,就把红黑树转回链表

2.Java 1.8 中 HashMap 的不同
a. 在 Java 1.8 中,如果链表的长度 超过了 8 ,那么链表将转化为 红黑树链表长度低 于6,就把红黑树转回链表
b. 发生 hash 碰撞时,Java 1.7会在链表头部插入,而 Java 1.8 会在链表尾部插入
c. 在 Java 1.8 中,Entry 被 Node 代替(换了一个马甲)

3.put过程 
a. 对Key求Hash值,然后再计算下标 
b. 如果没有碰撞,直接放入桶中(碰撞的意思是计算得到的Hash值相同,需要放到同一个 bucket中)
c.如果碰撞了,以链表的方式链接到后面 
d.如果链表长度超过阀值( TREEIFY THRESHOLD==8),就把链表转成红黑树,链表长度低于6,就把红黑树转回链表 
e. 如果节点已经存在就替换旧值(就是value的值如果存在,就会被前面的值覆盖) 
f. 如果桶满了(容量16*加载因子0.75),就需要 resize(扩容2倍后重排)

 4. get过程
a. 当我们调用get()方法,HashMap会使用键对象的hashCode找到bucket位置 
b. 找到bucket位置之后,会调用keys.equals()方法去找到链表中正确的节点
c. 最终找到要找的值对象

List、Set、Map三个接口的区别以及常见子类?
1. List、Set单列,Map是双列的键值对
2. List可重复,set不可重复
3. List有序的,set是无序
4. List中最常用的两个子类:ArrayList(基于数组,查询快)和LinkedList(基于链表,增删快)
5. Set中最常用的两个子类:HashSet和TreeSet
6. Map中最常用的两个子类:HashMap和TreeMap


TreeSet、HashSet、TreeMap的区别?
1.TreeSet、HashSet、TreeMap都是Java中的集合类,其中TreeSet和TreeMap是基于红黑树实现的,而HashSet是基于HashMap实现的。TreeSet和TreeMap是有序的,而HashSet是无序的。TreeSet和HashSet都是Set接口的实现类,而TreeMap是Map接口的实现类。


2.红黑树是一种自平衡二叉查找树,它能够保证在最坏情况下基本操作的时间复杂度为O(logn),HashMap和HashSet的底层实现都是数组+链表/红黑树,它们的时间复杂度也是O(1)或O(logn)

扩展问题:

什么是ConcurrentHashMap
ConcurrentHashMap是线程安全的哈希表,它支持高并发并且效率比Hashtable更高。它采用了锁分段技术,将整个Map分成了若干个Segment,每个Segment都是一个独立的哈希表,拥有自己的锁。这样,在多线程并发访问时,不同的线程可以同时访问不同的Segment,从而大大提高了并发访问的效率。同时,ConcurrentHashMap也支持高效的读操作,因为读操作不需要加锁,可以并发执行。


什么是LinkedHashMap
1.LinkedHashMap是Java中的一个类,它继承自HashMap类,它可以保持插入顺序,也可以保持访问顺序。
2.迭代LinkedHashMap时,它会按照元素插入的顺序返回元素,或者按照元素最近访问的顺序返回元素。
3.LinkedHashMap的实现是通过维护一个双向链表来实现的。


什么是CopyOnWriteArrayList

1.CopyOnWriteArrayList是Java中的一种并发容器,它是ArrayList的线程安全版本。
2.它通过在修改时创建副本来实现线程安全,因此不需要使用显式的同步


3.CopyOnWriteArrayList虽然是线程安全的,但是由于每次修改都需要创建副本,因此在频繁修改的情况下可能会影响性能,在选择使用它时需要权衡其线程安全性和性能


什么是ConcurrentModificationException异常
1.ConcurrentModificationException异常是Java中的一种异常,表示在迭代集合时
2.如果在迭代期间修改了集合,就会抛出此异常。这通常是由于多个线程同时修改集合而导致的
3.使用迭代器遍历集合时可以使用remove()方法删除元素,但是不建议在迭代期间修改集合,可能会导致ConcurrentModificationException异常的抛出

说下ConcurrentHashMap ?
1.在ConcurrentHashMap中,读操作和写操作都能保证很高的性能 

2. 在进行读操作时(几乎)不需要加锁,而在写操作时通过锁分段技术只对所操作的段加锁而不影响客户端对其它段的访问

3. 在理想状态下,ConcurrentHashMap 可支持 16个线程执行并发写操作,及任意数量线程的读操作

4. 关于它的存储结构

 a. JDK 1.7 中使用分段锁(ReentrantLock + Segment + HashEntry),相当于把一个 HashMap 分成多个段,每段分配一把锁,这样支持多线程访问。
锁粒度:基于 Segment ,包含多个 HashEntry 

b. JDK 1.8 中使用 CAS + synchronized + Node + 红黑树。
锁粒度: Node(首结 点) (实现 Map.Entry<K,V>),锁粒度降低了


动态代理有几种实现?
1. java的动态代理技术的实现主要有两种方式: 
a. JDK原生动态代理 
b. CGLIB动态代理 

2. JDK原生动态代理是Java原生支持的,不需要任何外部依赖,但是它只能基于接口进行代理 (需要代理的对象必须实现于某个接口) 
3. CGLIB通过继承的方式进行代理(让需要代理的类成为Enhancer的父类),无论目标对象有没,有实现接口都可以代理,但是无法处理final的情况


堆和栈的区别?
1. 说下栈内存
a. 栈内存首先是一片内存区域,存储的都是局部变量
b. 凡是定义在方法中的都是局部变量(方法外的是全局变量),for循环内部定义的也是局部变量
c. 是先加载函数才能进行局部变量的定义,所以方法先进栈,然后再定义变量,变量有自己的作用域,一旦离开作用域,变量就会被释放。
d. 栈内存的更新速度很快,因为局部变量的生命周期都很短。

2. 说下堆内存
a. 存储的是数组和对象(其实数组就是对象),凡是new建立的都是在堆中
b. 堆中存放的都是实体(对象),实体用于封装数据,而且是封装多个(实体的多个属性),如果一个数据消失,这个实体也没有消失,还可以用,所以堆是不会随时释放的
c. 但是栈不一样,栈里存放的都是单个变量,变量被释放了,那就没有了。
d. 堆里的实体虽然不会被释放,但是会被当成垃圾,Java有垃圾回收机制不定时的收取。

3. 说下堆与栈的区别
a. 栈内存存储的是局部变量而堆内存存储的是实体;
b. 栈内存的更新速度要快于堆内存,因为局部变量的生命周期很短;
c. 栈内存存放的变量生命周期一旦结束就会被释放,而堆内存存放的实体会被垃圾回收机制不定时的回收。

常见的数据结构有哪些?
一共八大数据结构分类
a. 数组:连续的存储空间,查询速度快
b. 栈:先进后出
c. 队列:先进先出
d. 链表:
ⅰ. 单链表:链表中的元素的指向只能指向链表中的下一个元素或者为空,元素之间不能相互指向。也就是一种 线性链表。

ⅱ. 双向链表:是这样一个有序的结点序列,每个链表元素既有指向下一个元素的指针,又有指向前一个元素的指 针,其中每个结点都有两种指针,即left和right。left指针指向左边结点,right指针指向右边结 点。

ⅲ. 循环链表:是在单向链表和双向链表的基础上,将两种链表的最后一个结点指向第一个结点从而实现循环。


e. 树:它是由n(n>=1)个有限节点组成一个具有层次关系的集合。把它叫做 “树”是因为它看起来像一棵倒挂的树,也就是说它是根朝上,而叶朝下的

f. 散列表:散列表(Hash table,也叫哈希表),是根据关键码值(Key value)而直接进行访问的数据结构。也 就是说,它通过把关键码值映射到表中一个位置来访问记录,以加快查找的速度。这个映射函数叫 做散列函数,存放记录的数组叫做散列表。

g. 堆:堆是一种比较特殊的数据结构,可以被看做一棵树的数组对象,具有以下的性质: 堆中某个节点的值总是不大于或不小于其父节点的值; 堆总是一棵完全二叉树。

h. 图:图是由结点的有穷集合V和边的集合E组成。其中,为了与树形结构加以区别,在图结构中常常将结点称为顶点,边是顶点的有序偶对,若两个顶点之间存在一条边,就表示这两个顶点具有相邻关系


常见的树知道哪些?
1. 二叉树
2. 二叉查找树
3. 平衡二叉树
a. 平衡查找树之AⅥL(最佳二叉树)树
b. 平衡二叉树之红黑树
4. B树
5. B+树
6. B*树

常见设计模式知道哪些?
1. 单例模式(Singleton)
2. 构建模式(Builder)
3. 抽象工厂模式(Abstract Factory)
4. 工厂方法模式(Factory Method)
5. 观察者模式(Observer)
6. 模板方法模式(Template Method)
7. 装饰者模式(Decorator)
8. 代理模式(Proxy)

排序算法知道哪些?
1.冒泡排序(手写)
public static void bubbleSort(int[] arr) {
  int len = arr.length;
  for (int i = 0; i < len - 1; i++) {
    for (int j = 0; j < len - 1 - i; j++) {
      if (arr[j] > arr[j + 1]) {
        int temp = arr[j];
        arr[j] = arr[j + 1];
        arr[j + 1] = temp;
      }
    }
  }
}


2. 插入排序
3. 快速排序
4. 选择排序(手写)

public static void selectionSort(int[] arr) {
  int n = arr.length;
  for (int i = 0; i < n-1; i++) {
    int min_idx = i;
    for (int j = i+1; j < n; j++)
      if (arr[j] < arr[min_idx])
        min_idx = j;
    int temp = arr[min_idx];
    arr[min_idx] = arr[i];
    arr[i] = temp;
  }
}
5. 希尔排序
6. 归并排序
7. 堆排序
8. 计数排序
9. 桶排序
10. 基数排序

知道设计模式的分类吗?

创建型的设计模式:

a. 单例模式(Singleton)

b. 构建模式(Builder)

c. 原型模式(Prototype)

d. 抽象工厂模式(Abstract Factory)

e. 工厂方法模式(Factory Method)

行为设计模式:策略模式(Strategy)

a. 状态模式(State)

b. 责任链模式(Chain of Responsibility)

c. 解释器模式(Interpreter)

d. 命令模式(Command)

e. 观察者模式(Observer)

f. 备忘录模式(Memento)

g. 迭代器模式(Iterator)

h. 模板方法模式(Template Method)

i. 访问者模式(Visitor)

j. 中介者模式(Mediator)

结构型设计模式

a. 装饰者模式(Decorator)

b. 代理模式(Proxy)

c. 组合模式(Composite)

d. 桥连接模式(Bridge)

e. 适配器模式(Adapter)

f. 蝇量模式(Flyweight)

g. 外观模式(Facade)

辅助理解:

单例模式(Singleton):确保有且只有一个对象被创建。

抽象工厂模式(Abstract Factory):允许客户创建对象的家族,而无需指定他们的具体类。

工厂方法模式(Factory Method):由子类决定要创建的具体类是哪一个

装饰者模式(Decorator):包装一个对象,以提供新的行为

状态模式(State):封装了基于状态的行为,并使用委托在行为之间切换。

迭代器模式(Iterator):在对象的集合之中游走,而不暴露集合的实现。

外观模式(Facade):简化一群类的接口。

策略模式(Strategy):封装可以互换的行为,并使用委托来决定要使用哪一个

代理模式(Proxy):包装对象,以控制对此对象的访问

适配器模式(Adapter):封装对象,并提供不同的接口。

观察者模式(Observer):让对象能够在状态改变时被通知。

模板方法模式(Template Method):有子类决定如何实现一个算法中的步骤。

组合模式(Composite):客户用一致的方法处理对象集合和单个对象。

命令模式(Command):封装请求成为对象。

说一下单例模式?

1.单列模式有5种常见的写法

a. 饿汉式

b. 懒汉式

c. 双检锁

d. 静态内部类,用的最多

e. 枚举

2. 单例的四大原则:

a、构造器私有化

b、以静态方法或者枚举返回实例

c、确保实例只有一个,尤其是多线程环境

d、确保反序列化时不会重新构建对象

辅助理解

饿汉模式

public class SingleTon{

private static SingleTon INSTANCE = new SingleTon();

private SingleTon(){}

public static SingleTon getInstance(){

return INSTANCE;

 }

}

懒汉式

public class SingleTon{

private static SingleTon INSTANCE = null;

private SingleTon(){}

public static SingleTon getInstance() {

if(INSTANCE == null){

INSTANCE = new SingleTon();

 }

return INSTANCE;

}

}

双重检查加锁

public class SingleTon{

    private static SingleTon INSTANCE = null;

    private SingleTon(){}

    public static SingleTon getInstance(){

        if(INSTANCE == null){

            synchronized(SingleTon.class){

                if(INSTANCE == null){

                    INSTANCE = new SingleTon();

                }

            }

            return INSTANCE;

        }

    }

}

静态内部类,用的最多

public class SingleTon{

    private SingleTon(){}

    private static class SingleTonHoler{

        private static SingleTon INSTANCE = new SingleTon();

    }

    public static SingleTon getInstance(){

        return SingleTonHoler.INSTANCE;

    }

}

枚举不做要求

说下设计模式的原则?

1. 设计模式的六大原则

a. 开闭原则(Open Close Principle)

b. 里氏代换原则(Liskov Substitution Principle)

c. 依赖倒转原则(Dependence Inversion Principle)

d. 接口隔离原则(Interface Segregation Principle)

e. 迪米特法则,又称最少知道原则(Demeter Principle)

f. 合成复用原则(Composite Reuse Principle)

辅助理解详细说下几种原则

  1. 开闭原则(Open Close Principle):软件对扩展应该是开放的,对修改是封闭的,通俗来说就是,开发一个软件时,应该对其进行功能扩展,而在进行这些扩展时,不需要对原来的程序进行修改

2、里氏代换原则(Liskov Substitution Principle)

里氏代换原则是面向对象设计的基本原则之一。

里氏代换原则中说,任何基类可以出现的地方,子类一定可以出现。LSP 是继承复用的基石,只有当派生类可以替换掉基类,且软件单位的功能不受到影响时,基类才能真正被复用,而派生类也能够在基类的基础上增加新的行为。里氏代换原则是对开闭原则的补充。实现开闭原则的关键步骤就是抽象化,而基类与子类的继承关系就是抽象化的具体实现,所以里氏代换原则是对实现抽象化的具体步骤的规范(子类可以扩展父类的功能,但是不能改变父类原有的功能)

3、依赖倒转原则(Dependence Inversion Principle)

这个原则是开闭原则的基础,具体内容:针对接口编程,依赖于抽象而不依赖于具体。

4、接口隔离原则(Interface Segregation Principle)

这个原则的意思是:使用多个隔离的接口,比使用单个接口要好。它还有另外一个意思是:降低类之间的耦合度。由此可见,其实设计模式就是从大型软件架构出发、便于升级和维护的软件设计思想,它强调降低依赖,降低耦合。

5、迪米特法则,又称最少知道原则(Demeter Principle)

最少知道原则是指:一个实体应当尽量少地与其他实体之间发生相互作用,使得系统功能模块相对独立

  1. 合成复用原则(Composite Reuse Principle)合成复用原则是指:尽量使用合成/聚合的方式,而不是使用继承。

java中你知道哪些锁?

  1. 乐观锁/悲观锁

(1).乐观锁与悲观锁是一种广义上的概念,体现了看待线程同步的不同角度,在Java和数据库中都有此概念对应的实际应用(mysql中行锁、java.util.concurrent包中的原子类)

(2).悲观锁适合写操作多的场景,先加锁可以保证写操作时数据正确。

(3).乐观锁适合读操作多的场景,不加锁的特点能够使其读操作的性能大幅提升。

2. 共享锁/独享锁

3. 公平锁/非公平锁

4. 互斥锁/读写锁

5. 可重入锁

6. 自旋锁

7. 分段锁

(1).分段锁其实是一种锁的设计,并不是具体的一种锁,对于ConcurrentHashMap而言,其并发的实现就是通过分段锁的形式来实现高效的并发操作

(2).以ConcurrentHashMap来说一下分段锁的含义以及设计思想:

(2.1).ConcurrentHashMap中的分段锁称为Segment,它即类似于HashMap(JDK7与JDK8中HashMap的实现)的结构,即内部拥有一个Entry数组,数组中的每个元素又是一个链表;同时又是一个ReentrantLock(Segment继承了ReentrantLock)。

(2.2).当需要put元素的时候,并不是对整个hashmap进行加锁,而是先通过hashcode来知道他要放在那一个分段中,然后对这个分段进行加锁,所以当多线程put的时候,只要不是放在一个分段中,就实现了真正的并行的插入。

(2.3).在统计size的时候,可就是获取hashmap全局信息的时候,就需要获取所有的分段锁才能统计。

(2.4).分段锁的设计目的是细化锁的粒度,当操作不需要更新整个数组的时候,就仅仅针对数组中的一项进行加锁操作。

  1. 偏向锁/轻量级锁/重量级锁

Java线程的状态或者生命周期?

1. Java的线程状态被定义在公共枚举类java.lang.Thread.state中。一种有六种状态

a. 新建(new):表示线程新建出来还没有被启动的状态,比如:Thread t = new

MyThread();

b. 就绪/运行(runnable):该状态包含了经典线程模型的两种状态:就绪(Ready)、运行(Running):

c. 阻塞(blocked):通常与锁有关系,表示线程正在获取有锁控制的资源,比如进入synchronized代码块,获取ReentryLock等;发起阻塞式IO也会阻塞,比如字符流字节流操作。

d. 等待(waiting):线程在等待某种资源就绪。

e. 超时等待(timed_wait):线程进入条件和等待类似,但是它调用的是带有超时时间的方法。

f. 终止(terminated):线程正常退出或异常退出后,就处于终结状态。也可以叫线程的

死亡。

2. 经典线程模型包含5种状态,:新建、就绪、运行、等待、退出

3. 经典线程的就绪、运行,在java里面统一叫RUNNABLE

4. Java的BLOCKED、WAITING、TIMED_WAITING都属于传统模型的等待状态

哪些情况或者方法可以进入等待状态?

1. 当一个线程执行了Object.wait()的时候,它一定在等待另一个线程执行Object.notify()或者Object.notifyAll()。

2. 一个线程thread,其在主线程中被执行了thread.join()的时候,主线程即会等待该线程执行完成。

3. 当一个线程执行了LockSupport.park()的时候,其在等待执行

LockSupport.unpark(thread)。

哪些情况或者方法可以进入超时等待状态?

该状态不同于WAITING,它可以在指定的时间后自行返回

1. Object.wait(long)

2. Thread.join(long)

3. LockSupport.parkNanos()

4. LockSupport.parkUntil()

5. Thread.sleep(long)

synchronized 与lock(package java.util.concurrent.locks;)区别?

  1. Lock是一个接口,而synchronized是java的一个关键字

2. synchronized异常会释放锁,lock异常不会释放,所以一般try catch包起来,finally中写入unlock,避免死锁。

3. Lock可以提高多个线程进行读操作的效率

4. synchronized关键字,可以放代码块、实例方法、静态方法、类上

5. lock一般使用实现类ReentrantLock类做为锁,配合lock()和unlock()方法。在finally块中写unlock()以防死锁。

6. jdk1.6之前synchronized低效。jdk1.6之后synchronized高效。

synchronized 与ReentrantLock区别?

1. synchronized依赖JVM实现,ReentrantLock是JDK实现的。synchronized是内置锁,只要在代码开始的地方加synchronized,代码结束会自动释放。Lock必须手动加锁,手动释放锁。

2. ReenTrantLock比synchronized增加了一些高级功能。synchronized代码量少、自动化,但扩展性低,不够灵活;ReentrantLock扩展性好、灵活,但代码量相对多。

3. 两者都是可重入锁(也叫做递归锁,可重入锁指的是在一个线程中可以多次获取同一把锁),也都是互斥锁。

4. synchronized是非公平锁,ReentrantLock可以指定是公平锁也是非公平锁。

synchronized 与ThreadLocal区别?

  1. 都是为了解决多线程中相同变量的访问冲突问题

2. Synchronized同步机制,提供一份变量,让不同的线程排队访问。

3. ThreadLocal关键字,为每一个线程都提供了一份变量,因此可以同时访问而互不影响

4. ThreadLocal比直接使用synchronized同步机制解决线程安全问题更简单,更方便,且结果程序(专业术语)拥有更高的并发性

synchronized 与volatile区别?

1. volatile是一个类型修饰符(type specifier)

2. volatile,它能够使变量在值发生改变时能尽快地让其他线程知道

3. 关键字volatile是线程同步的轻量级实现,所以volatile性能肯定比synchronized要好,并且只能修改变量,而synchronized可以修饰方法,以及代码块

4. 多线程访问volatile不会发生阻塞,而synchronized会出现阻塞

5. volatile能保证数据的可见性,但不能保证原子性;而synchronized可以保证原子性,也可以间接保证可见性,因为它会将私有内存和公共内存中的数据做同步

6. 关键字volatile解决的是变量在多线程之间的可见性;而synchronized解决的是多线程之间资源同步问题

Thread类中的start() 和run()方法有什么区别?

1. 通过调用线程类的start()方法来启动一个线程,使线程处于就绪状态,即可以被JVM来调度执行,在调度过程中,JVM通过调用线程类的run()方法来完成实际的业务逻辑,当run()方法结束后,此线程就会终止

2.如果直接调用线程类的run()方法,会被当作一个普通的函数调用,程序中仍然只有主线程这一个线程。即start()方法能够异步的调用run()方法,但是直接调用run()方法却是同步的,无法达到多线程的目的

3.只用通过调用线程类的start()方法才能达到多线程的目的

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值