前言
上一篇文章《Java并发源码之ReentrantLock(一)》中,对ReentrantLock
的结构、作用、主要成员变量、主要方法等做了详细分析说明。本文将对主要的lock
、unlock
方法进行分析,分别包括公平锁和非公平锁。
戒急用忍
上一篇文章《Java并发源码之ReentrantLock(一)》中,对ReentrantLock
的结构、作用、主要成员变量、主要方法等做了详细分析说明。本文将对主要的lock
、unlock
方法进行分析,分别包括公平锁和非公平锁。
ReentrantLock
是一个可重入的互斥锁,与使用synchronized
方法和语句访问的隐式监视锁具有相同的基本行为和语义,但具有扩展功能。ReentrantLock
属于最后一个成功加锁并且还没有释放锁的线程。当一个线程请求lock
时,如果锁不属于任何线程,将立马得到这个锁;如果锁已经被当前线程拥有,当前线程会立即返回。
下面这个图是与ReentrantLock
相关的UML类图,可以看到ReentrantLock
实现了Lock
和Serializable
接口,表示它实现了lock()
、unlock()
等Lock
的接口方法,并且是一个可以序列化的类。ReentrantLock
主要成员变量为Sync
,Sync
是一个抽象类,继承了AbstractQueuedSynchronizer
(简称AQS
),AQS
提供了一个基于FIFO队列,可以用于构建锁或者其他相关同步装置的基础框架,本文不详细介绍。而Sync
有两个实现类:NonfairSync
和FairSync
,分别代表非公平锁和公平锁。也就是说ReentrantLock
中所有的锁操作都是由sync
这个成员变量完成的。
似乎我们在工作中很少用到LinkedList
,大部分都是使用ArrayList
。先来看一下LinkedList
的定义吧,它是List
和Deque
接口的双向链表实现;实现所有可选列表操作,并允许所有元素(包括null);所有的操作都实在操作双向列表;索引到列表中的操作将从开始或结束遍历列表,以更接近指定的索引为准。从这个定义来看,这个LinkedList
更适合较多插入和删除元素,较少读取元素,因为插入和删除元素不涉及重排数据。
至于Deque
是什么。Deque
是支持两端元素插入和移除的线性集合,而名称deque
是双端队列(double ended queue)
的缩写,通常发音为deck
。既然它是个列表,那它确实继承了Queue
,也支持所有与Queue
相关的进
、出
方法。本文不再对deque
做详细说明。
下面是LinkedList
的主要成员变量。可以看到包含头指针和尾指针,而Node
也是双向链表节点的表示,也就是说LinkedList
真的是双向链表表示的。
|
|
相信ArrayList
是小伙伴们在Java开发中经常会使用的类之一。那么ArrayList
究竟是一个什么类呢。大概来说它是List
接口的可调整大小的数组实现。 实现所有可选列表操作,并允许所有元素,包括null。除了实现List
接口之外,他的类还提供了一些方法来操纵数组的内部大小来存储列表。 也就是说ArrayList
内部是由数组实现,往ArrayList
中放置元素也是默认按照先后顺序进行放置;它的大小是可调整的,不用像使用数组一样担心它不够存放数据(这里排除内存限制)。
总的来说ArrayList
不论从使用还是理解上都是比较简单的,那么让我们来分析一下其中的一些关键方法吧。
下面列出了ArrayList
的关键成员变量,数量并不多,可以看到元素在ArrayList
中是存储在数组中的。
You are given an integer array sorted in ascending order (may contain duplicates), you need to split them into several subsequences, where each subsequences consist of at least 3 consecutive integers. Return whether you can make such a split.
Example 1:
Input: [1,2,3,3,4,5]
Output: True
Explanation:
You can split them into two consecutive subsequences :
1, 2, 3
3, 4, 5
Example 2:
Input: [1,2,3,3,4,4,5,5]
Output: True
Explanation:
You can split them into two consecutive subsequences :
1, 2, 3, 4, 5
3, 4, 5
Example 3:
Input: [1,2,3,4,4,5]
Output: False
Note:
The length of the input is in range of [1, 10000]
给一个数组,数组中的数字按顺序排列并且存在重复数字,需要将这个数组拆分为几个子数组。子数组的至少包含3个连续数字。返回是否可以满足这样的拆分。
You are given an integer array sorted in ascending order (may contain duplicates), you need to split them into several subsequences, where each subsequences consist of at least 3 consecutive integers. Return whether you can make such a split.
Example 1:
Input: [1,2,3,3,4,5]
Output: True
Explanation:
You can split them into two consecutive subsequences :
1, 2, 3
3, 4, 5
Example 2:
Input: [1,2,3,3,4,4,5,5]
Output: True
Explanation:
You can split them into two consecutive subsequences :
1, 2, 3, 4, 5
3, 4, 5
Example 3:
Input: [1,2,3,4,4,5]
Output: False
Note:
The length of the input is in range of [1, 10000]
给一个数组,数组中的数字按顺序排列并且存在重复数字,需要将这个数组拆分为几个子数组。子数组的至少包含3个连续数字。返回是否可以满足这样的拆分。
In the world of Dota2, there are two parties: the Radiant
and the Dire
.
The Dota2 senate consists of senators coming from two parties. Now the senate wants to make a decision about a change in the Dota2 game. The voting for this change is a round-based procedure. In each round, each senator can exercise one
of the two rights:
Ban one senator's right
:Announce the victory
:Given a string representing each senator’s party belonging. The character ‘R’ and ‘D’ represent the Radiant
party and the Dire
party respectively. Then if there are n senators, the size of the given string will be n
.
The round-based procedure starts from the first senator to the last senator in the given order. This procedure will last until the end of voting. All the senators who have lost their rights will be skipped during the procedure.
Suppose every senator is smart enough and will play the best strategy for his own party, you need to predict which party will finally announce the victory and make the change in the Dota2 game. The output should be Radiant
or Dire
.
Example 1:
Input: “RD”
Output: “Radiant”
Explanation: The first senator comes from Radiant and he can just ban the next senator’s right in the round 1.
And the second senator can’t exercise any rights any more since his right has been banned.
And in the round 2, the first senator can just announce the victory since he is the only guy in the senate who can vote.
Example 2:
Input: “RDD”
Output: “Dire”
Explanation:
The first senator comes from Radiant and he can just ban the next senator’s right in the round 1.
And the second senator can’t exercise any rights anymore since his right has been banned.
And the third senator comes from Dire and he can ban the first senator’s right in the round 1.
And in the round 2, the third senator can just announce the victory since he is the only guy in the senate who can vote.
Note:
The length of the given string will in the range [1, 10,000].
Dota2有两个政党,光明党与黑暗党(好扯)。参议院的参议员也都属于这两个政党。现在参议员们决定要对Data2做出改变,当然每个政党希望的改变是不一样的,需要投票决定。投票是一个回合制程序。在每一轮中,每个参议员都可以行使两种权利之一。一种权利是让另一个政党的参议员在这一轮以及以后的每一轮没有任何权利;另一种是在参议会中只剩一个政党的参议员有权利的时候代表这个政党的胜利。
给出一个代表每个参议员党派归属的字符串,只包含两种字母,R
和D
,R
代表Radiant
,D
代表Dire
,如果有n
个参议员那么字符串的长度就为n
。
回合制程序从第一个参议员开始,到最后一个指定的参议员。这个程序将持续到投票结束。所有失去权利的参议员都将在程序中被跳过。假设每个参议员是足够聪明,会为自己的当事人发挥最好的策略,你需要预测哪一方将最终宣布胜利并在DOTA2游戏的变化。输出应该是辐射或可怕的。
相信同学们在Coding(搬砖)的过程中经常会用到HashMap,那么HashMap到底是个什么东东呢?
以JDK8来讲,HashMap继承Map接口,在Map接口中描述Map是“An object that maps keys to values. A map cannot contain duplicate keys each key can map to at most one value.” 也就是说Map是一个映射键值的对象。映射中不能包含重复键,并且每个键可以映射到最多一个值
。HashMap实现了Map接口,并且为基本操作(如:get、put)提供了常量时间的性能。
下面的代码注释里详细的说明了HashMap的几个重要的成员变量。从这几个变量中可以分析得出,所有的键值对都存放在table
变量中,而table
变量的大小不是固定不变的,是在动态变化的,而且大小始终为2的幂。
|
|
从上面的代码总可以看到,table
属性的类型为Node
,这个Node
实现了Map.Entry
接口,所以本质上还是Map.Entry
。另外,HashMap包含另外一种TreeNode
,TreeNode
本身为红黑树结构。在HashMap中,当一个普通Node(Plain Node)的长度大于一定值时会转换为TreeNode
,而TreeNode
长度小于一定值时会转换普通Node。
从下面的代码中可以看到,Node
实现了Map.Entry
接口,并且本身为链表结构,这样是为了处理不同键值可能会出现相同Hash的情况。
|
|
从这个类的名字Unsafe
上来说这个类就是一个不安全的类,也是不开放给用户直接使用的(当然我们还是可以通过其他一些方法用到)。
这个类在jdk源码中多个类中用到,主要作用是任意内存地址位置处读写数据,外加一下CAS操作。它的大部分操作都是绕过JVM通过JNI完成的,因此它所分配的内存需要手动free,所以是非常危险的。但是Unsafe中很多(但不是所有)方法都很有用,且有些情况下,除了使用JNI,没有其他方法弄够完成同样的事情。
至于研究它的起因,是因为我最近在看jdk8的ConcurrentHashMap,这个版本的主要函数就是用过Unsafe来完成的。
大家都知道Java中String是不可变类。也就是说如果有一个String初始化的值位”Hello”,如果把它赋值为”Hello World!”,那么不是在原内存地址上修改数据,而是重新生成了一个新对象并重新指向这个对象。而在Java虚拟机中,字符串是直接放在常量池中的,而且关于String比较相关的题目很多,这篇文章不做详细描述。
下面的代码会生成几个String,先不要着急的往下看答案,自己想想。
这个问题主要还是考察了Java虚拟机对String中”+”运算符的特殊处理,我们来看一下编译生成的class是什么样的。见下图。
看到String HelloWorld!
是不是很惊讶,是的,这段代码只生成了一个字符串。