Merge branch 'master' of https://github.com/xiongraorao/Interview-Notebook
31
code/src/main/java/com/raorao/Test.java
Normal file
@ -0,0 +1,31 @@
|
||||
package com.raorao;
|
||||
|
||||
/**
|
||||
* .
|
||||
*
|
||||
* @author Xiong Raorao
|
||||
* @since 2018-08-02-16:46
|
||||
*/
|
||||
public class Test {
|
||||
|
||||
private int a;
|
||||
|
||||
public static void main(String[] args) {
|
||||
//System.out.println(foo());
|
||||
int r = new Test().foo();
|
||||
System.out.println(r);
|
||||
}
|
||||
|
||||
public int foo() {
|
||||
try {
|
||||
System.out.println("sadfasdfasdf");
|
||||
return a;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return 3;
|
||||
} finally {
|
||||
System.out.println("finally: " + ++a);
|
||||
return a;
|
||||
}
|
||||
}
|
||||
}
|
28
code/src/main/java/com/raorao/java/base/InnerClassTest.java
Normal file
@ -0,0 +1,28 @@
|
||||
package com.raorao.java.base;
|
||||
|
||||
/**
|
||||
* 内部类的引用.
|
||||
*
|
||||
* @author Xiong Raorao
|
||||
* @since 2018-08-06-14:43
|
||||
*/
|
||||
public class InnerClassTest {
|
||||
void f(){
|
||||
System.out.println("InnerClassTest.f()");
|
||||
}
|
||||
public class Inner{
|
||||
public InnerClassTest outer(){
|
||||
return InnerClassTest.this;
|
||||
}
|
||||
}
|
||||
|
||||
public Inner inner(){
|
||||
return new Inner();
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
InnerClassTest test = new InnerClassTest();
|
||||
InnerClassTest.Inner inner = test.new Inner();
|
||||
//inner = new InnerClassTest.Inner(); // 如果Inner 是static就可以,否则只能采用上面的语句
|
||||
}
|
||||
}
|
@ -0,0 +1,65 @@
|
||||
package com.raorao.java.thread;
|
||||
|
||||
import java.util.concurrent.locks.Condition;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
/**
|
||||
* 可重入锁测试.
|
||||
*
|
||||
* @author Xiong Raorao
|
||||
* @since 2018-08-06-10:27
|
||||
*/
|
||||
public class ReentrantLockTest {
|
||||
|
||||
private Lock lock = new ReentrantLock();
|
||||
private Condition conditionA = lock.newCondition();
|
||||
private Condition conditionB = lock.newCondition();
|
||||
|
||||
public static void main(String[] args) throws InterruptedException {
|
||||
ReentrantLockTest test = new ReentrantLockTest();
|
||||
new Thread(() -> test.testLock()).start();
|
||||
new Thread(() -> test.testLock()).start();
|
||||
|
||||
Thread t = new Thread(() -> test.awaitA());
|
||||
t.start();
|
||||
Thread.sleep(2000);
|
||||
test.signalA();
|
||||
|
||||
}
|
||||
|
||||
public void awaitA() {
|
||||
lock.lock();
|
||||
try {
|
||||
System.out.println("before awaitA at " + System.currentTimeMillis());
|
||||
conditionA.await(); // 在此之前必须获得锁,不然报错illegalMonitorStateException 错误
|
||||
System.out.println("after awaitA at " + System.currentTimeMillis());
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
lock.unlock();
|
||||
System.out.println(" 释放锁 awaitA ");
|
||||
}
|
||||
}
|
||||
|
||||
public void signalA() {
|
||||
lock.lock();
|
||||
try {
|
||||
System.out.println("signalA at " + System.currentTimeMillis());
|
||||
conditionA.signal(); // 在此之前必须获得锁,不然报错illegalMonitorStateException 错误
|
||||
System.out.println("signalA over at " + System.currentTimeMillis());
|
||||
} finally {
|
||||
lock.unlock();
|
||||
System.out.println(" 释放锁 signalA ");
|
||||
}
|
||||
}
|
||||
|
||||
public void testLock() {
|
||||
lock.lock();
|
||||
for (int i = 0; i < 5; i++) {
|
||||
System.out.print(i + " ");
|
||||
}
|
||||
System.out.println();
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
@ -0,0 +1,68 @@
|
||||
package com.raorao.java.thread;
|
||||
|
||||
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
||||
|
||||
/**
|
||||
* 读写锁测试.
|
||||
*
|
||||
* @author Xiong Raorao
|
||||
* @since 2018-08-06-11:48
|
||||
*/
|
||||
public class ReentrantReadWriteLockTest {
|
||||
|
||||
private ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
|
||||
|
||||
public void read(){
|
||||
lock.readLock().lock(); // 读锁
|
||||
System.out.println("获得读锁 " + Thread.currentThread().getName() + " " + System.currentTimeMillis());
|
||||
try {
|
||||
Thread.sleep(2000);
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}finally {
|
||||
lock.readLock().unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public void write(){
|
||||
lock.writeLock().lock(); // 写锁
|
||||
System.out.println("获得写锁 " + Thread.currentThread().getName() + " " + System.currentTimeMillis());
|
||||
try {
|
||||
Thread.sleep(2000);
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}finally {
|
||||
lock.writeLock().unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
// 1. 读读共享, A 和 B 同时获得锁
|
||||
ReentrantReadWriteLockTest test = new ReentrantReadWriteLockTest();
|
||||
Thread t1 = new Thread(()-> {
|
||||
Thread.currentThread().setName("A");
|
||||
test.read();
|
||||
});
|
||||
Thread t2 = new Thread(()-> {
|
||||
Thread.currentThread().setName("B");
|
||||
test.read();
|
||||
});
|
||||
t1.start();
|
||||
t2.start();
|
||||
|
||||
// 2. 写写互斥, D线程比C线程落后两秒执行
|
||||
t1 = new Thread(()->{
|
||||
Thread.currentThread().setName("C");
|
||||
test.write();
|
||||
});
|
||||
t2 = new Thread(()->{
|
||||
Thread.currentThread().setName("D");
|
||||
test.write();
|
||||
});
|
||||
t1.start();
|
||||
t2.start();
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright (c) 2018. Xiong Raorao. All rights reserved.
|
||||
* Project Name: book-notes
|
||||
* File Name: Test.java
|
||||
* Date: 18-7-25 下午5:32
|
||||
* Author: Xiong Raorao
|
||||
*/
|
||||
|
||||
package com.raorao.java.thread;
|
||||
|
||||
/**
|
||||
* .
|
||||
*
|
||||
* @author Xiong Raorao
|
||||
* @since 2018-07-25-17:32
|
||||
*/
|
||||
public class ThreadLocalTest {
|
||||
private static ThreadLocal<Integer> local = new ThreadLocal<>();
|
||||
public static void main(String[] args) throws InterruptedException {
|
||||
Thread t1 = new Thread(() -> {
|
||||
System.out.println(" I am t1");
|
||||
local.set(1);
|
||||
try {
|
||||
Thread.sleep(2000);
|
||||
System.out.println("t1 value: " + local.get());
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
});
|
||||
Thread.sleep(2000);
|
||||
Thread t2 = new Thread(() -> {
|
||||
System.out.println("I am t2");
|
||||
System.out.println("before set , I get " + local.get());
|
||||
try {
|
||||
Thread.sleep(2000);
|
||||
local.set(2);
|
||||
System.out.println("set after 2 s, I get " + local.get());
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
});
|
||||
t2.start();
|
||||
t1.start();// 无论怎么更换两个线程的启动顺序,得到的值是不一样的
|
||||
}
|
||||
}
|
36
code/src/main/java/com/raorao/java/thread/TimerTest.java
Normal file
@ -0,0 +1,36 @@
|
||||
package com.raorao.java.thread;
|
||||
|
||||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
|
||||
/**
|
||||
* 定时器.
|
||||
*
|
||||
* @author Xiong Raorao
|
||||
* @since 2018-08-06-13:07
|
||||
*/
|
||||
public class TimerTest {
|
||||
private static Timer timer = new Timer();
|
||||
|
||||
static class MyTask extends TimerTask{
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
System.out.println("运行时间: " + new Date());
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
MyTask task1 = new MyTask();
|
||||
try {
|
||||
Date taskDate = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse("2018-08-06 13:15:00");
|
||||
System.out.println("执行时间: " + taskDate.toLocaleString() + ",当前时间" + new Date().toLocaleString());
|
||||
timer.schedule(task1, taskDate);
|
||||
} catch (ParseException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
@ -28,11 +28,13 @@ Thoutworks | 内推 | | [内推链接](https://jinshuju.net/f/CcO2JA)
|
||||
顺丰科技 | 网申 | 即日 -- 9.23日 | [招聘官网](http://campus.sf-tech.com.cn/campusRecruitment/Default.html?p=28668990421) <br>7月20日投了内推
|
||||
多益网络 | 内推 | <li>内推笔试第一批:8.11 10:00 <li> 内推笔试第二批: 9.06 10:00 | [招聘官网](https://xz.duoyi.com/jobs/index.html?t=0) <br>7月20日投了内推
|
||||
好未来 | 提前批 | 8.19日截止 | [招聘官网](http://job.100tal.com/jobxq?jobId=510212759) <li> 7月24日投了
|
||||
华为 | 网申 | | [招聘官网](http://career.huawei.com/reccampportal/next/mini/index.html)
|
||||
腾讯 | 提前批/网申 | <li> 提前批:7.25-9.12 <li> 提前批:7.25-9.14 <li> 在线笔试:9.16-9.17 <li> 面试:9.26开始| [招聘官网](https://join.qq.com/)
|
||||
抖音、头条 | 内推 | 8.1 - 12.31| [招聘官网](https://job.bytedance.com/campus/) <li> 8.2 投简历
|
||||
携程 | 内推 | <li> 内推:8.2 - 8.12 <li> 网申 8.2 - 9.4 | [招聘官网](http://campus.ctrip.com)
|
||||
老虎证券 | 内推 | <li> 内推: 8.4 - 8.10 | <li>8.4 日已经提交内推
|
||||
贝壳网 | 内推 | | [招聘官网](http://campus.ke.com/)<li> 8.4已投简历
|
||||
美团 | 内推/网申 | 面试时间:9.6-9.14 | <li>8.6 已投简历
|
||||
|
||||
## 2. 面试记录
|
||||
|
||||
|
BIN
interview/java/img/TIM截图20180806153217.jpg
Normal file
After Width: | Height: | Size: 6.0 KiB |
BIN
interview/java/img/TIM截图20180806160547.jpg
Normal file
After Width: | Height: | Size: 13 KiB |
BIN
interview/java/img/TIM截图20180806160740.jpg
Normal file
After Width: | Height: | Size: 16 KiB |
BIN
interview/java/img/TIM截图20180806162223.jpg
Normal file
After Width: | Height: | Size: 14 KiB |
BIN
interview/java/img/TIM截图20180806162336.jpg
Normal file
After Width: | Height: | Size: 14 KiB |
BIN
interview/java/img/TIM截图20180806163031.jpg
Normal file
After Width: | Height: | Size: 8.5 KiB |
BIN
interview/java/img/TIM截图20180806164620.jpg
Normal file
After Width: | Height: | Size: 11 KiB |
BIN
interview/java/img/TIM截图20180806164709.jpg
Normal file
After Width: | Height: | Size: 12 KiB |
BIN
interview/java/img/TIM截图20180806171115.jpg
Normal file
After Width: | Height: | Size: 18 KiB |
BIN
interview/java/img/TIM截图20180806171310.jpg
Normal file
After Width: | Height: | Size: 12 KiB |
@ -16,6 +16,28 @@
|
||||
- [newCachedThreadPool](#newcachedthreadpool)
|
||||
- [Future 接口](#future-接口)
|
||||
- [ScheduledThreadPoolExecutor](#scheduledthreadpoolexecutor)
|
||||
- [ThreadLocal](#threadlocal)
|
||||
- [Lock](#lock)
|
||||
- [Synchronized](#synchronized)
|
||||
- [ReentrantLock](#reentrantlock)
|
||||
- [ReentranceLock 几个特殊的方法](#reentrancelock-几个特殊的方法)
|
||||
- [ReentranceReadWriteLock](#reentrancereadwritelock)
|
||||
- [condition](#condition)
|
||||
- [公平锁和非公平锁](#公平锁和非公平锁)
|
||||
- [Timer](#timer)
|
||||
- [shedule(TimerTask task, Date date)](#sheduletimertask-task-date-date)
|
||||
- [shedule 周期性执行](#shedule-周期性执行)
|
||||
- [sheduleAtFixedRate](#sheduleatfixedrate)
|
||||
- [Concurrent 容器](#concurrent-容器)
|
||||
- [BlockingQueue](#blockingqueue)
|
||||
- [DelayQueue](#delayqueue)
|
||||
- [PriorityBlockingQueue](#priorityblockingqueue)
|
||||
- [ArrayBlockingQueue 和 LinkedBlockingQueue](#arrayblockingqueue-和-linkedblockingqueue)
|
||||
- [ConcurrentHashMap](#concurrenthashmap)
|
||||
- [ConcurrentLinkedDeque 和 ConcurrentLinkedQueue](#concurrentlinkeddeque-和-concurrentlinkedqueue)
|
||||
- [Deque](#deque)
|
||||
- [LinkedList](#linkedlist)
|
||||
- [ArrayDeque](#arraydeque)
|
||||
- [参考文档](#参考文档)
|
||||
|
||||
<!-- /TOC -->
|
||||
@ -475,6 +497,547 @@ ScheduledThreadPoolExecutor 继承自 ThreadPoolExecutor, 主要用来在给定
|
||||
|
||||
该类采用了DelyQueue,封装了一个优先队列,该队列会对队列中的SheduledFutureTask 进行排序。time小的会排在前面,如果time相同,则会比较sequenceNumber, 就是说如果两个任务的执行时间相同,谁先提交就谁先执行
|
||||
|
||||
# ThreadLocal
|
||||
|
||||
变量值的共享可以采用public static 的类变量,但是在多线程情况下,static 类变量显然不能满足多线程的读写,因此采用ThreadLocal 变量来存储多线程下的变量的副本。
|
||||
|
||||
``` java
|
||||
/*
|
||||
* Copyright (c) 2018. Xiong Raorao. All rights reserved.
|
||||
* Project Name: book-notes
|
||||
* File Name: Test.java
|
||||
* Date: 18-7-25 下午5:32
|
||||
* Author: Xiong Raorao
|
||||
*/
|
||||
|
||||
package com.raorao.java.thread;
|
||||
|
||||
/**
|
||||
* .
|
||||
*
|
||||
* @author Xiong Raorao
|
||||
* @since 2018-07-25-17:32
|
||||
*/
|
||||
public class ThreadLocalTest {
|
||||
private static ThreadLocal<Integer> local = new ThreadLocal<>();
|
||||
public static void main(String[] args) throws InterruptedException {
|
||||
Thread t1 = new Thread(() -> {
|
||||
System.out.println(" I am t1");
|
||||
local.set(1);
|
||||
try {
|
||||
Thread.sleep(2000);
|
||||
System.out.println("t1 value: " + local.get());
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
});
|
||||
Thread.sleep(2000);
|
||||
Thread t2 = new Thread(() -> {
|
||||
System.out.println("I am t2");
|
||||
System.out.println("before set , I get " + local.get());
|
||||
try {
|
||||
Thread.sleep(2000);
|
||||
local.set(2);
|
||||
System.out.println("set after 2 s, I get " + local.get());
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
});
|
||||
t2.start();
|
||||
t1.start();// 无论怎么更换两个线程的启动顺序,得到的值是不一样的
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
# Lock
|
||||
|
||||
除了使用synchronized 关键字加锁同步之外,也可以实现Lock 来自定义同步过程。
|
||||
|
||||
## Synchronized
|
||||
|
||||
jvm提供的一种互斥同步锁的方式
|
||||
|
||||
synchronized 可以作用于代码块,方法,类方法,类等,锁加载的
|
||||
|
||||
## ReentrantLock
|
||||
|
||||
``` java
|
||||
public class LockExample {
|
||||
|
||||
private Lock lock = new ReentrantLock();
|
||||
|
||||
public void func() {
|
||||
lock.lock();
|
||||
try {
|
||||
for (int i = 0; i < 10; i++) {
|
||||
System.out.print(i + " ");
|
||||
}
|
||||
} finally {
|
||||
lock.unlock(); // 确保释放锁,从而避免发生死锁。
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
LockExample lockExample = new LockExample();
|
||||
ExecutorService executorService = Executors.newCachedThreadPool();
|
||||
executorService.execute(() -> lockExample.func());
|
||||
executorService.execute(() -> lockExample.func());
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9
|
||||
|
||||
**synchronized 和 ReentrantLock的比较:**
|
||||
|
||||
1. 锁的实现
|
||||
|
||||
synchronized 是 JVM 实现的,而 ReentrantLock 是 JDK 实现的。
|
||||
|
||||
2. 性能
|
||||
|
||||
新版本 Java 对 synchronized 进行了很多优化,例如自旋锁等,synchronized 与 ReentrantLock 大致相同。
|
||||
|
||||
3. 等待可中断
|
||||
|
||||
当持有锁的线程长期不释放锁的时候,正在等待的线程可以选择放弃等待,改为处理其他事情。
|
||||
|
||||
ReentrantLock 可中断,而 synchronized 不行。
|
||||
|
||||
4. 公平锁
|
||||
|
||||
公平锁是指多个线程在等待同一个锁时,必须按照申请锁的时间顺序来依次获得锁。
|
||||
|
||||
synchronized 中的锁是非公平的,ReentrantLock 默认情况下也是非公平的,但是也可以是公平的。
|
||||
|
||||
5. 锁绑定多个条件
|
||||
|
||||
一个 ReentrantLock 可以同时绑定多个 Condition 对象。
|
||||
|
||||
### ReentranceLock 几个特殊的方法
|
||||
|
||||
getHoldCount(): 查询当前线程保持此锁的个数
|
||||
getQueueLength(): 返回正在等待获取次锁定的估计线程数。比如有5个线程,其中1个线程执行await()方法,调用该方法就返回4
|
||||
getWaitQueueLength(): 返回等待与此锁定相关的给定条件Condition的线程估计数。
|
||||
|
||||
hasQueuedThread(Thread t): 查询指定线程是否正在等待获取此锁定
|
||||
hasQueuedThreads(): 查询是否有线程正在等待获取此锁定
|
||||
hasWaiters(Condition condition): 查询是否有线程正在等待与次锁定有关的condition条件
|
||||
|
||||
## ReentranceReadWriteLock
|
||||
|
||||
ReentranceLock 是完全互斥锁,同一时间,只有同一个线程在执行ReentrantLock.lock()后面的任务,虽然安全,但是效率低下。
|
||||
|
||||
ReentranceReadWriteLock(读写锁),读锁是共享锁,多个读锁之间不互斥,读锁和写锁之间互斥,写锁和写锁之间互斥。
|
||||
|
||||
``` java
|
||||
package com.raorao.java.thread;
|
||||
|
||||
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
||||
|
||||
/**
|
||||
* 读写锁测试.
|
||||
*
|
||||
* @author Xiong Raorao
|
||||
* @since 2018-08-06-11:48
|
||||
*/
|
||||
public class ReentrantReadWriteLockTest {
|
||||
|
||||
private ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
|
||||
|
||||
public void read(){
|
||||
lock.readLock().lock(); // 读锁
|
||||
System.out.println("获得读锁 " + Thread.currentThread().getName() + " " + System.currentTimeMillis());
|
||||
try {
|
||||
Thread.sleep(2000);
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}finally {
|
||||
lock.readLock().unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public void write(){
|
||||
lock.writeLock().lock(); // 写锁
|
||||
System.out.println("获得写锁 " + Thread.currentThread().getName() + " " + System.currentTimeMillis());
|
||||
try {
|
||||
Thread.sleep(2000);
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}finally {
|
||||
lock.writeLock().unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
// 1. 读读共享, A 和 B 同时获得锁
|
||||
ReentrantReadWriteLockTest test = new ReentrantReadWriteLockTest();
|
||||
Thread t1 = new Thread(()-> {
|
||||
Thread.currentThread().setName("A");
|
||||
test.read();
|
||||
});
|
||||
Thread t2 = new Thread(()-> {
|
||||
Thread.currentThread().setName("B");
|
||||
test.read();
|
||||
});
|
||||
t1.start();
|
||||
t2.start();
|
||||
|
||||
// 2. 写写互斥, D线程比C线程落后两秒执行
|
||||
t1 = new Thread(()->{
|
||||
Thread.currentThread().setName("C");
|
||||
test.write();
|
||||
});
|
||||
t2 = new Thread(()->{
|
||||
Thread.currentThread().setName("D");
|
||||
test.write();
|
||||
});
|
||||
t1.start();
|
||||
t2.start();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
读写和写读两个锁也是互斥的,这里就不测试了。
|
||||
|
||||
## condition
|
||||
|
||||
condition 可以用于实现线程wait 和 notify,主要的方法有await()、signal() 和 signalAll() 方法,对应Object 类的 wait(), notify() 和notifyAll()方法,区别是,**前者只会通知持有锁的在等待的对象,后者则是对所有等待的线程通知,效率低下**
|
||||
|
||||
``` java
|
||||
package com.raorao.java.thread;
|
||||
|
||||
import java.util.concurrent.locks.Condition;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
/**
|
||||
* 可重入锁测试.
|
||||
*
|
||||
* @author Xiong Raorao
|
||||
* @since 2018-08-06-10:27
|
||||
*/
|
||||
public class ReentrantLockTest {
|
||||
|
||||
private Lock lock = new ReentrantLock();
|
||||
private Condition conditionA = lock.newCondition();
|
||||
private Condition conditionB = lock.newCondition();
|
||||
|
||||
public static void main(String[] args) throws InterruptedException {
|
||||
ReentrantLockTest test = new ReentrantLockTest();
|
||||
new Thread(() -> test.testLock()).start();
|
||||
new Thread(() -> test.testLock()).start();
|
||||
|
||||
Thread t = new Thread(() -> test.awaitA());
|
||||
t.start();
|
||||
Thread.sleep(2000);
|
||||
test.signalA();
|
||||
|
||||
}
|
||||
|
||||
public void awaitA() {
|
||||
lock.lock();
|
||||
try {
|
||||
System.out.println("before awaitA at " + System.currentTimeMillis());
|
||||
conditionA.await(); // 在此之前必须获得锁,不然报错illegalMonitorStateException 错误
|
||||
System.out.println("after awaitA at " + System.currentTimeMillis());
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
lock.unlock();
|
||||
System.out.println(" 释放锁 awaitA ");
|
||||
}
|
||||
}
|
||||
|
||||
public void signalA() {
|
||||
lock.lock();
|
||||
try {
|
||||
System.out.println("signalA at " + System.currentTimeMillis());
|
||||
conditionA.signal(); // 在此之前必须获得锁,不然报错illegalMonitorStateException 错误
|
||||
System.out.println("signalA over at " + System.currentTimeMillis());
|
||||
} finally {
|
||||
lock.unlock();
|
||||
System.out.println(" 释放锁 signalA ");
|
||||
}
|
||||
}
|
||||
|
||||
public void testLock() {
|
||||
lock.lock();
|
||||
for (int i = 0; i < 5; i++) {
|
||||
System.out.print(i + " ");
|
||||
}
|
||||
System.out.println();
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
上面的程序对应的是利用Condition的进行等待和通知。
|
||||
|
||||
## 公平锁和非公平锁
|
||||
|
||||
公平锁:线程获取锁的顺序使按照线程加锁的顺序来分配的,满足FIFO
|
||||
非公平锁:等待线程抢占获取锁,也有可能造成某个线程一直获取不到锁
|
||||
|
||||
ReentrantLock 构造函数输入可以设置是否是公平锁,默认非公平锁。
|
||||
|
||||
|
||||
# Timer
|
||||
|
||||
Timer 定时器,主要负责计划任务的功能,也就是在指定的时间开始执行某一个任务。
|
||||
|
||||
## shedule(TimerTask task, Date date)
|
||||
|
||||
``` java
|
||||
package com.raorao.java.thread;
|
||||
|
||||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
|
||||
/**
|
||||
* 定时器.
|
||||
*
|
||||
* @author Xiong Raorao
|
||||
* @since 2018-08-06-13:07
|
||||
*/
|
||||
public class TimerTest {
|
||||
private static Timer timer = new Timer();
|
||||
|
||||
static class MyTask extends TimerTask{
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
System.out.println("运行时间: " + new Date());
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
MyTask task1 = new MyTask();
|
||||
try {
|
||||
Date taskDate = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse("2018-08-06 13:15:00");
|
||||
System.out.println("执行时间: " + taskDate.toLocaleString() + ",当前时间" + new Date().toLocaleString());
|
||||
timer.schedule(task1, taskDate);
|
||||
} catch (ParseException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
执行时间: 2018-8-6 13:15:00,当前时间2018-8-6 13:14:27
|
||||
运行时间: Mon Aug 06 13:15:00 CST 2018
|
||||
|
||||
如果任务执行时间比当前时间晚,则到了计划时间执行,否则立即执行。
|
||||
|
||||
## shedule 周期性执行
|
||||
|
||||
函数: shedule(TimeTask task, Date firstTime, long period)
|
||||
表示在指定的日期之后,按照指定的时间间隔(period)周期性的无休止的执行某一任务。
|
||||
|
||||
**同样满足如果是未来的任务,到计划时间执行,否则立即执行**
|
||||
|
||||
## sheduleAtFixedRate
|
||||
|
||||
该方法和shedule方法的区别在于任务不延迟的情况。
|
||||
|
||||
shedule: 如果执行任务的时间没有被延迟,那么下一次任务的执行时间参考的是上一次任务的“开始”时间计算。
|
||||
|
||||
sheduleAtFixedRate: 如果执行任务的时间没有被延迟,那么下一次任务的执行时间参考的是上一次任务的“结束”时间计算。
|
||||
|
||||
# Concurrent 容器
|
||||
|
||||
|
||||
## BlockingQueue
|
||||
|
||||

|
||||
|
||||
BlockingQueue 是一个阻塞接口,定义了阻塞队列的行为。
|
||||
|
||||
- boolean add(E e): 对尾插入元素,成功返回true;
|
||||
- boolean offer(E e): 队尾插入元素,成功返回true; 和 add 区别在于,如果超过队列容量,add 会抛出 IllegalStateException 异常
|
||||
- void put(E e): 队尾插入元素,如果在线程等待的时候发生线程中断,抛出异常。
|
||||
- boolean offer(E e, long timeout, TimeUnit unit): 超时放弃,线程等待的时候如果发生中断信号,也会抛出中断异常。
|
||||
- E take(): 弹出队头元素,线程等待的时候如果发生中断信号,也会抛出中断异常。
|
||||
- E remove(): 弹出队头元素, 如果为空,抛出NoSuchElementException;
|
||||
- E poll(): 弹出队头元素,如果为空,返回null
|
||||
- E poll(long timeout, TimeUnit unit): 超时放弃,线程等待的时候如果发生中断信号,抛出异常。
|
||||
- E element(): 仅仅返回对头元素, 如果为空,抛出NoSuchElementException;
|
||||
- E peek(): 仅仅返回队头元素,如果为空,返回null
|
||||
- int remainingCapacity(): 返回理想情况下,可以直接入队不用阻塞的元素个数
|
||||
|
||||
## DelayQueue
|
||||
|
||||

|
||||
|
||||
无界的阻塞队列(BlockingQueue), 用于防止实现了Delayed接口的对象,其中的对象只能在到期时才能从对队列中取走。队列本身是有序的, 即对头对象的延迟到期的时间最长的Delayed元素。
|
||||
|
||||
为了具有调用行为,存放到DelayDeque的元素必须继承Delayed接口。Delayed接口使对象成为延迟对象,它使存放在DelayQueue类中的对象具有了激活日期。该接口强制执行下列两个方法。
|
||||
|
||||
CompareTo(Delayed o):Delayed接口继承了Comparable接口,因此有了这个方法。
|
||||
getDelay(TimeUnit unit):这个方法返回到激活日期的剩余时间,时间单位由单位参数指定。
|
||||
|
||||
``` java
|
||||
public class DelayEvent implements Delayed {
|
||||
private Date startDate;
|
||||
public DelayEvent(Date startDate) {
|
||||
super();
|
||||
this.startDate = startDate;
|
||||
}
|
||||
@Override
|
||||
public int compareTo(Delayed o) {
|
||||
long result = this.getDelay(TimeUnit.NANOSECONDS)
|
||||
- o.getDelay(TimeUnit.NANOSECONDS);
|
||||
if (result < 0) {
|
||||
return -1;
|
||||
} else if (result > 0) {
|
||||
return 1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public long getDelay(TimeUnit unit) {
|
||||
Date now = new Date();
|
||||
long diff = startDate.getTime() - now.getTime();
|
||||
return unit.convert(diff, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public class DelayTask implements Runnable {
|
||||
private int id;
|
||||
private DelayQueue<DelayEvent> queue;
|
||||
public DelayTask(int id, DelayQueue<DelayEvent> queue) {
|
||||
super();
|
||||
this.id = id;
|
||||
this.queue = queue;
|
||||
}
|
||||
@Override
|
||||
public void run() {
|
||||
Date now = new Date();
|
||||
Date delay = new Date();
|
||||
delay.setTime(now.getTime() + id * 1000); // 根据id的不同,延迟的时间不同
|
||||
System.out.println("Thread " + id + " " + delay);
|
||||
for (int i = 0; i < 100; i++) {
|
||||
DelayEvent delayEvent = new DelayEvent(delay);
|
||||
queue.add(delayEvent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class DelayDequeMain {
|
||||
public static void main(String[] args) throws Exception {
|
||||
DelayQueue<DelayEvent> queue = new DelayQueue<DelayEvent>();
|
||||
Thread threads[] = new Thread[5];
|
||||
for (int i = 0; i < threads.length; i++) {
|
||||
DelayTask task = new DelayTask(i + 1, queue);
|
||||
threads[i] = new Thread(task);
|
||||
}
|
||||
for (int i = 0; i < threads.length; i++) {
|
||||
threads[i].start();
|
||||
}
|
||||
for (int i = 0; i < threads.length; i++) {
|
||||
try {
|
||||
threads[i].join();
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
do {
|
||||
int counter = 0;
|
||||
DelayEvent delayEvent;
|
||||
do {
|
||||
delayEvent = queue.poll();
|
||||
if (delayEvent != null) {
|
||||
counter++;
|
||||
}
|
||||
} while (delayEvent != null);
|
||||
System.out.println("At " + new Date() + " you have read " + counter+ " event");
|
||||
TimeUnit.MILLISECONDS.sleep(500);
|
||||
} while (queue.size() > 0);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## PriorityBlockingQueue
|
||||
|
||||

|
||||
阻塞的优先队列。
|
||||
|
||||
## ArrayBlockingQueue 和 LinkedBlockingQueue
|
||||
|
||||

|
||||

|
||||
|
||||
FIFO 阻塞队列, 前者采用固定数组长度的数组实现队列(初始化需指定队列长度),后者采用链表实现。
|
||||
|
||||
## ConcurrentHashMap
|
||||
|
||||

|
||||
|
||||
ConcurrentHashMap 默认容量为16, 最大容量2^30,默认并发度16,负载因子0.75。当散列桶的元素个数超过8的时候,将链表改为红黑树(jdk1.8)。
|
||||
|
||||
ConcurrentHashMap 相较于HashMap,采用了分段锁(Segment, 继承自 ReentrantLock)的方式保证多线程安全(JDK1.7)。
|
||||
|
||||
在JDK1.8 中, CAS 操作来支持更高的并发度,在 CAS 操作失败时使用内置锁 synchronized,
|
||||
|
||||
## ConcurrentLinkedDeque 和 ConcurrentLinkedQueue
|
||||
|
||||

|
||||

|
||||
|
||||
ConcurrentLinkedDeque: 线程安全的链表实现的双端队列。
|
||||
ConcurrentLinkedQueque: 线程安全的链表实现的队列。
|
||||
|
||||
## Deque
|
||||
|
||||
Deque 定义一个双端队列的接口,可以在两段进行插入和弹出, 方法如下:
|
||||
|
||||
修饰符和返回值 | 方法名 | 描述
|
||||
--- | --- | ---
|
||||
void | push(E) |向队列头部插入一个元素,失败时抛出异常
|
||||
void | addFirst(E) |向队列头部插入一个元素,失败时抛出异常
|
||||
void | addLast(E) |向队列尾部插入一个元素,失败时抛出异常
|
||||
boolean | offerFirst(E)|向队列头部加入一个元素,失败时返回false
|
||||
boolean | offerLast(E)|向队列尾部加入一个元素,失败时返回false
|
||||
E | getFirst() | 获取队列头部元素,队列为空时抛出异常
|
||||
E | getLast() | 获取队列尾部元素,队列为空时抛出异常
|
||||
E | peekFirst() | 获取队列头部元素,队列为空时返回null
|
||||
E | peekLast() | 获取队列尾部元素,队列为空时返回null
|
||||
boolean | removeFirstOccurrence(Object) | 删除第一次出现的指定元素,不存在时返回false
|
||||
boolean | removeLastOccurrence(Object) | 删除最后一次出现的指定元素,不存在时返回false
|
||||
E | pop() | 弹出队列头部元素,队列为空时抛出异常
|
||||
E | removeFirst() | 弹出队列头部元素,队列为空时抛出异常
|
||||
E | removeLast() | 弹出队列尾部元素,队列为空时抛出异常
|
||||
E | pollFirst() | 弹出队列头部元素,队列为空时返回null
|
||||
E | pollLast() | 弹出队列尾部元素,队列为空时返回null
|
||||
Iterator<E> | descendingIterator() | 返回队列反向迭代器
|
||||
|
||||
### LinkedList
|
||||
|
||||

|
||||
|
||||
LinkedList: 链表实现的双端队列
|
||||
|
||||
### ArrayDeque
|
||||
|
||||

|
||||
|
||||
ArrayDeque: 数组实现的双端队列,默认队列长度为16,队列满了就直接扩充一倍(double)
|
||||
|
||||
# 参考文档
|
||||
|
||||
- [java并发编程--Executor框架](https://www.cnblogs.com/MOBIN/p/5436482.html)
|