Star's Blog

Keep learning, Keep improving


  • 首页

  • 分类

  • 关于

  • 标签

  • 归档

  • 搜索

Oracle数据库中的索引

发表于 2018-10-28 | 分类于 数据库

建立索引的优点:
1、大大加快数据的检索速度;
2、创建唯一性索引,保证数据库表中每一行数据的唯一性;
3、加速表和表之间的连接;
4、在使用分组和排序子句进行数据检索时,可以显著减少查询中分组和排序的时间。

索引的种类

1、按照索引列值的唯一性,索引可分为唯一索引和非唯一索引;

非唯一索引:

create index 索引名 on 表名(列名) tablespace 表空间名;

唯一索引:

建立主键或者唯一约束时会自动在对应的列上建立唯一索引;

注:创建主键时,默认在主键上创建了唯一索引,因此不能再在主键上创建索引。

2、索引列的个数:单列索引和复合索引;

3、按照索引列的物理组织方式。

B树索引

create index 索引名 on 表名(列名) tablespace 表空间名;

位图索引

create bitmap index 索引名 on 表名(列名) tablespace 表空间名;

反向键索引

create index 索引名 on 表名(列名) reverse tablespace 表空间名;

函数索引

create index 索引名 on 表名(函数名(列名)) tablespace 表空间名;

聚集索引(也叫聚簇索引)
在聚集索引中,表中行的物理顺序与键值的逻辑(索引)顺序相同.一个表只能包含一个聚集索引.如果某索引不是聚集索引,则表中行的物理顺序与键值的逻辑顺序不匹配.与非聚集索引相比,聚集索引通常提供更快的数据访问速度.

删除索引

drop index 索引名

重建索引

alter index 索引名 rebuild

索引的创建格式:

1
2
3
4
5
6
7
8
9
10
11
CREATE UNIQUE | BITMAP INDEX <schema>.<index_name> 
ON <schema>.<table_name>
(<column_name> | <expression> ASC | DESC,
<column_name> | <expression> ASC | DESC,...)
TABLESPACE <tablespace_name>
STORAGE <storage_settings>
LOGGING | NOLOGGING
COMPUTE STATISTICS
NOCOMPRESS | COMPRESS<nn>
NOSORT | REVERSE
PARTITION | GLOBAL PARTITION<partition_setting>

​ UNIQUE | BITMAP:指定UNIQUE为唯一值索引,BITMAP为位图索引,省略为B-Tree索引。
​ <column_name> | ASC | DESC:可以对多列进行联合索引,当为expression时即“基于函数的索引”
​ TABLESPACE:指定存放索引的表空间(索引和原表不在一个表空间时效率更高)
​ STORAGE:可进一步设置表空间的存储参数
​ LOGGING | NOLOGGING:是否对索引产生重做日志(对大表尽量使用NOLOGGING来减少占用空间并提高效率)
​ COMPUTE STATISTICS:创建新索引时收集统计信息
​ NOCOMPRESS | COMPRESS:是否使用“键压缩”(使用键压缩可以删除一个键列中出现的重复值)
​ NOSORT | REVERSE:NOSORT表示与表中相同的顺序创建索引,REVERSE表示相反顺序存储索引值
​ PARTITION | NOPARTITION:可以在分区表和未分区表上对创建的索引进行分区

​ 使用USER_IND_COLUMNS查询某个TABLE中的相应字段索引建立情况

​ 使用DBA_INDEXES/USER_INDEXES查询所有索引的具体设置情况。

Oracle中的索引类型

​ 在Oracle中的索引可以分为:B树索引、位图索引、反向键索引、基于函数的索引、簇索引、全局索引、局部索引、HASH索引、降序索引等,下面逐一讲解:

阅读全文 »

Spring的事务管理

发表于 2018-10-27 | 分类于 Java

事务管理对于企业应用来说是至关重要的,即使出现异常情况,它也可以保证数据的一致性。
Spring Framework对事务管理提供了一致的抽象,其特点如下:

  • 为不同的事务API提供一致的编程模型,比如JTA(Java Transaction API), JDBC, Hibernate, JPA(Java Persistence API和JDO(Java Data Objects)

  • 支持声明式事务管理,特别是基于注解的声明式事务管理,简单易用

  • 提供比其他事务API如JTA更简单的编程式事务管理API

  • 与spring数据访问抽象的完美集成

事务的基本原理

Spring事务的本质其实就是数据库对事务的支持,没有数据库的事务支持,spring是无法提供事务功能的。对于纯JDBC操作数据库,想要用到事务,可以按照以下步骤进行:

  1. 获取连接 Connection con = DriverManager.getConnection()

  2. 开启事务con.setAutoCommit(true/false)

  3. 执行CRUD

  4. 提交事务/回滚事务 con.commit() / con.rollback()

  5. 关闭连接 conn.close()

事务管理方式

spring支持编程式事务管理和声明式事务管理两种方式。

编程式事务管理使用TransactionTemplate或者直接使用底层的PlatformTransactionManager。对于编程式事务管理,spring推荐使用TransactionTemplate。

声明式事务管理建立在AOP之上的。其本质是对方法前后进行拦截,然后在目标方法开始之前创建或者加入一个事务,在执行完目标方法之后根据执行情况提交或者回滚事务。声明式事务最大的优点就是不需要通过编程的方式管理事务,这样就不需要在业务逻辑代码中掺杂事务管理的代码,只需在配置文件中做相关的事务规则声明(或通过基于@Transactional注解的方式),便可以将事务规则应用到业务逻辑中。

显然声明式事务管理要优于编程式事务管理,这正是spring倡导的非侵入式的开发方式。声明式事务管理使业务代码不受污染,一个普通的POJO对象,只要加上注解就可以获得完全的事务支持。和编程式事务相比,声明式事务唯一不足地方是,后者的最细粒度只能作用到方法级别,无法做到像编程式事务那样可以作用到代码块级别。但是即便有这样的需求,也存在很多变通的方法,比如,可以将需要进行事务管理的代码块独立为方法等等。

声明式事务管理也有两种常用的方式,一种是基于tx和aop名字空间的xml配置文件,另一种就是基于@Transactional注解。显然基于注解的方式更简单易用,更清爽。

自动提交(AutoCommit)与连接关闭时的是否自动提交

自动提交

默认情况下,数据库处于自动提交模式。每一条语句处于一个单独的事务中,在这条语句执行完毕时,如果执行成功则隐式的提交事务,如果
执行失败则隐式的回滚事务。

对于正常的事务管理,是一组相关的操作处于一个事务之中,因此必须关闭数据库的自动提交模式。不过,这个我们不用担心,spring会将底层连接的自动提交特性设置为false。

1
2
3
4
5
6
7
8
9
10
 1 // switch to manual commit if necessary. this is very expensive in some jdbc drivers,
2 // so we don't want to do it unnecessarily (for example if we've explicitly
3 // configured the connection pool to set it already).
4 if (con.getautocommit()) {
5 txobject.setmustrestoreautocommit(true);
6 if (logger.isdebugenabled()) {
7 logger.debug("switching jdbc connection [" + con + "] to manual commit");
8 }
9 con.setautocommit(false);
10 }

有些数据连接池提供了关闭事务自动提交的设置,最好在设置连接池时就将其关闭。但C3P0没有提供这一特性,只能依靠spring来设置。
因为JDBC规范规定,当连接对象建立时应该处于自动提交模式,这是跨DBMS的缺省值,如果需要,必须显式的关闭自动提交。C3P0遵守这一规范,让客户代码来显式的设置需要的提交模式。

连接关闭时的是否自动提交

当一个连接关闭时,如果有未提交的事务应该如何处理?JDBC规范没有提及,C3P0默认的策略是回滚任何未提交的事务。这是一个正确的策略,但JDBC驱动提供商之间对此问题并没有达成一致。
C3P0的autoCommitOnClose属性默认是false,没有十分必要不要动它。或者可以显式的设置此属性为false,这样会更明确。

基于注解的声明式事务管理配置

XML配置文件

1
2
3
4
5
6
7
1 <!-- transaction support-->
2 <!-- PlatformTransactionMnager -->
3 <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
4 <property name="dataSource" ref="dataSource" />
5 </bean>
6 <!-- enable transaction annotation support -->
7 <tx:annotation-driven transaction-manager="txManager" />

还要在xml配置文件中添加tx名字空间

1
2
3
4
5
6
7
8
9
10
11
12
 1 ...
2 xmlns:tx="http://www.springframework.org/schema/tx"
3 xmlns:aop="http://www.springframework.org/schema/aop"
4 xsi:schemaLocation="
5 ...
6
7 http://www.springframework.org/schema/tx
8
9
10 http://www.springframework.org/schema/tx/spring-tx.xsd
11
12 ...

MyBatis自动参与到spring事务管理中,无需额外配置,只要org.mybatis.spring.SqlSessionFactoryBean引用的数据源与DataSourceTransactionManager引用的数据源一致即可,否则事务管理会不起作用。

spring事务特性

spring所有的事务管理策略类都继承自org.springframework.transaction.PlatformTransactionManager接口。

1
2
3
4
5
6
7
8
9
1 public interface PlatformTransactionManager {
2
3 TransactionStatus getTransaction(TransactionDefinition definition)
4 throws TransactionException;
5
6 void commit(TransactionStatus status) throws TransactionException;
7
8 void rollback(TransactionStatus status) throws TransactionException;
9 }

其中TransactionDefinition接口定义以下特性:

事务隔离级别
阅读全文 »

设计模式之代理模式

发表于 2018-10-27 | 分类于 设计模式

什么是代理模式?

日常生活中我们经常会碰到代理模式,例如我们找房产中介帮我们介绍房子,找婚姻中介帮我们介绍对象,找保洁帮我们打理房间,找律师帮我们进行诉讼等。我们在无形中都运用到了代理模式。

为什么要使用代理?

运用代理可以使我们的生活更加便利,有了代理,我们不需要自己去找房子,不需要自己去找对象,不需要自己去打理房间,不需要自己去诉讼。当然,你也可以选择一切都自己来干,但是存在前提条件,一是你是否都具备这样的资源和能力来做这些事情,二是你是否愿意花费这么多精力和时间来做这些事情。总之,代理模式使我们各专其事,我们可以将时间浪费在美好的事情上,而不用天天被一些琐事所羁绊。

代理模式有哪些实现?

代理的实现分动态代理和静态代理,静态代理的实现是对已经生成了的JAVA类进行封装。

动态代理则是在运行时生成了相关代理类,在JAVA中生成动态代理一般有三种方式。

静态代理

- 由程序员创建或特定工具自动生成源代码再对其编译。在程序运行前代理类的.class文件就已经存在了。

动态代理

- 在程序运行时运用反射机制动态创建而成。

静态代理

代理接口:UserDao.java

1
2
3
public interface UserDao {
void save();
}

目标对象:UserDaoImpl.java

1
2
3
4
5
6
public class UserDaoImpl implements UserDao {
@Override
public void save() {
System.out.println("正在保存用户信息。。。");
}
}

代理对象:TransactionHandler.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class TransactionHandler implements UserDao {

private UserDaoImpl target;

public TransactionHandler(UserDaoImpl target){
this.target = target;
}

@Override
public void save() {
System.out.println("开启事务控制。。。");
target.save();
System.out.println("关闭事务控制。。。");
}

public static void main(String[] args) {
UserDaoImpl target = new UserDaoImpl();
UserDao userDao = new TransactionHandler(target);
userDao.save();
}
}

总的来说静态代理实现简单也容易理解,但是静态代理不能使一个代理类反复作用于多个目标对象,代理对象直接持有目标对象的引用,这导致代理对象和目标对象类型紧密耦合了在一起。如果UserDao接口下还有另一个实现类也需要进行事务控制,那么就要重新写一个代理类,这样就会产生许多重复的模版代码,不能达到代码复用的目的。而动态代理就可以很好的解决这样的问题。

动态代理

Spring AOP的拦截功能是由java中的动态代理来实现的。说白了,就是在目标类的基础上增加切面逻辑,生成增强的目标类(该切面逻辑或者在目标类函数执行之前,或者目标类函数执行之后,或者在目标类函数抛出异常时候执行。不同的切入时机对应不同的Interceptor的种类,如BeforeAdviseInterceptor,AfterAdviseInterceptor以及ThrowsAdviseInterceptor等)。

Spring AOP的源码中用到了两种动态代理来实现拦截切入功能:jdk动态代理和cglib动态代理(核心)。两种方法同时存在,各有优劣。jdk动态代理是由java内部的反射机制来实现的,cglib动态代理底层则是借助asm来实现的。总的来说,反射机制在生成类的过程中比较高效,而asm在生成类之后的相关执行过程中比较高效(可以通过将asm生成的类进行缓存,这样解决asm生成类过程低效问题)。还有一点必须注意:jdk动态代理的应用前提,必须是目标类基于统一的接口。如果没有上述前提,jdk动态代理不能应用。由此可以看出,jdk动态代理有一定的局限性,cglib这种第三方类库实现的动态代理应用更加广泛,且在效率上更有优势。

这里仍然使用上面同样的代理接口和目标对象。

阅读全文 »

Spring中的常用注解

发表于 2018-10-25 | 分类于 Spring

注解本身没有功能的,就和xml一样。注解和xml都是一种元数据,元数据即解释数据的数据,这就是所谓配置。

本文主要罗列Spring|SpringMVC相关注解的简介。

Spring部分

声明bean的注解

@Component 组件,没有明确的角色

@Service 在业务逻辑层使用(service层)

@Repository 在数据访问层使用(dao层)

@Controller 在展现层使用,控制器的声明(C)

注入bean的注解

@Autowired:由Spring提供

@Inject:由JSR-330提供

@Resource:由JSR-250提供

都可以注解在set方法和属性上,推荐注解在属性上(一目了然,少写代码)。

java配置类相关注解

@Configuration 声明当前类为配置类,相当于xml形式的Spring配置(类上)

@Bean 注解在方法上,声明当前方法的返回值为一个bean,替代xml中的方式(方法上)

@Configuration 声明当前类为配置类,其中内部组合了@Component注解,表明这个类是一个bean(类上)

@ComponentScan 用于对Component进行扫描,相当于xml中的(类上)

@WishlyConfiguration 为@Configuration与@ComponentScan的组合注解,可以替代这两个注解

切面(AOP)相关注解

Spring支持AspectJ的注解式切面编程。

@Aspect 声明一个切面(类上)
使用@After、@Before、@Around定义建言(advice),可直接将拦截规则(切点)作为参数。

@After 在方法执行之后执行(方法上)
@Before 在方法执行之前执行(方法上)
@Around 在方法执行之前与之后执行(方法上)

@PointCut 声明切点

在java配置类中使用@EnableAspectJAutoProxy注解开启Spring对AspectJ代理的支持(类上)

@Bean的属性支持

@Scope 设置Spring容器如何新建Bean实例(方法上,得有@Bean)
其设置类型包括:

Singleton (单例,一个Spring容器中只有一个bean实例,默认模式),
Protetype (每次调用新建一个bean),
Request (web项目中,给每个http request新建一个bean),
Session (web项目中,给每个http session新建一个bean),
GlobalSession(给每一个 global http session新建一个Bean实例)

@StepScope 在Spring Batch中还有涉及

@PostConstruct 由JSR-250提供,在构造函数执行完之后执行,等价于xml配置文件中bean的initMethod

@PreDestory 由JSR-250提供,在Bean销毁之前执行,等价于xml配置文件中bean的destroyMethod

@Value注解

@Value 为属性注入值(属性上)
支持如下方式的注入:
》注入普通字符

1
2
@Value("Michael Jackson")
String name;

》注入操作系统属性

1
2
@Value("#{systemProperties['os.name']}")
String osName;

》注入表达式结果

1
2
@Value("#{ T(java.lang.Math).random() * 100 }")
String randomNumber;

》注入其它bean属性

1
2
@Value("#{domeClass.name}")
String name;

》注入文件资源

1
2
@Value("classpath:com/hgs/hello/test.txt")
Resource file;

》注入网站资源

1
2
@Value("http://www.cznovel.com")
Resource url;

》注入配置文件

1
2
@Value("${book.name}")
String bookName;

》注入配置使用方法:
① 编写配置文件(test.properties)

1
book.name=《三体》

② @PropertySource 加载配置文件(类上)

1
@PropertySource("classpath:com/hgs/hello/test/test.propertie")

③ 还需配置一个PropertySourcesPlaceholderConfigurer的bean。

阅读全文 »

concurrentHashmap的设计艺术

发表于 2018-10-24 | 分类于 多线程

concurrentHashmap是Java工程师接触最多的关于并发和线程安全的类,本篇从jdk1.6, jdk1.7, jdk1.8三个版本的实现来详细分析其设计艺术。

结构原型

首先简单从整体把握concurrentHashmap的结构原理。

JDK1.6和JDK1.7版本

该版本的ConcurrentHashMap结构:每一个segment都是一个HashEntry[] table, table中的每一个元素本质上都是一个HashEntry的单向队列(单向链表实现)。每一个segment都是一个HashEntry[] table, table中的每一个元素本质上都是一个HashEntry的单向队列。

JDK1.8版本

该版本的ConcurrentHashMap结构:放弃了Segment的概念,而是直接用Node数组+链表+红黑树的数据结构来实现,并发控制使用Synchronized和CAS来操作,整个看起来就像是优化过且线程安全的HashMap。其中Node数组里面的元素包括TreeBin,ForwardingNode ,Node 。因为放弃了Segment,所以具体并发级别可以认为是hash桶是数量,也就是容量,会随扩容而改变,不再是固定值。

构造函数

名词解释

initialCapacity:初始化容量,指的是所有Segment中的hash桶的数量和,默认16。

concurrencyLevel:最大并发级别,也是数组segments的最大长度,初始化之后就不会改变,实际扩容的是每个segment里的hash桶,默认16。

loadFactor:每个Segment的加载因子,当个数大于threshold=hash桶数量*loadFactor时候会扩容。

Unsafe:这是jdk1.7,1.8常用的机制,可以看这篇文章。

1
2
3
4
5
6
7
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
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
<------------------------------jdk1.7版本-------------------------------------->
private static final sun.misc.Unsafe UNSAFE;
// Segment数组第一个元素的地址相对于该对象实例起始地址的偏移量
private static final long SBASE;
// Segment数组中元素element基本类型大小的移位量,比如我们要在i位置加入元素,
//用unsafe.putOrderedObject(segments, (i << SSHIFT) + SBASE, element);
private static final int SSHIFT;
//HashEntry数组第一个元素的地址相对于该对象实例起始地址的偏移量
private static final long TBASE;
//HashEntry数组中元素基本类型大小的移位量
private static final int TSHIFT;
// ConcurrentHashMap属性hashSeed,segmentShift,segmentMask,segments的相对实例地址的偏移量
private static final long HASHSEED_OFFSET;
private static final long SEGSHIFT_OFFSET;
private static final long SEGMASK_OFFSET;
private static final long SEGMENTS_OFFSET;

static {
int ss, ts;
try {
UNSAFE = sun.misc.Unsafe.getUnsafe();
Class tc = HashEntry[].class;
Class sc = Segment[].class;
//获取数组中第一个元素的偏移量(get offset of a first element in the array)
TBASE = UNSAFE.arrayBaseOffset(tc);
SBASE = UNSAFE.arrayBaseOffset(sc);
//获取数组中一个元素的大小(get size of an element in the array)
ts = UNSAFE.arrayIndexScale(tc);
ss = UNSAFE.arrayIndexScale(sc);
//其中hashSeed = randomHashSeed(this),随机一个hashSeed,用于key的hash计算
HASHSEED_OFFSET = UNSAFE.objectFieldOffset(ConcurrentHashMap.class.getDeclaredField("hashSeed"));
SEGSHIFT_OFFSET = UNSAFE.objectFieldOffset(ConcurrentHashMap.class.getDeclaredField("segmentShift"));
SEGMASK_OFFSET = UNSAFE.objectFieldOffset(ConcurrentHashMap.class.getDeclaredField("segmentMask"));
SEGMENTS_OFFSET = UNSAFE.objectFieldOffset(ConcurrentHashMap.class.getDeclaredField("segments"));
} catch (Exception e) {
throw new Error(e);
}
if ((ss & (ss-1)) != 0 || (ts & (ts-1)) != 0)
throw new Error("data type scale not a power of two");
SSHIFT = 31 - Integer.numberOfLeadingZeros(ss);
TSHIFT = 31 - Integer.numberOfLeadingZeros(ts);
}
<---------------------------------jdk1.8版本--------------------------------->
// Unsafe mechanics
private static final sun.misc.Unsafe U;
private static final long SIZECTL;
private static final long TRANSFERINDEX;
private static final long BASECOUNT;
private static final long CELLSBUSY;
private static final long CELLVALUE;
private static final long ABASE;
private static final int ASHIFT;

static {
try {
U = sun.misc.Unsafe.getUnsafe();
Class<?> k = ConcurrentHashMap.class;
SIZECTL = U.objectFieldOffset
(k.getDeclaredField("sizeCtl"));
TRANSFERINDEX = U.objectFieldOffset
(k.getDeclaredField("transferIndex"));
BASECOUNT = U.objectFieldOffset
(k.getDeclaredField("baseCount"));
CELLSBUSY = U.objectFieldOffset
(k.getDeclaredField("cellsBusy"));
Class<?> ck = CounterCell.class;
CELLVALUE = U.objectFieldOffset
(ck.getDeclaredField("value"));
Class<?> ak = Node[].class;
ABASE = U.arrayBaseOffset(ak);
int scale = U.arrayIndexScale(ak);
if ((scale & (scale - 1)) != 0)
throw new Error("data type scale not a power of two");
ASHIFT = 31 - Integer.numberOfLeadingZeros(scale);
} catch (Exception e) {
throw new Error(e);
}
}

sizeCtl: java1.8版本特有的,用于数组的初始化与扩容控制;当sizeCtl = -1 时,表明table正在初始化;当sizeCtl = 0时,表明用了无参构造方法,没有指定初始容量默认值;当sizeCtl > 0时,表明用了传参构造方法,指定了初始化容量值;当sizeCtl = -(1+ N),表明在扩容, 其中N的低RESIZE_STAMP_SHIFT位表示参与扩容线程数。

JDK1.6版本
1
2
3
4
5
6
7
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
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
public ConcurrentHashMap(int initialCapacity, float loadFactor, int concurrencyLevel) {
//默认loadFactor=DEFAULT_LOAD_FACTOR=0.75f
if (!(loadFactor > 0) || initialCapacity < 0 || concurrencyLevel <= 0)
throw new IllegalArgumentException();
//默认concurrencyLevel=DEFAULT_CONCURRENCY_LEVEL=16,
//MAX_SEGMENTS =1<<16即数组segments的最大长度,也是最大并发级别;
if (concurrencyLevel > MAX_SEGMENTS)
concurrencyLevel = MAX_SEGMENTS;

//保证concurrencyLevel是2^n,默认ssize=16;
int sshift = 0;
int ssize = 1;
while (ssize < concurrencyLevel) {
++sshift;
ssize <<= 1;
}
//定位Segment的index时hash值的移位,默认时候sshift=4,segmentShift=32-4=28;
segmentShift = 32 - sshift;
//用于& 运算定位Segment的index,默认构造时是0x0f(十进制也就是15)
segmentMask = ssize - 1;
this.segments = Segment.newArray(ssize);

//默认initialCapacity=DEFAULT_INITIAL_CAPACITY=16
//MAXIMUM_CAPACITY=1<<30
if (initialCapacity > MAXIMUM_CAPACITY)
initialCapacity = MAXIMUM_CAPACITY;
//确定每个segment初始化时候有多少个hash桶,并保证为2^n,默认cap = 1,但是之后每个segment构造完成后这个cap值就没用了。
int c = initialCapacity / ssize;
if (c * ssize < initialCapacity)
++c;
int cap = 1;
while (cap < c)
cap <<= 1;
//确定cap后开始初始化每个segment的table
for (int i = 0; i < this.segments.length; ++i)
this.segments[i] = new Segment< K,V > (cap, loadFactor);
}

public ConcurrentHashMap(int initialCapacity, float loadFactor) {
this(initialCapacity, loadFactor, DEFAULT_CONCURRENCY_LEVEL);
}

public ConcurrentHashMap(int initialCapacity) {
this(initialCapacity, DEFAULT_LOAD_FACTOR, DEFAULT_CONCURRENCY_LEVEL);
}

public ConcurrentHashMap() {
this(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR, DEFAULT_CONCURRENCY_LEVEL);
}

public ConcurrentHashMap(Map<? extends K, ? extends V> m) {
this(Math.max((int) (m.size() / DEFAULT_LOAD_FACTOR) + 1, DEFAULT_INITIAL_CAPACITY),
DEFAULT_LOAD_FACTOR, DEFAULT_CONCURRENCY_LEVEL);
putAll(m);
}
JDK1.7版本
1
2
3
4
5
6
7
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
public ConcurrentHashMap(int initialCapacity, float loadFactor, int concurrencyLevel) {  
if (!(loadFactor > 0) || initialCapacity < 0 || concurrencyLevel <= 0)
throw new IllegalArgumentException();
if (concurrencyLevel > MAX_SEGMENTS)
concurrencyLevel = MAX_SEGMENTS;
// Find power-of-two sizes best matching arguments
int sshift = 0;
int ssize = 1;
while (ssize < concurrencyLevel) {
++sshift;
ssize <<= 1;
}
this.segmentShift = 32 - sshift;
this.segmentMask = ssize - 1;
if (initialCapacity > MAXIMUM_CAPACITY)
initialCapacity = MAXIMUM_CAPACITY;
int c = initialCapacity / ssize;
if (c * ssize < initialCapacity)
++c;
//cap = MIN_SEGMENT_TABLE_CAPACITY = 2,即每个segment容量最小初始化为2,
//而jdk1.6默认cap=1
int cap = MIN_SEGMENT_TABLE_CAPACITY;
while (cap < c)
cap <<= 1;
//与jdk1.6不同点,这里只是构造一个segments和segments[0],懒加载
Segment<K,V> s0 = new Segment<K,V>(loadFactor, (int)(cap * loadFactor), (HashEntry<K,V>[])new HashEntry[cap]);
Segment<K,V>[] ss = (Segment<K,V>[])new Segment[ssize];
//这里用UNSAFE给数组ss第一个元素赋值,内存非立即可见
UNSAFE.putOrderedObject(ss, SBASE, s0);
this.segments = ss;
}
// 其余的几个同上
阅读全文 »
1…131415…20
Morning Star

Morning Star

100 日志
14 分类
37 标签
GitHub
© 2021 Morning Star
由 Hexo 强力驱动
|
主题 — NexT.Pisces v5.1.4