admin 管理员组

文章数量: 1184232


2024年1月17日发(作者:汇编语言编译器是什么平台)

基础篇

基本功

面向对象特征

final, finally, finalize 的区别int 和 Integer 有什么区别重载和重写的区别

抽象类和接口有什么区别说说反射的用途及实现

说说自定义注解的场景及实现

HTTP 请求的 GET 与 POST 方式的区别session 与 cookie 区别

JDBC 流程MVC 设计思想

equals 与 == 的区别

集合

List 和 Set 区别List 和 Map 区别

Arraylist 与 LinkedList 区别ArrayList 与 Vector 区别HashMap 和 Hashtable 的区别HashSet 和 HashMap 区别

HashMap 和 ConcurrentHashMap 的区别HashMap 的工作原理及代码实现ConcurrentHashMap 的工作原理及代码实现

线程

创建线程的方式及实现

sleep() 、join()、yield()有什么区别

说说 CountDownLatch 原理说说 CyclicBarrier 原理

说说 Semaphore 原理说说 Exchanger 原理

说说 CountDownLatch 与 CyclicBarrier 区别ThreadLocal 原理分析

讲讲线程池的实现原理

线程池的几种方式

线程的生命周期

锁机制

说说线程安全问题volatile 实现原理悲观锁 乐观锁CAS 乐观锁

ABA 问题

乐观锁的业务场景及实现方式

核心篇

数据存储

MySQL 索引使用的注意事项说说反模式设计

说说分库与分表设计

分库与分表带来的分布式困境与应对之策

说说SQL 优化之道

MySQL 遇到的死锁问题

存储引擎的 InnoDB 与 MyISAM

数据库索引的原理

为什么要用 B-tree

聚集索引与非聚集索引的区别

limit 20000 加载很慢怎么解决选择合适的分布式主键方案选择合适的数据存储方案 ObjectId

规则

聊聊 MongoDB 使用场景倒排索引

聊聊 ElasticSearch 使用场景缓存使用

Redis 有哪些类型Redis 内部结构

聊聊 Redis 使用场景Redis 持久化机制Redis 如何实现持久化Redis 集群方案与实现Redis 为什么是单线程的缓存奔溃

缓存降级

使用缓存的合理性问题

消息队列

消息队列的使用场景

消息的重发补偿解决思路消息的幂等性解决思路消息的堆积解决思路

自己如何实现消息队列

如何保证消息的有序性

框架篇

Spring

BeanFactory 和 ApplicationContext 有什么区别

Spring Bean 的生命周期Spring IOC 如何实现

说 说 Spring AOP

Spring AOP 实现原理

动态代理(cglib 与 JDK)

Spring 事务实现方式Spring 事务底层原理

如何自定义注解实现功能Spring MVC 运行流程Spring MVC 启动流程Spring 的单例实现原理

Spring 框架中用到了哪些设计模式

Spring 其他产品(Srping Boot、Spring Cloud、Spring Secuirity、Spring Data、Spring AMQP 等)

Netty

为什么选择 Netty

说说业务中,Netty 的使用场景

原生的 NIO 在 JDK 1.7 版本存在 epoll bug

什么是TCP 粘包/拆包TCP粘包/拆包的解决办法Netty 线程模型

说说 Netty 的零拷贝Netty 内部执行流程Netty 重连实现

微服务篇

微服务

前后端分离是如

微服务哪些框架

你怎么理解 RPC 框架说说 RPC 的实现原理说说 Dubbo 的实现原理你怎么理解 RESTful

说说如何设计一个良好的 API

如何理解 RESTful API 的幂等性如何保证接口的幂等性

说说 CAP 定理、 BASE 理论怎么考虑数据一致性问题

说说最终一致性的实现方案你怎么看待微服务

微服务与 SOA 的区别如何拆分服务

微服务如何进行数据库管理

如何应对微服务的链式调用异常对于快速追踪与定位问题

微服务的安全分布式

谈谈业务中使用分布式的场景

Session 分布式方案分布式锁的场景

分布式锁的实现方案分布式事务

集群与负载均衡的算法与实现

说说分库与分表设计

分库与分表带来的分布式困境与应对之策

安全&性能

安全问题

安全要素与 STRIDE 威胁防范常见的 Web 攻击

服务端通信安全攻防HTTPS 原理剖析HTTPS 降级攻击

授权与认证

基于角色的访问控制基于数据的访问控制

性能优化

性能指标有哪些如

何发现性能瓶颈

性能调优的常见手段

说说你在项目中如何进行性能调优

工程篇

需求分析

你如何对需求原型进行理解和拆分

说说你对功能性需求的理解

说说你对非功能性需求的理解

你针对产品提出哪些交互和改进意见

你如何理解用户痛点

设计能力

说说你在项目中使用过的 UML 图你如何考虑组件化

你如何考虑服务化你如何进行领域建模你如何划分领域边界

说说你项目中的

说说概要设计

设计模式

你项目中有使用哪些设计模式

说说常用开源框架中设计模式使用分析说说你对设计原则的理解

23种设计模式的设计理念

设计模式之间的异同,例如策略模式与状态模式的区别

设计模式之间的结合,例如策略模式+简单工厂模式的实践

设计模式的性能,例如单例模式哪种性能更好。

业务工程

你系统中的前后端分离是如何做的

说说你的开发流程

你和团队是如何沟通的

你如何进行代码评审

说说你对技术与业务的理解

说说你在项目中经常遇到的 Exception

说说你在项目中遇到感觉最难Bug,怎么解决的说说你在项目中遇到印象最深困难,怎么解决的你觉得你们项目还有哪些不足的地方

你是否遇到过 CPU 100% ,如何排查与解决你是否遇到过 内存 OOM ,如何排查与解决说说你对敏捷开发的实践

说说你对开发运维的实践

介绍下工作中的一个对自己最有价值的项目,以及在这个过程中的角色软实力

说说你的亮点

说说你最近在看什么书

说说你觉得最有意义的技术书籍工作之余做什么事情

基础篇

基本功

说说个人发展方向方面的思考

说说你认为的服务端开发工程师应该具备哪些能力说说你认为的架构师是什么样的,架构师主要做什么说说你所理解的技术专家

面向对象特征

封装,继承,多态和抽象

1. 封装

封装给对象提供了隐藏内部特性和行为的能力。对象提供一些能被其他对象访问的方法

来改

变它内部的数据。在 Java 当中,有 3 种修饰符: public, private 和 protected。每一种修饰符

给其他的位于同一个包或者不同包下面对象赋予了不同的访问权限。下面列出了使用封装的一些好处:

通过隐藏对象的属性来保护对象内部的状态。

提高了代码的可用性和可维护性,因为对象的行为可以被单独的改变或者是扩展。禁止对象之间的不良交互提高模块化

2. 继承

继承给对象提供了从基类获取字段和方法的能力。继承提供了代码的重用行,也可以在

不修改类的情况下给现存的类添加新特性。

3. 多态

多态是编程语言给不同的底层数据类型做相同的接口展示的一种能力。一个多态类型上

的操作可以应用到其他类型的值上面。

4. 抽象

抽象是把想法从具体的实例中分离出来的步骤,因此,要根据他们的功能而不是实现细

节来创建类。 Java 支持创建只暴漏接口而不包含方法实现的抽象的类。这种抽象技术的主要目的是把类的行为和实现细节分离开

final, finally, finalize 的区别

1. final

修饰符(关键字)如果一个类被声明为final,意味着它不能再派生出新的子类,不能作

为父类被继承。因此一个类不能既被声明为 abstract的,又被声明为final的。将变量或方法声明为final,可以保证它们在使用中不被改变。被声明为final的变量必须在声明时

给定初值,而在以后的引用中只能读取,不可修改。被声明为final的方法也同样只能使

用,不能重载。

2. finally

在异常处理时提供 finally 块来执行任何清除操作。如果抛出一个异常,那么相匹配的

catch 子句就会执行,然后控制就会进入 finally 块(如果有的话)。

3. finalize

方法名。Java 技术允许使用 finalize() 方法在垃圾收集器将对象从内存中清除出去之前做必要的清理工作。这个方法是由垃圾收集器在确定这个对象没有被引用时对这个对象

调用的。它是在 Object 类中定义的,因此所有的类都继承了它。子类覆盖 finalize() 方法以整理系统资源或者执行其他清理工作。finalize() 方法是在垃圾收集器删除对象之前对这个对象调用的。

int 和 Integer 有什么区别

int 是基本数据类型

Integer是其包装类,注意是一个类。为什么要提供包装类呢

一是为了在各种类型间转化,通过各种方法的调用。否则 你无法直接通过变量转化。比如,现在int要转为String

1 int a=0;

2 String result=ng(a);

3 1

4 2

在java中包装类,比较多的用途是用在于各种数据类型的转化中。我写几个demo

//通过包装类来实现转化的

1 int num=f("12");

2 int num2=nt("12");

3 double num3=f("12.2");

4 double num4=ouble("12.2");

5

//其他的类似。通过基本数据类型的包装来的valueOf和parseXX来实现String转为XX

6

String a=f("1234");//这里括号中几乎可以是任何类型

7 String b=f(true);

8

String c=new Integer(12).toString();//通过包装类的toString()也可以

9 String d=new Double(2.3).toString();

10 1

11 2

12 3

13 4

14 5

15 6

16 7

17 8

18 9

再举例下。比如我现在要用泛型

1 List nums;

2

1

这里<>需要类。如果你用int。它会报错的。

重载和重写的区别

override(重写)

1. 方法名、参数、返回值相同。

2. 子类方法不能缩小父类方法的访问权限。

3. 子类方法不能抛出比父类方法更多的异常(但子类方法可以不抛出异常)。

4. 存在于父类和子类之间。

5. 方法被定义为final不能被重写。

overload(重载)

1. 参数类型、个数、顺序至少有一个不相同。

2. 不能重载只有返回值不同的方法名。

3. 存在于父类和子类、同类中。

区别点

英文

定义

权限

范围

重载

Overloading

方法名称相同,参数的类型或个数不同

对权限没要求

发生在一个类中

Overiding

重写(覆写)

方法名称、参数类型、返回值类型全部相同

被重写的方法不能拥有更严格的权限

发生在继承类中

抽象类和接口有什么区别

接口是公开的,里面不能有私有的方法或变量,是用于让别人使用的,而抽象类是可以有私

有方法或私有变量的,

另外,实现接口的一定要实现接口里定义的所有方法,而实现抽象类可以有选择地重写需要

用到的方法,一般的应用里,最顶级的是接口,然后是抽象类实现接口,最后才到具体类实

现。

还有,接口可以实现多重继承,而一个类只能继承一个超类,但可以通过继承多个接口实现

多重继承,接口还有标识(里面没有任何方法,如Remote接口)和数据共享(里面的变量

全是常量)的作用。

说说反射的用途及实现

Java反射机制主要提供了以下功能:在运行时构造一个类的对象;判断一个类所具有的成

员变量和方法;调用一个对象的方法;生成动态代理。反射最大的应用就是框架

Java反射的主要功能:

确定一个对象的类

取出类的modifiers,数据成员,方法,构造器,和超类.

找出某个接口里定义的常量和方法说明.

创建一个类实例,这个实例在运行时刻才有名字(运行时间才生成的对象).

取得和设定对象数据成员的值,如果数据成员名是运行时刻确定的也能做到.

在运行时刻调用动态对象的方法.

创建数组,数组大小和类型在运行时刻才确定,也能更改数组成员的值.

反射的应用很多,很多框架都有用到

spring 的 ioc/di 也 是 反 射 …

javaBean和jsp之间调用也是反射…

struts的 FormBean 和页面之间…也是通过反射调用…

JDBC 的 classForName()也是反射…

hibernate的 find(Class clazz) 也是反射…

反射还有一个不得不说的问题,就是性能问题,大量使用反射系统性能大打折扣。怎么使用

使你的系统达到最优就看你系统架构和综合使用问题啦,这里就不多说了。

来源:/blog/1423512

说说自定义注解的场景及实现

(此题自由发挥,就看你对注解的理解了!==)登陆、权限拦截、日志处理,以及各种Java

框架,如Spring,Hibernate,JUnit 提到注解就不能不说反射,Java自定义注解是通过运行时靠反射获取注解。实际开发中,例如我们要获取某个方法的调用日志,可以通过

AOP(动态代理机制)给方法添加切面,通过反射来获取方法包含的注解,如果包含日志

注解,就进行日志记录。

HTTP 请求的 GET 与 POST 方式的区别

GET方法会把名值对追加在请求的URL后面。因为URL对字符数目有限制,进而限制了用在

客户端请求的参数值的数目。并且请求中的参数值是可见的,因此,敏感信息不能用这种方

式传递。

POST方法通过把请求参数值放在请求体中来克服GET方法的限制,因此,可以发送的参数

的数目是没有限制的。最后,通过POST请求传递的敏感信息对外部客户端是不可见的。

参考:/wangli-66/p/

session 与 cookie 区别

cookie 是 Web 服务器发送给浏览器的一块信息。浏览器会在本地文件中给每一个 Web 服务

器存储 cookie。以后浏览器在给特定的 Web 服务器发请求的时候,同时会发送所有为该服务器存储的 cookie。下面列出了 session 和 cookie 的区别:

无论客户端浏览器的做设置,session都应该能正常工作。客户端可以选择禁用

cookie,

但是, session 仍然是能够工作的,因为客户端无法禁用服务端的 session。

JDBC 流程

1、 加载JDBC驱动程序:

在连接数据库之前,首先要加载想要连接的数据库的驱动到JVM(Java虚拟机),

这通过类的静态方法forName(String className)实现。

例如:

1 try{

2

//加载MySql的驱动类

3 e("") ;

4 }catch(ClassNotFoundException e){

5

n("找不到驱动程序类 ,加载驱动失败!");

6 tackTrace() ;

7 }

8 1

9 2

10 3

11 4

12 5

13 6

14 7

成功加载后,会将Driver类的实例注册到DriverManager类中。

2、 提供JDBC连接的URL

连接URL定义了连接数据库时的协议、子协议、数据源标识。书写形式:协议:子协议:数据源标识

协议:在JDBC中总是以jdbc开始 子协议:是桥连接的驱动程序或是数据库管理系统名称。数据源标识:标记找到数据库来源的地址与连接端口。

例如:

jdbc:mysql://localhost:3306/test?

useUnicode=true&characterEncoding=gbk;useUnicode=true;(MySql的连接URL)

表示使用Unicode字符集。如果characterEncoding设置为 gb2312或GBK,本参数必须设置

为true 。characterEncoding=gbk:字符编码方式。

3、创建数据库的连接

要连接数据库,需要向Manager请求并获得Connection对象, 该对象就代表一个数据库的连接。

使用DriverManager的getConnectin(String url , String username , String password )方法传入指定的欲连接的数据库的路径、数据库的用户名和 密码来获得。

例如: //连接MySql数据库,用户名和密码都是root

1 String url = "jdbc:mysql://localhost:3306/test" ;

2 String username = "root" ;

3 String password = "root" ;

4 try{

5 Connection con = nection(url , username , password

) ;

6 }catch(SQLException se){

7

n("数据库连接失败!");

8 tackTrace() ;

9 }

10 1

11 2

12 3

13 4

14 5

15 6

16 7

17 8

18 9

4、 创建一个Statement

要执行SQL语句,必须获得ent实例,Statement实例分为以下3 种类型:

1、执行静态SQL语句。通常通过Statement实例实现。

2、执行动态SQL语句。通常通过PreparedStatement实例实现。

3、执行数据库存储过程。通常通过CallableStatement实例实现。具体的实现方式:

Statement stmt = Statement() ; PreparedStatement pstmt =

eStatement(sql) ; CallableStatement cstmt = eCall("{CALL

demoSp(? , ?)}") ;

5、执行SQL语句

Statement接口提供了三种执行SQL语句的方法:executeQuery 、executeUpdate 和

execute

1、ResultSet executeQuery(String sqlString):执行查询数据库的SQL语句 ,返回一个结果集(ResultSet)对象。

2、int executeUpdate(String sqlString):用于执行INSERT、UPDATE或 DELETE语句以及SQL DDL语句,如:CREATE TABLE和DROP TABLE等

3、execute(sqlString):用于执行返回多个结果集、多个更新计数或二者组合的 语句。 具体实现的代码:

ResultSet rs = eQuery(“SELECT * FROM …”) ; int rows =

eUpdate(“INSERT INTO …”) ; boolean flag = e(String sql) ;

6、处理结果

两种情况:

1、执行更新返回的是本次操作影响到的记录数。

2、执行查询返回的结果是一个ResultSet对象。

ResultSet包含符合SQL语句中条件的所有行,并且它通过一套get方法提供了对这些 行中数据的访问。

使用结果集(ResultSet)对象的访问方法获取数据:

while(()){

String name = ing(“name”) ;

String pass = ing(1) ; // 此方法比较高效

}

(列是从左到右编号的,并且从列1开始)

7、关闭JDBC对象

操作完成以后要把所有使用的JDBC对象全都关闭,以释放JDBC资源,关闭顺序和声 明顺序相反:

1、关闭记录集

2、关闭声明

3、关闭连接对象

1

if(rs != null){ // 关闭记录集

2

3

4

5

6

7

}

8

if(stmt != null){ // 关闭声明

}

try{

() ;

}catch(SQLException

e){ tackTrace() ;

9

10

11

12

13

14 }

try{

() ;

}catch(SQLException

e){ tackTrace() ;

}

15 if(conn != null){ // 关闭连接对象

16

17

18

19

20

21 }

22 1

23 2

24 3

25 4

26 5

27 6

28 7

29 8

30 9

31 10

32 11

33 12

34 13

35 14

36 15

37 16

38 17

39 18

40 19

41 20

42 21

try{

() ;

}catch(SQLException e){

tackTrace() ;

}

MVC 设计思想

MVC就是M:Model 模型V:View 视图

C:Controller 控制器

模型就是封装业务逻辑和数据的一个一个的模块,控制器就是调用这些模块的(java中通常是

用Servlet来实现,框架的话很多是用Struts2来实现这一层),视图就主要是你看到的,比如JSP

等.

当用户发出请求的时候,控制器根据请求来选择要处理的业务逻辑和要选择的数据,再返回去

把结果输出到视图层,这里可能是进行重定向或转发等.

equals 与 == 的区别

值类型(int,char,long,boolean等)都是用==判断相等性。对象引用的话,判断引用所指的对 象是否是同一个。equals是Object的成员函数,有些类会覆盖(override)这个方法,用于判

断对象的等价性。例如String类,两个引用所指向的String都是"abc",但可能出现他们实际对应的对象并不是同一个(和jvm实现方式有关),因此用判断他们可能不相等,但用equals判断一定是相等的。

集合

List 和 Set 区别

List,Set都是继承自Collection接口

List特点:元素有放入顺序,元素可重复

Set特点:元素无放入顺序,元素不可重复,重复元素会覆盖掉

(注意:元素虽然无放入顺序,但是元素在set中的位置是有该元素的HashCode决定的,其

位置其实是固定的,加入Set 的Object必须定义equals()方法 ,另外list支持for循环,也就是通过下标来遍历,也可以用迭代器,但是set只能用迭代,因为他无序,无法用下标来取得

想要的值。)

Set和List对比:

Set:检索元素效率低下,删除和插入效率高,插入和删除不会引起元素位置改变。

List:和数组类似,List可以动态增长,查找元素效率高,插入删除元素效率低,因为会引

起其他元素位置改变。

List 和 Map 区别

List是对象集合,允许对象重复。

Map是键值对的集合,不允许key重复。

Arraylist 与 LinkedList 区别

Arraylist:

优点:ArrayList是实现了基于动态数组的数据结构,因为地址连续,一旦数据存储好了,查

询操作效率会比较高(在内存里是连着放的)。

缺点:因为地址连续, ArrayList要移动数据,所以插入和删除操作效率比较低。LinkedList:

优点:LinkedList基于链表的数据结构,地址是任意的,所以在开辟内存空间的时候不需要等

一个连续的地址,对于新增和删除操作add和remove,LinedList比较占优势。LinkedList 适用于要头尾操作或插入指定位置的场景

缺点:因为LinkedList要移动指针,所以查询操作性能比较低。适用场景分析:

当需要对数据进行对此访问的情况下选用ArrayList,当需要对数据进行多次增加删除修改时

采用LinkedList。

ArrayList 与 Vector 区别

1

public ArrayList(int initialCapacity)//构造一个具有指定初始容量的空列表。

2

public ArrayList()//构造一个初始容量为10的空列表。

3

public ArrayList(Collection c)//构造一个包含指定

collection 的元素的列表

4 1

5 2

6 3

Vector有四个构造方法:

1

public Vector()//使用指定的初始容量和等于零的容量增量构造一个空向量。

2

public Vector(int initialCapacity)//构造一个空向量,使其内部数据数组的大

小,其标准容量增量为零。

3

public Vector(Collection c)//构造一个包含指定 collection 中

的元素的向量

4

public Vector(int initialCapacity,int capacityIncrement)//使用指定的初

始容量和容量增量构造一个空的向量

5 1

6

2

7 3

8

4

ArrayList和Vector都是用数组实现的,主要有这么三个区别:

1. Vector是多线程安全的,线程安全就是说多线程访问同一代码,不会产生不确定的结

果。而ArrayList不是,这个可以从源码中看出,Vector类中的方法很多有synchronized

进行修饰,这样就导致了Vector在效率上无法与ArrayList相比;

2. 两个都是采用的线性连续空间存储元素,但是当空间不足的时候,两个类的增加方式是

不同。

3. Vector可以设置增长因子,而ArrayList不可以。

4. Vector是一种老的动态数组,是线程同步的,效率很低,一般不赞成使用。适用场景分析:

1. Vector是线程同步的,所以它也是线程安全的,而ArrayList是线程异步的,是不安全的。如果不考虑到线程的安全因素,一般用ArrayList效率比较高。

2. 如果集合中的元素的数目大于目前集合数组的长度时,在集合中使用数据量比较大的数

据,用Vector有一定的优势。

HashMap 和 Hashtable 的区别

1.

hashMap去掉了HashTable 的contains方法,但是加上了containsValue()和

containsKey()方法。

2.

hashTable同步的,而HashMap是非同步的,效率上逼hashTable要高。p允许空键值,而hashTable不允许。

注意:

TreeMap:非线程安全基于红黑树实现。TreeMap没有调优选项,因为该树总处于平衡状态。

Treemap:适用于按自然顺序或自定义顺序遍历键(key)。

参考:/qq_22118507/article/details/51576319

HashSet 和 HashMap 区别

set是线性结构,set中的值不能重复,hashset是set的hash实现,hashset中值不能重复是

用hashmap的key来实现的。

map是键值对映射,可以空键空值。HashMap是Map接口的hash实现,key的唯一性是通过key值hash值的唯一来确定,value值是则是链表结构。

他们的共同点都是hash算法实现的唯一性,他们都不能持有基本类型,只能持有对象

HashMap 和 ConcurrentHashMap 的区别

ConcurrentHashMap是线程安全的HashMap的实现。

(1)

ConcurrentHashMap对整个桶数组进行了分割分段(Segment),然后在每一个分段上

都用lock锁进行保护,相对于HashTable的syn关键字锁的粒度更精细了一些,并发性能更

好,而HashMap没有锁机制,不是线程安全的。

(2)

HashMap的键值对允许有null,但是ConCurrentHashMap都不允许。

HashMap 的工作原理及代码实现

参考:/2015/07/01/Java集合学习1:HashMap的实现原理/

ConcurrentHashMap 的工作原理及代码实现

HashTable里使用的是synchronized关键字,这其实是对对象加锁,锁住的都是对象整体,

当Hashtable的大小增加到一定的时候,性能会急剧下降,因为迭代时需要被锁定很长的时

间。

ConcurrentHashMap算是对上述问题的优化,其构造函数如下,默认传入的是16,0.75,

16。

1

public ConcurrentHashMap(int paramInt1, float paramFloat, int

paramInt2) {

2

//…

3

int i = 0;

4

int j = 1;

5

while (j < paramInt2) {

6

7

8

}

9

tShift = (32 ‐ i);

10

tMask = (j ‐ 1);

11

ts = ay(j);

12

//…

13

int k = paramInt1 / j;

++i;

j <<= 1;

14 if (k * j < paramInt1)

15 ++k;

16 int l = 1;

17 while (l < k)

18 l <<= 1;

19

20 for (int i1 = 0; i1 < ; ++i1)

21 ts[i1] = new Segment(l, paramFloat);

22 }

23 public V put(K paramK, V paramV) {

24 if (paramV == null)

25 throw new NullPointerException();

26 int i = hash(de()); //这里的hash函数和HashMap中的不一样

27 return ts[(i >>> tShift &

tMask)].put(paramK, i, paramV, false);

28 }

29 1

30 2

31 3

32 4

33 5

34 6

35 7

36 8

37 9

38 10

39 11

40 12

41 13

42 14

43 15

44 16

45 17

46 18

47 19

48 20

49 21

50 22

51 23

52 24

53 25

54 26

55

27

56

28

ConcurrentHashMap引入了分割(Segment),上面代码中的最后一行其实就可以理解为把一

个大的Map拆分成N个小的HashTable,在put方法中,会根据hash(de())来 决定具体存放进哪个Segment,如果查看Segment的put操作,我们会发现内部使用的同步 机制是基于lock操作的,这样就可以对Map的一部分(Segment)进行上锁,这样影响的只 是将要放入同一个Segment的元素的put操作,保证同步的时候,锁住的不是整个Map(HashTable就是这么做的),相对于HashTable提高了多线程环境下的性能,因此HashTable已经被淘汰了。

线程

创建线程的方式及实现

Java中创建线程主要有三种方式:

一、继承Thread类创建线程类

(1)

定义Thread类的子类,并重写该类的run方法,该run方法的方法体就代表了线程要完成的任务。因此把run()方法称为执行体。

(2)

创建Thread子类的实例,即创建了线程对象。

(3)

调用线程对象的start()方法来启动该线程。

1

package ;

2

3

public class FirstThreadTest extends Thread{

4

int i = 0;

5

6

7

8

9

10

11

12

13

14

15

}

public static void main(String[] args)

{

for(int i = 0;i< 100;i++)

{

}

//重写run方法,run方法的方法体就是现场执行体public void run()

{

for(;i<100;i++){ n(getName()+" "+i);

16

n(tThread().getName()+" :

"+i);

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

if(i==20)

{

new FirstThreadTest().start();

new FirstThreadTest().start();

}

}

}

}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

上述代码中tThread()方法返回当前正在执行的线程对象。getName()方法返

回调用该方法的线程的名字。

二、通过Runnable接口创建线程类

(1)

定义runnable接口的实现类,并重写该接口的run()方法,该run()方法的方法体同样是该线程的线程执行体。

(2)

创建 Runnable实现类的实例,并依此实例作为Thread的target来创建Thread对象,该Thread对象才是真正的线程对象。

(3)

调用线程对象的start()方法来启动该线程。

1 package ;

2

3 public class RunnableThreadTest implements Runnable

4 {

5

6

7

8

9

10

11

"+i);

12

13

14

15

16

17

18

"+i);

19

20

21

22

23

25

26

27 }

28

29 1

30 2

31 3

32 4

33 5

34 6

}

}

}

if(i==20)

{

RunnableThreadTest rtt = new RunnableThreadTest();

new Thread(rtt,"新线程1").start();

new Thread(rtt,"新线程2").start(); 24}

public static void main(String[] args)

{

for(int i = 0;i < 100;i++)

{

n(tThread().getName()+"

}

private int i;

public void run()

{

for(i = 0;i <100;i++)

{

n(tThread().getName()+"

35 7

36 8

37 9

38 10

39 11

40 12

41 13

42 14

43 15

44 16

45 17

46 18

47 19

48 20

49 21

50 22

51 23

52 24

53 25

54 26

55 27

56 28

三、通过Callable和Future创建线程

(1)

创建Callable接口的实现类,并实现call()方法,该call()方法将作为线程执行体,并且有返回值。

(2)

创建Callable实现类的实例,使用FutureTask类来包装Callable对象,该FutureTask对象封装了该Callable对象的call()方法的返回值。

(3)

使用FutureTask对象作为Thread对象的target创建并启动新线程。

(4)

调用FutureTask对象的get()方法来获得子线程执行结束后的返回值

1 package ;

2

3 import le;

4 import ionException;

5 import Task;

6

7 public class CallableThreadTest implements Callable

8 {

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

"+i);

39

40

41

42 }

43 1

44 2

45 3

46 4

47 5

}

}

return i;

}

@Override

public Integer call() throws Exception

{

int i = 0;

for(;i<100;i++)

{

n(tThread().getName()+"

{

}

tackTrace();

{

tackTrace();

} catch (ExecutionException e)

}

try

{

n("子线程的返回值:"+());

} catch (InterruptedException e)

public static void main(String[] args)

{

CallableThreadTest ctt = new CallableThreadTest();

FutureTask ft = new FutureTask<>(ctt);

for(int i = 0;i < 100;i++)

{

n(tThread().getName()+" 的循

环变量i的值"+i);

if(i==20)

{

}

new Thread(ft,"有返回值的线程").start();

48 6

49 7

50 8

51 9

52 10

53 11

54 12

55 13

56 14

57 15

58 16

59 17

60 18

61 19

62 20

63 21

64 22

65 23

66 24

67 25

68 26

69 27

70 28

71 29

72 30

73 31

74 32

75 33

76 34

77 35

78 36

79 37

80 38

81 39

82 40

83 41

84 42

创建线程的三种方式的对比

采用实现Runnable、Callable接口的方式创见多线程时,优势是:

线程类只是实现了Runnable接口或Callable接口,还可以继承其他类。

在这种方式下,多个线程可以共享同一个target对象,所以非常适合多个相同线程来处理同

一份资源的情况,从而可以将CPU、代码和数据分开,形成清晰的模型,较好地体现了面

向对象的思想。

劣势是:

编程稍微复杂,如果要访问当前线程,则必须使用tThread()方法。使用继承Thread类的方式创建多线程时优势是:

编写简单,如果需要访问当前线程,则无需使用tThread()方法,直接使用this

即可获得当前线程。

劣势是:

线程类已经继承了Thread类,所以不能再继承其他父类。

sleep() 、join()、yield()有什么区别

1、sleep()方法

在指定的毫秒数内让当前正在执行的线程休眠(暂停执行),此操作受到系统计时器和调度

程序精度和准确性的影响。 让其他线程有机会继续执行,但它并不释放对象锁。也就是如

果有Synchronized同步块,其他线程仍然不能访问共享数据。注意该方法要捕获异常

比如有两个线程同时执行(没有Synchronized),一个线程优先级为MAX_PRIORITY,另一

个为MIN_PRIORITY,如果没有Sleep()方法,只有高优先级的线程执行完成后,低优先级

的线程才能执行;但当高优先级的线程sleep(5000)后,低优先级就有机会执行了。

总之,sleep()可以使低优先级的线程得到执行的机会,当然也可以让同优先级、高优先级的

线程有执行的机会。

2、yield()方法

yield()方法和sleep()方法类似,也不会释放“锁标志”,区别在于,它没有参数,即yield()方

法只是使当前线程重新回到可执行状态,所以执行yield()的线程有可能在进入到可执行状态

后马上又被执行,另外yield()方法只能使同优先级或者高优先级的线程得到执行机会,这也

和sleep()方法不同。

3、join()方法

Thread的非静态方法join()让一个线程B“加入”到另外一个线程A的尾部。在A执行完毕之前,

B不能工作。

1

Thread t = new MyThread();

();

();

2

3

4 1

5 2

6 3

保证当前线程停止执行,直到该线程所加入的线程完成为止。然而,如果它加入的线程没有

存活,则当前线程不需要停止。

说说 CountDownLatch 原理

参考:

分析CountDownLatch的实现原理

什么时候使用CountDownLatch

Java并发编程:CountDownLatch、CyclicBarrier和Semaphore

说说 CyclicBarrier 原理

参考:

JUC回顾之-CyclicBarrier底层实现和原理

说说 Semaphore 原理

JAVA多线程–信号量(Semaphore)

JUC回顾之-Semaphore底层实现和原理

说说 Exchanger 原理

ger应用范例与原理浅析

说说 CountDownLatch 与 CyclicBarrier 区别

CountDownLatch

减计数方式

计算为0时释放所线有程

CyclicBarrier

加计数方式

定值 时释放所有等待线程

计数为0时,无法重置

调用countDown()方法计数减一,调用await()

方法只进行阻塞,对计数没任何影响

不可重复利用

计数达到指定值时,计数置为0重新开始

调用await()方法计数加1,若加1后的值不等于构造方法的值,则线程阻塞

可重复利用

尽量把CyclicBarrier和CountDownLatch的区别说通俗点

ThreadLocal 原理分析

Java并发编程:深入剖析ThreadLocal

讲讲线程池的实现原理

主要是ThreadPoolExecutor的实现原理

Java并发编程:线程池的使用

线程池的几种方式

newFixedThreadPool(int nThreads)

创建一个固定长度的线程池,每当提交一个任务就创建一个线程,直到达到线程池的最大数

量,这时线程规模将不再变化,当线程发生未预期的错误而结束时,线程池会补充一个新的

线程

newCachedThreadPool()

创建一个可缓存的线程池,如果线程池的规模超过了处理需求,将自动回收空闲线程,而当

需求增加时,则可以自动添加新线程,线程池的规模不存在任何限制

newSingleThreadExecutor()

这是一个单线程的Executor,它创建单个工作线程来执行任务,如果这个线程异常结束,会

创建一个新的来替代它;它的特点是能确保依照任务在队列中的顺序来串行执行

newScheduledThreadPool(int corePoolSize)

创建了一个固定长度的线程池,而且以延迟或定时的方式来执行任务,类似于Timer。举个栗子

1 private static final Executor exec=edThreadPool(50);

2

3 Runnable runnable=new

4

Runnable(){ public void run(){

5

6

7 }

}

...

8 e(runnable);

9

10 Callable callable=new Callable() {

11

12

14 };

15

16 Future future=(callable);

17 (); // 等待计算完成后,获取结果

18 (); // 如果任务已完成,则返回 true

19 elled(); // 如果在任务正常完成前将其取消,则返回 true

20 (true); // 试图取消对此任务的执行,true中断运行的任务,false

允许正在运行的任务运行完成

21 1

22 2

23 3

24 4

25 5

26 6

27 7

28 8

29 9

30 10

31 11

32 12

33 13

34 14

35 15

36 16

37 17

38 18

39 19

40 20

public Object call() throws Exception {

return null; 13}

参考:

创建线程池的几种方式

线程的生命周期

新建(New)、就绪(Runnable)、运行(Running)、阻塞(Blocked)和死亡(Dead)5种状态

(1)生命周期的五种状态新建(new Thread)

当创建Thread类的一个实例(对象)时,此线程进入新建状态(未被启动)。

例如:Thread t1=new Thread();

就绪(runnable)

线程已经被启动,正在等待被分配给CPU时间片,也就是说此时线程正在就绪队列中排队

等候得到CPU资源。例如:();

运行(running)

线程获得CPU资源正在执行任务(run()方法),此时除非此线程自动放弃CPU资源或者有

优先级更高的线程进入,线程将一直运行到结束。

死亡(dead)

当线程执行完毕或被其它线程杀死,线程就进入死亡状态,这时线程不可能再进入就绪状态

等待执行。

自然终止:正常运行run()方法后终止

异常终止:调用**stop()**方法让一个线程终止运行堵塞(blocked)

由于某种原因导致正在运行的线程让出CPU并暂停自己的执行,即进入堵塞状态。

正在睡眠:用sleep(long t) 方法可使线程进入睡眠方式。一个睡眠着的线程在指定的时间过去可进入就绪状态。

正在等待:调用wait()方法。(调用motify()方法回到就绪状态)

被另一个线程所阻塞:调用suspend()方法。(调用resume()方法恢复)

参考:

线程的生命周期

锁机制

说说线程安全问题

线程安全是指要控程制对某个资源的有序访问或修改,而在这些线程之间没有产生冲突。

在Java里,线程安全一般体现在两个方面:

1、多个thread对同一个java实例的访问(read和modify)不会相互干扰,它主要体现在关

键字synchronized。如ArrayList和Vector,HashMap和Hashtable(后者每个方法前都有synchronized关键字)。如果你在interator一个List对象时,其它线程remove一个element,

问题就出现了。

2、每个线程都有自己的字段,而不会在多个线程之间共享。它主要体现在Local类,而没有Java关键字支持,如像static、transient那样。

volatile 实现原理

聊聊并发(一)——深入分析Volatile的实现原理

悲观锁 乐观锁

乐观锁 悲观锁

是一种思想。可以用在很多方面。比如数据库方面。

悲观锁就是for update(锁定查询的行)

乐观锁就是 version字段(比较跟上一次的版本号,如果一样则更新,如果失败则要重复读-

比较-写的操作。)

JDK方面:

悲观锁就是sync

乐观锁就是原子类(内部使用CAS实现)

本质来说,就是悲观锁认为总会有人抢我的。乐观锁就认为,基本没人抢。

CAS 乐观锁

乐观锁是一种思想,即认为读多写少,遇到并发写的可能性比较低,所以采取在写时先读出

当前版本号,然后加锁操作(比较跟上一次的版本号,如果一样则更新),如果失败则要重

复读-比较-写的操作。

CAS是一种更新的原子操作,比较当前值跟传入值是否一样,一样则更新,否则失败。CAS顶多算是乐观锁写那一步操作的一种实现方式罢了,不用CAS自己加锁也是可以的。

ABA 问题

ABA:如果另一个线程修改V值假设原来是A,先修改成B,再修改回成A,当前线程的CAS

操作无法分辨当前V值是否发生过变化。


本文标签: 线程 方法 实现

更多相关文章

XP Live DLL加载失败?掌握这三步轻松解决,让你用得安心!

1月前

电脑使用中,动态链接库(DLL)文件加载失败是一个常见的技术障碍,并且特别提到的xlive.dll加载问题尤其普遍。xlive.dll是微软公司推出的“Games for Windows - LIVE”服务所依赖的核心DLL文件,这对

搞不定NORMAL.DOTM?解锁Word文档打开障碍的秘诀都在这

1月前

Word文档打不开?四招轻松解决在日常办公中,Word文档作为不可或缺的工具,承载着大量的文字处理和文档编辑工作。然而,当用户急于查看或编辑某个Word文档时,却发现它无法打开,这无疑会让人倍感焦虑。这种情况不仅会导致时

xinput1_3.dll出错让你头疼?简单步骤帮你快速解决

1月前

找不到xinput1_3.dll要怎么处理?首先我们就要先了解xinput1_3.dll这个文件,只有知道这个文件了,那么才可以修复xinput1_3.dll文件,今天我们就来给大家详细的讲解一下这方面吧。 一.xinput1

电脑故障提示xinput1_3.dll缺失,试试这7招帮你快速修复系统!

1月前

xinput1_3.dll是微软Microsoft DirectX的一个重要动态链接库(DLL)文件,它主要与DirectInput API相关,为Windows操作系统中的游戏和应用程序提供对各种输入设备的支持。以下是关于xinpu

卡在错误代码126上?这篇指南集合了7个实用技巧,帮你快速解决xinput1_3.dll加载失败难题!

1月前

xinput1_3.dll是Windows操作系统中一个非常关键的动态链接库(Dynamic Link Library, DLL)文件,它是微软DirectX软件开发包的组成部分,专门用于支持游戏控制器和其它输入设备在游戏及多媒体应用

破解无法启动的游戏难题——找到并修复xinput1_3.dll的方法!

1月前

当电脑出现提示,显示 ​xinput1_3.dll​文件缺失时,这实际上是一种常见的DLL文件遗失错误,其修复过程通常相对简单。今天,我们将探讨四种不同的方法来解决 ​xinput1_3.dll​缺失的问题。 一.xinput

xinput1_3.dll失踪了?四个实用方法帮你快速搞定

1月前

找不到xinput1_3.dll要怎么处理?首先我们就要先了解xinput1_3.dll这个文件,只有知道这个文件了,那么才可以修复xinput1_3.dll文件,今天我们就来给大家详细的讲解一下这方面吧。 一.xinput1

解决MSVCP110.dll出错:简单步骤帮你快速修复电脑问题

1月前

msvcp110.dll是什么东西?如果有人问出这个问题,那么百分之一百就是他遇到了丢失msvcp110.dll文件的问题了,不然别人是不可能问出这个问题的!这个文件名字一般只有在你的电脑系统出现问题的时候,它才会弹出提示。今天来给大

安卓应用开发者指南:弹幕视频功能详解

1月前

简单概述 现在有个很流行视频的效果就是 弹幕效果,满屏幕的文字从右到左飘来飘去。看的眼花缭乱,看起来还蛮cool的现在就是来实现这一的一个效果,大部分的都是从右向左移动漂移,本文的效果中也支持从左向右的

从错误到流畅:解决Flash中心内存读取问题的实用方法

1月前

内存不能为read正确修复方法2010年06月03日内存不能为read正确修复方法:内存指令不能read,这是在电脑使用中常常出现地情况,内存是主板上地存储部件,是CPU直接与之沟通,并用其寄存当前正在使用地(即

Linux中的隐蔽空间:隐藏文件全解析

1月前

在Linux中,隐藏文件以点(.)开头的文件或文件夹被认为是隐藏文件。隐藏文件通常用于存储系统配置文件或敏感文件。 以下是几种不同的方法来隐藏文件或文件夹: 方法1:在文件或文件夹名字前面加上点(.) mv fil

解锁隐藏文件夹的秘籍:轻松步骤教你显示系统中的隐藏文件

1月前

一、问题背景 有时候急着找一份重要文件,明明记得存在电脑里, 翻遍所有文件夹却连影子都看不到!是不是瞬间慌了神, 怀疑文件被误删,甚至担心电脑中病毒了? 其实不用瞎着急!

d3dcompiler43.dll丢失?别慌,这里教你3步快速找到并解决方法!

1月前

d3dcompiler_43.dll是什么文件?当你知道d3dcompiler_43.dll这个文件名字的时候,相信你是遇到了d3dcompiler_43.dll丢失的问题了!所以才会这样问,其实这就是一个普通的dll文件,对于电脑系

一文详解D3DCompiler_43.dll,快速修复丢失不再烦恼

27天前

dcompiler_43.dll 是一个Windows系统中的系统文件,属于DirectX软件的一部分。这个dcompiler_43.dll(动态链接库)文件主要用于处理与3D图形编程有关的任务,是运行许多游戏和高级图形程序必需的组件

内存清理大法:让你的电脑运行如飞,告别卡顿烦恼

18天前

电脑内存(RAM)的清理对于维持系统的流畅运行至关重要。随着使用时间的增加,系统内存会被各种应用程序和后台进程占用,导致系统响应变慢,甚至出现卡顿现象。通过有效地清理内存,可以提升电脑的性能,延长其使用寿命。本文将详细介绍如何清理电脑

Android7.0 数据业务长连接拨号过程_数据拨号

18天前

前面我们已经分析了android在进行数据业务拨号前,进行相关准备工作的流程,现在我们可以分析一下整个数据业务长连接拨号在框架部分的流程。 长连接的“长”,是相对于终端进行彩信发送等操作时,建立的临时数据连接而言的(这种临时数

Winsock LSP导致无法上网(传说中的“浏览器劫持”)

17天前

关于Winsock LSP“浏览器劫持”,中招者一直高居不下,由于其特殊性,直接删除而不恢复LSP的正常状态很可能会导致无法上网所以对其修复需慎重. 先说说什么是Winsock LSP“浏览器劫持”.Winsock LS

如何让BACK键变为HOME键_android 屏蔽返回键改为home

17天前

back键Android的程序无需刻意的去退出,当你一按下手机的back键的时候,系统会默认调用程序栈中最上层Activity的Destroy()方法来销毁当前Activity,当此Activity又被其它Activit

无法识别USB设备解决办法_usb无法识别设备怎么办

15天前

无法识别USB设备 有时候当我们将USB移动硬盘或者U盘插到电脑上时,会遇到“无法识别USB设备”问题,反复重试也无济于事,但是同一个USB设备在其他电脑中是可以正常显示的。 方法1:卸载驱动,然后重新连接外置硬盘

五种百度云盘速度慢解决方法_百度云

15天前

我们chrome插件网在之前有介绍过一篇文章:!里面有介绍过一些百度云盘下载工具和解决百度云盘下载限速的问题。时间久了有些方法并不好用啦,今天我们重新整理了五种百度云盘下载速度慢解决方法。希望可以帮助到大家。 原文

发表评论

全部评论 0
暂无评论