admin 管理员组

文章数量: 887053


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值是否发生过变化。


本文标签: 线程 方法 实现

更多相关文章

Matlab R2011b破解方法【来自大佬】

1月前

问题&#xff1a;&#xff08;有问题不可怕&#xff0c;可怕的是无法描述问题 --CC&#xff09; 网上都可以下载到MatlabR2011b的安装包&#xff0c;其中安装包里面也包含了破

电脑截图快捷键是哪个?分享3种截屏方法,轻松上手

1月前

当你正用电脑浏览网页或者打游戏&#xff0c;突然屏幕上的内容让你眼前一亮&#xff0c;想要立刻保存下来&#xff0c;你会选择什么样的方式&#xff1f;许多人不免要问&#xff0c;电脑截图快捷

安装Python 提示缺少Windows 7 Service Pack 1不一样的方法 。Log File提示缺少KB2533625

1月前

遇到这个问题&#xff0c;不要直接点击update your machine 去下载Windows 7 Service Pack 1,首先应该查看Log File&#xff0c;查看最后一行提示的详细信息&#xff

Beyond Compare 4.X 破解方法(亲测有效)

1月前

Windows下Beyond Compare 4 30天评估到期了的话&#xff0c;可以尝试下面两种方式: 破解方式 把Beyond Compare 4安装文件夹下面的BCUnrar.dll文件删掉就行了&#xff0c;

win7安装wincc信息服务器不可用,Win7安装WinCC的方法

1月前

WinCC 7.0 SP1 以下的版本最高支持到 Windows Vista 操作系统&#xff0c;如果需要安装在 Win 7 下&#xff0c;要以某种兼容模式安装。 WinCC 7.0 SP2 支持Windows 7&

虚拟机连接物理机的打印机_deepin下virtualbox虚拟windows 7系统安装物理打印机的方法...

27天前

正在使用deepin linux操作系统&#xff0c;工作多数用windows 7&#xff0c;目前打印机在linux下支持得不是很理想&#xff0c;在用到打印机时总是在这两个操作系统中切换来切换去很麻烦&am

最新android系统下载,安卓6.0抢先体验方法|安卓6.0下载 安卓6.0系统下载地址_PC6教学...

27天前

谷歌正式确定了Android M版本号为安卓6.0&#xff0c;同时确定其代号为MARshmallow(棉花糖)&#xff0c;而且官方也放出了最新棉花糖系统的第三个开发者预览版固件&#xff0c;对于喜欢尝鲜和折

[Tools]Windows 7(64)系統下提取 deb 文件中的某个文件的一种方法

25天前

需求背景: 在分析手機發生 crash 的core dump 的時候&#xff0c;需要vmlinux 這個文件。 而這隻文件由於build 出來的時候被打包進 deb. 之前的做法是將deb放到 ubuntu 平台上解壓。

Windows7下使用Tsmmc.msc远程管理工具的方法

25天前

具体步骤&#xff1a; 1、将windows2003系统C:WINDOWSsystem32目录下的mstsmhst.dll、mstsmmc.dll、tsmmc.msc拷贝到Windows7系统中的C:WINDOWSsys

查看计算机.net环境版本,windows7系统查看NET Framework版本的两种方法

20天前

现在&#xff0c;很多用户都会在电脑中安装NET Framework&#xff0c;因为一些程序需要NET Framework才能运行。那么&#xff0c;如果想要查看我们计算机中的 NET Framework版本

Windows7下VB6的安装方法~

19天前

之前写过一篇日志&#xff0c;关于VB6程序在Windows7下运行的问题(http:www.dingcrownsoftblogLog?ID28.html) 。那么&#xff0c;如果要在Windows7下

Js判断linux还是windows,js获取客户端操作系统类型的方法【测试可用】

16天前

本文实例讲述了js获取客户端操作系统类型的方法。分享给大家供大家参考&#xff0c;具体如下&#xff1a; p> "http:www.w3TRxhtml1DTDxhtml1-transitio

电脑黑屏和蓝屏故障处理方法

16天前

电脑黑屏和蓝屏是常见的故障现象&#xff0c;可能由多种原因引起。以下是针对这两种故障的详细处理方法。 一、电脑黑屏故障处理 检查硬件连接 首先检查显示器的电源线、数据线是否连接牢固。确保电源线插紧在电源插座上&#xff

解决Windows7 Path环境变量过长的方法

14天前

http:blog.csdnswhardarticledetails53141937 今天在使用ffmpeg开发时出现了找不到文件的情况&#xff0c;最终发现cmd中path命令打印的路径不完整&#xff08;

icc校色文件使用教程_Windows7色彩管理显示器ICC设置方法

14天前

自从使用Windows 7之后&#xff0c;发现Windows 7并不会自动调用校色后的显示器icc文件。甚至人工切换icc文件时&#xff0c;颜色竟然没跟着转换(这代表icc对应的显卡LUT没有被上载到显示适配器去)&

win10 C盘扩容(系统自带磁盘管理功能受限时,请尝试此方法,亲测有效)

12天前

C盘的空间不够用了&#xff0c;D盘里面又有很多东西&#xff0c;空闲盘与C盘之间又隔了个D盘&#xff0c;这样无法直接扩容C盘的&#xff0c;可以试试下面的方法。 提醒&#xff1a;此方

LangChain 48 终极解决 实战Langchain访问OpenAI ChatGPT API Account deactivated的另类方法,访问跳板机API

11天前

OpenAI ChatGPT API Account deactivated的另类方法&#xff0c;访问跳板机API 系列 LangChain 40 实战Langchain访问OpenAI ChatGPT API Account

Win7下的 Visual Studio 2008 破解90天限制的激活升级方法。

11天前

&#xfeff;&#xfeff; 如今&#xff0c;Windows7 的推出&#xff0c;大大加速了 Vista 的退出历史舞台。对于开发人员而言&#xff0c;更是喜欢追逐新的事务。 很多朋友

cmd清理打印机缓存_如何清理C盘垃圾 清理C盘垃圾方法【详解】

11天前

电脑运行久了以后,难免会产生大量的临时文件和各种数据缓存等等,眼看着C盘空间越来越小,想要自己清理,又不知道哪些能删,哪些不能删。那么C盘空间快满了,如何清理C盘垃圾呢?下面小编带大家分享一下几个 C盘垃圾的清理办法 。 1、休眠文件清理

Win7延长激活时间方法(微软官方文档)

2天前

1、首先安装后&#xff0c;有一个30天的使用期。 2、在30天试用期即将结束时&#xff0c;用rearm命令后重启电脑&#xff0c;剩余时间又回复到30天。中声明该命令只能重复使用3次&#xff0c;

发表评论

全部评论 0
暂无评论