From 7ce55e2c16355e84eaeae8dd54e4d44aae33e8dc Mon Sep 17 00:00:00 2001 From: xiongraorao Date: Mon, 6 Aug 2018 13:25:12 +0800 Subject: [PATCH] =?UTF-8?q?=E6=9B=B4=E6=96=B0=20thread?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../raorao/java/thread/ReentrantLockTest.java | 65 +++ .../thread/ReentrantReadWriteLockTest.java | 68 ++++ .../com/raorao/java/thread/TimerTest.java | 36 ++ interview/README.md | 1 + interview/java/thread.md | 372 ++++++++++++++++++ 5 files changed, 542 insertions(+) create mode 100644 code/src/main/java/com/raorao/java/thread/ReentrantLockTest.java create mode 100644 code/src/main/java/com/raorao/java/thread/ReentrantReadWriteLockTest.java create mode 100644 code/src/main/java/com/raorao/java/thread/TimerTest.java diff --git a/code/src/main/java/com/raorao/java/thread/ReentrantLockTest.java b/code/src/main/java/com/raorao/java/thread/ReentrantLockTest.java new file mode 100644 index 00000000..434cad58 --- /dev/null +++ b/code/src/main/java/com/raorao/java/thread/ReentrantLockTest.java @@ -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(); + } +} diff --git a/code/src/main/java/com/raorao/java/thread/ReentrantReadWriteLockTest.java b/code/src/main/java/com/raorao/java/thread/ReentrantReadWriteLockTest.java new file mode 100644 index 00000000..4d9d3d97 --- /dev/null +++ b/code/src/main/java/com/raorao/java/thread/ReentrantReadWriteLockTest.java @@ -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(); + + } + + +} diff --git a/code/src/main/java/com/raorao/java/thread/TimerTest.java b/code/src/main/java/com/raorao/java/thread/TimerTest.java new file mode 100644 index 00000000..a61efab0 --- /dev/null +++ b/code/src/main/java/com/raorao/java/thread/TimerTest.java @@ -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(); + } + } +} diff --git a/interview/README.md b/interview/README.md index 4324a310..1732d384 100644 --- a/interview/README.md +++ b/interview/README.md @@ -28,6 +28,7 @@ Thoutworks | 内推 | | [内推链接](https://jinshuju.net/f/CcO2JA) 顺丰科技 | 网申 | 即日 -- 9.23日 | [招聘官网](http://campus.sf-tech.com.cn/campusRecruitment/Default.html?p=28668990421)
7月20日投了内推 多益网络 | 内推 |
  • 内推笔试第一批:8.11 10:00
  • 内推笔试第二批: 9.06 10:00 | [招聘官网](https://xz.duoyi.com/jobs/index.html?t=0)
    7月20日投了内推 好未来 | 提前批 | 8.19日截止 | [招聘官网](http://job.100tal.com/jobxq?jobId=510212759)
  • 7月24日投了 +华为 | 网申 | | [招聘官网](http://career.huawei.com/reccampportal/next/mini/index.html) 腾讯 | 提前批/网申 |
  • 提前批:7.25-9.12
  • 提前批:7.25-9.14
  • 在线笔试:9.16-9.17
  • 面试:9.26开始| [招聘官网](https://join.qq.com/) 抖音、头条 | 内推 | 8.1 - 12.31| [招聘官网](https://job.bytedance.com/campus/)
  • 8.2 投简历 携程 | 内推 |
  • 内推:8.2 - 8.12
  • 网申 8.2 - 9.4 | [招聘官网](http://campus.ctrip.com) diff --git a/interview/java/thread.md b/interview/java/thread.md index 871c6542..b77beeb7 100644 --- a/interview/java/thread.md +++ b/interview/java/thread.md @@ -16,6 +16,18 @@ - [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) - [参考文档](#参考文档) @@ -475,6 +487,366 @@ 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 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: 如果执行任务的时间没有被延迟,那么下一次任务的执行时间参考的是上一次任务的“结束”时间计算。 + # 参考文档 - [java并发编程--Executor框架](https://www.cnblogs.com/MOBIN/p/5436482.html) \ No newline at end of file