并发的多面性

用并发解决的问题可以分为速度和设计可管理性两种

更快的管理

如果有一台多处理器,就可以在这些处理器中分配任务,但是并发通常时提高在运行在单处理器上的程序的性能
并且单位时间内只有一个线程执行
如果程序中的某个任务应为该程序控制范围之外的某些条件而导致不能够继续运行,就说这个线程阻塞了

改进代码设计

Java的线程机制是抢占式的,这表示调度机制会在周期性的中断线程,切换到另外一个线程,从而为每个线程都提供时间片,使得每个线程都会分配到数量合理的时间去驱动它的任务

基本的线程机制

并发编程使我们可以将程序分为多个分离的,独立运行的任务。一个线程就是在进程中的一个单一的顺序控制流,因此进程可以拥有多个并发执行的任务

定义任务

描述任务,只需要实现Runnable接口即可

  1. public class ListOff implements Runnable {
  2. //描述接口只需要实现这个接口
  3. private int countDown = 10;
  4. private static int taskCount = 0;
  5. private final int id = taskCount++;
  6. public ListOff(){}
  7. public ListOff(int countDown) {
  8. this.countDown = countDown;
  9. }
  10. public String status(){
  11. return "#" + id + "(" + (countDown > 0 ? countDown : "ListOff! ") + ")";
  12. }
  13. @Override
  14. public void run() {
  15. while (countDown-- > 0) {
  16. System.out.println(status());
  17. Thread.yield();
  18. //“让一下”,可以让别的线程抢占,但是抢到抢不到另说
  19. }
  20. }
  21. }

可以在main方法中直接调用run方法

  1. public class MainThread {
  2. public static void main(String[] args) {
  3. final ListOff listOff = new ListOff();
  4. listOff.run();//直接调用该方法
  5. }
  6. }

每个实现了Runnable的类都会有run方法

Thread类

  1. public class BasicThread {
  2. public static void main(String[] args) {
  3. final Thread thread = new Thread(new ListOff());
  4. thread.start();//执行线程必须的初始化
  5. System.out.println("waiting for ListOff");
  6. //从输出中看,虽然开始线程的运行,但是先打印出来的使main方法中的输出语句
  7. }
  8. }

可以添加更多的线程去驱动更多的任务

  1. public class MoreBasicThreads {
  2. public static void main(String[] args) {
  3. for (int i = 0; i < 5; i++) {
  4. new Thread(new LiftOff()).start();//每一个线程都拥有一个LiftOff实例,所以不会发生资源竞争的问题
  5. //所以不会产生多资源问题
  6. }
  7. System.out.println("等待发射......");
  8. }
  9. }
  1. public static void main(String[] args) {
  2. final Thread thread = new Thread(new LiftOff());
  3. for (int i = 0; i < 5; i++) {//一个线程五个资源:IllegalThreadStateException
  4. thread.start();
  5. }
  6. System.out.println("等待发射......");
  7. }
  8. }

输出说明不同任务的执行在线程被换进换出时混在了一起,线程调度器会在这些处理器之间默默的分发线程

使用Executor

Executor在客户端和任务执行之间提供了一个间接层,与客户端直接执行任务不同,这个中介将执行任务。Executor允许管理异步任务的执行,而无需显示的管理线程的声明周期
ExecutorService:(具有服务声明周期的Executor)知道如何构建恰当的上下文来执行Runnable。
CachedThreadPool:为每一个任务都创建一个线程(创建与所需数量相同的线程)
ExecutorService对象是使用静态的Executor方法创建的

  1. public class CachedThreadPool {
  2. public static void main(String[] args) {
  3. ExecutorService exec = Executors.newCachedThreadPool();//底层是用Thread实现
  4. //为每个任务都创建一个线程,并且如果以前创建的线程可用,会重用他们
  5. for (int i = 0; i < 5; i++) {
  6. exec.execute(new LiftOff());
  7. }
  8. exec.shutdown();//先执行Shutdown,如果有未完成的任务,会把任务完成,之后不允许再次提交任务
  9. //如果在ShutDown之后再提交,会报错
  10. }
  11. }

FixedThreadPool使用了有限的线程集来执行所提交的任务

  1. public class FixedThreadPool {
  2. public static void main(String[] args) {
  3. ExecutorService exec = Executors.newFixedThreadPool(5);
  4. //一次分配五个线程(可以分配任意个线程)
  5. for (int i = 0; i < 5; i++) {
  6. exec.execute(new LiftOff());//在线程中执行任务
  7. }
  8. exec.shutdown();
  9. }
  10. }

在任何线程池中,如果已经创建的线程可用,都可以自动复用
SingleThreadExecutor:只有一个线程,就像是数量为1的FixedThreadPool,用这种方式,并不需要在共享资源上处理同步
线程池内部实现方式:

  1. public ThreadPoolExecutor(int corePoolSize,
  2. int maximumPoolSize,
  3. long keepAliveTime,
  4. TimeUnit unit,
  5. BlockingQueue<Runnable> workQueue,
  6. ThreadFactory threadFactory,
  7. RejectedExecutionHandler handler)

从任务中产生返回值

Runnable是执行工作的独立任务,他没有任何返回值,如果希望在完成时能够返回一个值,那么可以实现Callable接口而不是Runnable。
Callable是一个具有类型参数的泛型,它的类型参数是从方法call()中返回的值,并且必须使用ExecutorService.submit()方法调用它。

  1. public Thread(Runnable target) {//Callable不能再线程中提交任务
  2. init(null, target, "Thread-" + nextThreadNum(), 0);
  1. class TaskWithResult implements Callable<String> {
  2. private int id;
  3. public TaskWithResult(int id) {
  4. this.id = id;
  5. }
  6. @Override
  7. public String call() throws InterruptedException {
  8. TimeUnit.SECONDS.sleep(2);
  9. return "result of TaskWithResult " + id;
  10. }
  11. }
  12. public class CallableDemo {
  13. public static void main(String[] args) {
  14. System.out.println("------------");
  15. ExecutorService exec = Executors.newCachedThreadPool();
  16. ArrayList<Future<String>> result = new ArrayList<>();
  17. for (int i = 0; i < 10; i++) {
  18. result.add(exec.submit(new TaskWithResult(i)));
  19. }
  20. for (Future<String> fs : result) {
  21. try {
  22. System.out.println("0000000000000");
  23. System.out.println(fs.get());//调用get方法获取结果
  24. //System.out.println("1111111111111");
  25. } catch (InterruptedException | ExecutionException e) {
  26. e.printStackTrace();
  27. }finally {
  28. exec.shutdown();
  29. }
  30. }
  31. System.out.println("==============");
  32. }
  33. }
  1. class TaskResult implements Callable<String> {
  2. private final int id;
  3. private static final Random rand = new Random(47);
  4. public TaskResult(int id){
  5. this.id = id;
  6. }
  7. @Override
  8. public String call() throws Exception {
  9. int timeout = rand.nextInt(10);
  10. TimeUnit.SECONDS.sleep(timeout);//随机睡眠时间
  11. return "当前线程名字 : " + Thread.currentThread().getName() + " " +
  12. "执行了: " + timeout + " id : " + id;
  13. }
  14. }
  15. public class CallableDemo {
  16. public static void main(String[] args) {
  17. ExecutorService exec = Executors.newCachedThreadPool();
  18. ArrayList<Future<String>> futures = new ArrayList<>();
  19. for (int i = 0; i < 10; i++) {
  20. futures.add(exec.submit(new TaskResult(i)));
  21. }
  22. long start = System.currentTimeMillis();
  23. for (Future<String> future : futures) {
  24. try {
  25. System.out.println(future.get());//只要线程完成了就会立即获取
  26. } catch (InterruptedException | ExecutionException e) {
  27. e.printStackTrace();
  28. }
  29. }
  30. System.out.println(System.currentTimeMillis() - start / 1000);
  31. exec.shutdown();
  32. }
  33. }
  1. class TaskResult2 implements Callable<String> {
  2. private final int i;
  3. TaskResult2(int i) {
  4. this.i = i;
  5. }
  6. @Override
  7. public String call() throws Exception {
  8. TimeUnit.SECONDS.sleep(5);//模拟任务耗时的时间
  9. return "当前线程名字:" + Thread.currentThread().getName() + " " + i;
  10. }
  11. }
  12. public class CallableDemo2 {
  13. public static void main(String[] args) {
  14. ExecutorService exec = Executors.newSingleThreadExecutor();
  15. Future<String> future = exec.submit(new TaskResult2(1));
  16. try {
  17. System.out.println("11111111111111111111");
  18. //System.out.println(future.get());//等待结果的获取 阻塞程序的执行
  19. System.out.println(future.get(1, TimeUnit.SECONDS));//只等待1秒钟获取结果
  20. System.out.println("22222222222222222222");
  21. } catch (InterruptedException | ExecutionException | TimeoutException e) {
  22. e.printStackTrace();
  23. }
  24. exec.shutdown(); //并不会阻塞当前程序的执行;如果任务没有执行完成的话,将等待任务执行完成之后再关闭
  25. System.out.println("这是main方法的程序......");
  26. }
  27. }
  1. class TaskResult3 implements Callable<String> {
  2. private final int i;
  3. TaskResult3(int i) {
  4. this.i = i;
  5. }
  6. @Override
  7. public String call() throws Exception {
  8. System.out.println("--------before sleep---------");
  9. TimeUnit.SECONDS.sleep(i);//模拟任务耗时的时间
  10. System.out.println("--------after sleep---------");
  11. return "当前线程名字:" + Thread.currentThread().getName() + " " + i;
  12. }
  13. }
  14. public class CallableDemo3 {
  15. public static void main(String[] args) throws InterruptedException {
  16. ExecutorService exec = Executors.newSingleThreadExecutor();
  17. Future<String> future = exec.submit(new TaskResult3(3));
  18. try {
  19. System.out.println("---------1---------");
  20. System.out.println(future.get());
  21. System.out.println("---------2---------");
  22. } catch (InterruptedException | ExecutionException e) {
  23. e.printStackTrace();
  24. }
  25. exec.shutdown();
  26. System.out.println("其他程序继续执行");
  27. }
  28. }

休眠

影响任务行为的一种简单的方法是调用sleep();这将是任务终止执行给定的时间

  1. public class SleepingTask extends LiftOff {
  2. public void run() {
  3. try {
  4. while (countDown-- > 0) {
  5. System.out.print(status());
  6. TimeUnit.SECONDS.sleep(2);
  7. }
  8. } catch (InterruptedException e) {
  9. System.err.println("Interrupted");
  10. }
  11. }
  12. public static void main(String[] args) {
  13. ExecutorService exec = Executors.newCachedThreadPool();
  14. for (int i = 0; i < 5; i++)
  15. exec.execute(new SleepingTask());
  16. exec.shutdown();
  17. }
  18. }

优先级

调度器倾向于让优先权最高的线程先执行,但是,这并不是意味着优先权低的得不到执行,(优先权并不会导致死锁)可以使用getpriority()来读取现有线程的优先权,并且任何时刻都可以通过setPriority()来修改它

  1. public class SimplePriorities implements Runnable {
  2. private int countDown = 5;
  3. private volatile double d; // No optimization
  4. private int priority;
  5. public SimplePriorities(int priority) {
  6. this.priority = priority;
  7. }
  8. public String toString() {
  9. return Thread.currentThread() + ": " + countDown;
  10. }
  11. public void run() {
  12. Thread.currentThread().setPriority(priority);
  13. //驱动对该任务的引用
  14. while (true) {
  15. for (int i = 1; i < 100000; i++) {
  16. d += (Math.PI + Math.E) / (double) i;
  17. if (i % 1000 == 0)
  18. Thread.yield();
  19. }
  20. System.out.println(this);
  21. if (--countDown == 0) return;
  22. }
  23. }
  24. public static void main(String[] args) {
  25. ExecutorService exec = Executors.newCachedThreadPool();
  26. for (int i = 0; i < 5; i++)
  27. exec.execute(
  28. new SimplePriorities(Thread.MIN_PRIORITY));
  29. exec.execute(
  30. new SimplePriorities(Thread.MAX_PRIORITY));
  31. exec.shutdown();
  32. }
  33. }

并不是优先级高的就会先输出

让步

yield():干一会,停一下,然后让别的线程可以抢一下,并不是说一定能够抢占成功

后台线程

后台线程指在程序运行时再后台提供的一种服务的线程。当所有的非后台线程结束的时候,程序也就终止了,并且会杀死程序中所有的后台线程,所以说只要还有非后台线程还在运行,程序就不会终止

  1. public class SimpleDaemons implements Runnable {
  2. @Override
  3. public void run() {
  4. try {
  5. while (true) {
  6. TimeUnit.MILLISECONDS.sleep(100);
  7. System.out.println(Thread.currentThread() + " " + this);
  8. }
  9. } catch (InterruptedException e) {
  10. e.printStackTrace();
  11. System.out.println("sleep() interrupted");
  12. }
  13. }
  14. public static void main(String[] args) throws InterruptedException {
  15. for (int i = 0; i < 10; i++) {
  16. Thread daemon = new Thread(new SimpleDaemons());
  17. daemon.setDaemon(true);//将其设置成为后台线程
  18. daemon.start();
  19. }
  20. System.out.println("All daemons started");
  21. TimeUnit.MILLISECONDS.sleep(150);//如果它的休眠时间和它执行的休眠时间差不多的话,可能它执行不完
  22. }
  23. }

必须要在线程启动之前将其设置为后台线程
上面的代码创建了显式的线程,可以设置他们的后台标志。
通过编写ThreadFactory可以定制由Executor创建的线程的属性(后台,优先级,名称)

  1. public class DaemonFromFactory implements Runnable {
  2. @Override
  3. public void run() {
  4. try {
  5. while (true) {
  6. TimeUnit.MILLISECONDS.sleep(100);
  7. System.out.println(Thread.currentThread() + " " + this);
  8. TimeUnit.MILLISECONDS.sleep(200);
  9. System.out.println("是否执行了一半"); //第二此任务只会执行一半
  10. }
  11. } catch (InterruptedException e) {
  12. System.out.println("Interrupted");
  13. }
  14. }
  15. public static void main(String[] args) throws InterruptedException {
  16. ExecutorService exec = Executors.newCachedThreadPool(new DaemonThreadFactory());
  17. //使用线程池来创建后台线程
  18. for (int i = 0; i < 10; i++) {
  19. exec.execute(new DaemonFromFactory());
  20. }
  21. System.out.println("All Daemon started");
  22. TimeUnit.MILLISECONDS.sleep(500);
  23. }
  24. }

可以通过isDaemon()方法来确定线程是否是一个后台线程。如果是一个后台线程,那么它创建的任何线程都将被自动的设置成后台线程

  1. class Daemon implements Runnable{
  2. Thread[] t = new Thread[10];
  3. @Override
  4. public void run() {
  5. for (int i = 0; i < t.length; i++) {
  6. t[i] = new Thread(new DaemonSpawn());
  7. //DaemonSpawn并没有显式的说明他是后台线程,但是已经将Daemon设置成为了后台线程
  8. //所以DaemonSpawn也被设置成为了后台线程
  9. t[i].start();
  10. System.out.println("Daemon " + i + " started");
  11. }
  12. for (int i = 0; i < t.length; i++) {
  13. System.out.println("t[+" + i + "].isDaemon()=" + t[i].isDaemon() + ",");
  14. }
  15. while (true) {
  16. Thread.yield();
  17. }
  18. }
  19. }
  20. class DaemonSpawn implements Runnable{
  21. @Override
  22. public void run() {
  23. while (true) {
  24. Thread.yield();
  25. }
  26. }
  27. }
  28. public class Daemons {
  29. public static void main(String[] args) throws InterruptedException {
  30. Thread d = new Thread(new Daemon());
  31. d.setDaemon(true);//将Daemon设置成为后台线程
  32. d.start();
  33. System.out.println("d.isDaemon() = " + d.isDaemon() + " ");
  34. TimeUnit.SECONDS.sleep(1);
  35. }
  36. }

后台线程在不执行finally子句的情况下就会终止其run()方法

  1. class ADaemon implements Runnable{
  2. @Override
  3. public void run() {
  4. try {
  5. System.out.println("Starting ADaemon");
  6. TimeUnit.SECONDS.sleep(1);
  7. } catch (InterruptedException e) {
  8. System.out.println("Exiting via InterruptedException");
  9. }finally {
  10. System.out.println("This should always run? ");
  11. //并没有执行finally子句,线程就结束了
  12. }
  13. }
  14. }
  15. public class DaemonsDontRunFinally {
  16. public static void main(String[] args) {
  17. Thread t = new Thread(new ADaemon());
  18. t.setDaemon(true);
  19. t.start();
  20. }
  21. }

当最后一个非后台线程终止时,后台线程会突然终止,一次,一旦main方法退出,所有的后台线程都将会关闭

编码的变体

在非常简单的情况下,可以直接从Thread继承这种可替换的方式

  1. public class SimpleThread extends Thread {
  2. private int countDown = 5;
  3. private static int threadCount = 0;
  4. public SimpleThread(){
  5. super(Integer.toString(++threadCount));//分配一个新的Thread对象
  6. start();
  7. }
  8. @Override
  9. public String toString() {
  10. return "#" + getName() + "(" + countDown + ")";
  11. }
  12. public void run(){
  13. while (true) {
  14. System.out.println(this);
  15. if (--countDown == 0) {
  16. return;
  17. }
  18. }
  19. }
  20. public static void main(String[] args) {
  21. for (int i = 0; i < 5; i++) {
  22. new SimpleThread();
  23. }
  24. }
  25. }

另一种实现Runnable方法

  1. public class SelfManaged implements Runnable {
  2. private int countDown = 5;
  3. private Thread t = new Thread(this);//分配一个新的Thread对象
  4. public SelfManaged(){
  5. t.start();
  6. }
  7. @Override
  8. public String toString() {
  9. return Thread.currentThread().getName() + "(" + countDown + ")";
  10. }
  11. @Override
  12. public void run() {
  13. while (true) {
  14. System.out.println(this);
  15. if (countDown-- == 0) {
  16. return;
  17. }
  18. }
  19. }
  20. public static void main(String[] args) {
  21. for (int i = 0; i < 5; i++) {
  22. new SelfManaged();
  23. }
  24. }
  25. }

两者还是由区别的,实现Runnable接口可以继承另外的类,但是继承了Thread就不能在继承别的类,因为Java不允许多继承

术语

对Thread类实际没有任何控制权,实际操作是:创建任务,通过某种方式将一个线程附着在任务上,使得这个线程可以驱动任务,Thread类自身不执行任何操作,它只是驱动赋予它的任务(任务:比如Runnable接口中的run方法中的业务逻辑)

加入一个线程

一个线程可以在其他线程之上调用join方法,他会等待一段时间,直到这个线程结束(插队,谁调用join方法谁先执行),对join的调用可以被中断,在调用的线程上调用interrupt()方法,这时需要try-catch子句

被加入的线程一定会被挂起?

  1. class Sleeper extends Thread {
  2. private int duration;
  3. public Sleeper(String name, int sleepTime) {
  4. super(name);
  5. duration = sleepTime;
  6. start();//在构造器中间直接开始执行线程
  7. }
  8. public void run(){
  9. try {
  10. sleep(duration);
  11. } catch (InterruptedException e) {
  12. //被打断的信息在try catch之后它的被打断的信息就会被清除,所以isInterrupted在catch之后会返回到false
  13. System.out.println(getName() + " was interrupted " + "isInterrupted(): " + isInterrupted());
  14. return;
  15. }
  16. System.out.println(getName() + "has awakened");//被打断,所以这一行不会执行
  17. }
  18. }
  19. class Joiner extends Thread {
  20. private Sleeper sleeper;
  21. public Joiner(String name, Sleeper sleeper) {
  22. super(name);
  23. this.sleeper = sleeper;
  24. start();//在构造器中直接开始线程
  25. }
  26. public void run(){
  27. try {
  28. sleeper.join();//插队,先执行,sleeper执行之后才会执行joiner
  29. } catch (InterruptedException e) {
  30. System.out.println("Interrupted");
  31. }
  32. System.out.println(getName() + " join completed");
  33. }
  34. }
  35. public class Joining {
  36. public static void main(String[] args) {
  37. //Sleeper sleepy = new Sleeper("Sleepy", 1500);
  38. Sleeper grumpy = new Sleeper("Grumpy", 1500);
  39. //Joiner dopey = new Joiner("Dopey", sleepy);
  40. Joiner doc = new Joiner("Doc", grumpy);
  41. grumpy.interrupt();
  42. }
  43. }
  1. package stu;
  2. import java.util.concurrent.ExecutorService;
  3. import java.util.concurrent.Executors;
  4. import java.util.concurrent.TimeUnit;
  5. class Aoo implements Runnable {
  6. @Override
  7. public void run() {
  8. for (int i = 0; i < 10; i++) {
  9. try {
  10. TimeUnit.SECONDS.sleep(1);
  11. System.out.println("Aoo 线程 持有时间片");
  12. } catch (InterruptedException e) {
  13. e.printStackTrace();
  14. }
  15. }
  16. }
  17. }
  18. class Boo implements Runnable {
  19. @Override
  20. public void run() {
  21. try {
  22. TimeUnit.SECONDS.sleep(2);
  23. } catch (InterruptedException e) {
  24. e.printStackTrace();
  25. }
  26. System.out.println("Boo 线程 获得时间片");
  27. }
  28. }
  29. public class SleepJoin {
  30. public static void main(String[] args) throws InterruptedException {
  31. final Thread aoo = new Thread(new Aoo());
  32. System.out.println("Aoo 线程启动");
  33. aoo.start();
  34. for (int i = 0; i < 10; i++) {
  35. TimeUnit.MILLISECONDS.sleep(200);
  36. System.out.println("主线程");
  37. }
  38. aoo.join();
  39. for (int i = 0; i < 10; i++) {
  40. TimeUnit.SECONDS.sleep(1);
  41. System.out.println("主线程 持有时间片");
  42. }
  43. }
  44. }

joiner任务耗时的话,会继续执行,和sleeper执行完成或者中断没有任何关系

捕获异常

由于线程的本质特性,所以不能捕获从线程逃逸的异常,除非使用特殊的步骤捕获这种错误的异常

  1. public class ExceptionThread implements Runnable{
  2. @Override
  3. public void run() {
  4. throw new RuntimeException();//异常从线程中逃逸,没有被捕获,会打印错误信息
  5. }
  6. public static void main(String[] args) {
  7. //即使将try-catch将其包裹也没有用
  8. ExecutorService exec = Executors.newCachedThreadPool();
  9. exec.execute(new ExceptionThread());
  10. }
  11. }

Thread.UncaughtExceptionHandler允许你在每个Thread对象上都附着一个异常处理器
Thread.UncaughtExceptionHandler.uncaughtException()会在线程因未捕获的异常而临近死亡时调用它

  1. class ExceptionThread2 implements Runnable {
  2. @Override
  3. public void run() {
  4. Thread t = Thread.currentThread();//当前的线程
  5. System.out.println("run() by " + t);
  6. System.out.println("eh = " + t.getUncaughtExceptionHandler());
  7. throw new RuntimeException();
  8. }
  9. }
  10. class MyUncaughtExceptionHandler implements Thread.UncaughtExceptionHandler {
  11. @Override
  12. public void uncaughtException(Thread t, Throwable e) {
  13. System.out.println("caught " + e);
  14. }
  15. }
  16. class HandlerThreadFactory implements ThreadFactory {
  17. @Override
  18. public Thread newThread(Runnable r) {
  19. //System.out.println(this + " creating new Thread");
  20. Thread t = new Thread(r);//创建线程
  21. //System.out.println("created " + t);
  22. t.setUncaughtExceptionHandler(new MyUncaughtExceptionHandler());//设置线程处理器
  23. //System.out.println("eh = " + t.getUncaughtExceptionHandler());
  24. return t;
  25. }
  26. }
  27. public class CaptureUncaughtException {
  28. public static void main(String[] args) {
  29. ExecutorService exec = Executors.newCachedThreadPool(new HandlerThreadFactory());
  30. exec.execute(new ExceptionThread2());//执行任务
  31. }
  32. }

共享资源受限

多个线程同时访问一个资源

不正确的访问资源

  1. public abstract class IntGenerator {
  2. private volatile boolean canceled = false;
  3. public abstract int next();
  4. public void cancel(){
  5. canceled = true;
  6. }
  7. public boolean isCanceled(){
  8. return canceled;
  9. }
  10. }

canceled的标志的boolean类型的,它是原子性的赋值和返回值这样的操作在发生时没有中断的可能,因此时看不到这些简单操作时的中间状态

  1. public class EvenChecker implements Runnable{
  2. private IntGenerator generator;
  3. private final int id;
  4. public EvenChecker(IntGenerator g, int ident) {
  5. generator = g;
  6. id = ident;
  7. }
  8. @Override
  9. public void run() {
  10. while (!generator.isCanceled()) {
  11. int val = generator.next();
  12. if (val % 2 != 0) {
  13. System.out.println(val + "not even");
  14. generator.cancel();
  15. }
  16. }
  17. }
  18. public static void test(IntGenerator gp, int count) {
  19. System.out.println("Press Control-C to exit");
  20. ExecutorService exec = Executors.newCachedThreadPool();
  21. for (int i = 0; i < count; i++) {
  22. exec.execute(new EvenChecker(gp,i));
  23. }
  24. exec.shutdown();
  25. }
  26. public static void test(IntGenerator gp) { //重载test方法,这时候只有一个IntGenerator类型的gp
  27. //所以他们访问的资源时同一个
  28. test(gp, 10);
  29. }
  30. }

test重载之后,十个线程访问的是同一个gp,并且上述代码通过使代码依赖于非任务对象,可以消除潜在的竞争条件()

  1. public class EvenGenerator extends IntGenerator {
  2. private int currentEvenValue = 0;
  3. @Override
  4. public int next() {
  5. ++currentEvenValue;//此处的操作每次都分三步走的,所以多线程访问的时候并不安全
  6. ++currentEvenValue;
  7. return currentEvenValue;
  8. }
  9. public static void main(String[] args) {
  10. EvenChecker.test(new EvenGenerator());
  11. }
  12. }

在Java中,递增不属于原子操作,所以这种递增使不安全的

解决共享资源竞争

防止多个线程在访问同一个资源出问题的方法:当这个资源被某一个任务使用时,就给他加上锁,第一个访问某项资源的任务必须锁定这项资源,在被解锁之前,其他的任务不能访问这项资源
基本上所有的并发在解决线程冲突的时候都是采用顺序访问的方式,一段时间只有一个任务可以运行这段代码
Java提供了关键字synchronized,当任务执行被他保护的代码时,它会检查锁是否可用,然后来获取锁,执行代码,然后释放锁
如果某个任务正在执行被标记为synchronized的方法时,如果他没有冲该方法返回,那么在这个线程从该方法返回之前,其他要调用这个方法的线程都会被阻塞

  1. public class InstanceSynchronized {
  2. public synchronized void f(){
  3. try {
  4. System.out.println("进入到f()方法");
  5. TimeUnit.SECONDS.sleep(3);
  6. System.out.println("f()方法执行结束");
  7. } catch (InterruptedException e) {
  8. e.printStackTrace();
  9. }
  10. }
  11. public synchronized void g(){
  12. try {
  13. System.out.println("进入到g()方法");
  14. TimeUnit.SECONDS.sleep(5);
  15. System.out.println("g()方法执行结束");
  16. } catch (InterruptedException e) {
  17. e.printStackTrace();
  18. }
  19. }
  20. public static void main(String[] args) {
  21. final InstanceSynchronized in = new InstanceSynchronized();
  22. new Thread(in::f).start();
  23. //使用实例来调用两个方法,现在称synchornized为实例锁
  24. new Thread(in::g).start();
  25. f方法执行结束,释放了锁之后,这个实例才能调用被标记为synchornizedg方法
  26. }
  27. }

在实例调用任意的synchronized方法的时候,这个实例都会被加上锁,这是这个实例上的其他synchronized方法只有等前一个调用完毕了之后释放了锁之后才能够被调用

注意,在使用并发时,将域设置成为private是十分重要的,否则,synchronized关键字就不能防止其他的任务直接访问域,这样会产生冲突

  1. public class SynchronizedWithPrivateDemo {
  2. int num = 0;
  3. synchronized int getNum() {
  4. num++;
  5. num++;
  6. return num;
  7. }
  8. }
  9. class Demo {
  10. public static void main(String[] args) {
  11. SynchronizedWithPrivateDemo demo = new SynchronizedWithPrivateDemo();
  12. new Thread(() -> {
  13. int res = demo.getNum();
  14. /* try {
  15. TimeUnit.MILLISECONDS.sleep(300);
  16. } catch (InterruptedException e) {
  17. e.printStackTrace();
  18. }*/
  19. System.out.println(Thread.currentThread().getName() + "->" + res);
  20. }, "get-1").start();
  21. new Thread(() -> {
  22. try {
  23. TimeUnit.MILLISECONDS.sleep(300);
  24. } catch (InterruptedException e) {
  25. e.printStackTrace();
  26. }
  27. demo.num = 9999; //可以随意的将num的值更改,这并不是线程安全的
  28. }, "update-1").start();
  29. new Thread(() -> {
  30. try {
  31. TimeUnit.MILLISECONDS.sleep(400);
  32. } catch (InterruptedException e) {
  33. e.printStackTrace();
  34. }
  35. int res = demo.getNum();
  36. System.out.println(Thread.currentThread().getName() + "->" + res);
  37. }, "get-2").start();
  38. }
  39. }

如果正在写一个变量,它可能接下来被另一个线程读取,或者正在读取一个上一次已经被另一个线程写过的变量,那么必须使用同步,并且,读写线程都必须用相同的监视器锁同步(final修饰的线程一定是线程安全的)

同步控制EvenGenerator

  1. public class SynchronizedEvenGenerator extends IntGenerator{
  2. private int currentEvenValue = 0;
  3. @Override
  4. public synchronized int next() { //上锁,其他的线程就进不来了
  5. ++currentEvenValue;
  6. Thread.yield();//已经上锁了,其他的线程抢不到
  7. ++currentEvenValue;
  8. return currentEvenValue;
  9. }
  10. public static void main(String[] args) {
  11. EvenChecker.test(new SynchronizedEvenGenerator());
  12. }
  13. }

使用显示的Lock对象

Lock对象和Synchronized相比,必须显示的创建Lock对象,显示的锁定和释放

  1. public class MutexEvenGenerator extends IntGenerator {
  2. private int currentEvenValue = 0;
  3. private Lock lock = new ReentrantLock();//创建锁
  4. ReentrantLock,不公平锁,可重入锁
  5. @Override
  6. public int next() {
  7. lock.lock();//锁住
  8. try {
  9. ++currentEvenValue;
  10. Thread.yield();
  11. ++currentEvenValue;
  12. return currentEvenValue;//先执行finally,在执行return
  13. } finally {
  14. lock.unlock();//释放锁
  15. }
  16. }
  17. public static void main(String[] args) {
  18. EvenChecker.test(new MutexEvenGenerator());
  19. }
  20. }

有了显示的Lock对象,就可以使用finally子句将系统维护在正确的状态
注意:return必须放在try语句中,为了防止unlock过早的发生,从而导致第二个任务进来

synchronized和Lock对象的区别:使用synchronized关键字的时候。需要写的代码量更少,并且用户出现错误的可能性更低,但是synchronized关键字不能尝试着获取锁且最终获取锁会失败,或者尝试着获取锁一段时间然后放弃它

  1. public class AttemptLocking {
  2. private ReentrantLock lock = new ReentrantLock();
  3. public void untimed() throws InterruptedException {
  4. TimeUnit.SECONDS.sleep(2);
  5. boolean captured = lock.tryLock();
  6. try {
  7. System.out.println("tryLock()" + captured);
  8. }finally {
  9. if (captured) {
  10. lock.unlock();
  11. }
  12. }
  13. }
  14. public void timed() {
  15. boolean captured = false;
  16. try {
  17. System.out.println("tryLock(2,Times.SECONDS): " + captured);
  18. }finally {
  19. if (captured) {
  20. lock.unlock();
  21. }
  22. }
  23. }
  24. public static void main(String[] args) throws InterruptedException {
  25. final AttemptLocking al = new AttemptLocking();
  26. al.untimed();
  27. al.timed();
  28. new Thread(){ //可以将ReentrantLock看成是一个对象锁,谁先获得,将阻塞其他线程对该对象锁的获取
  29. {setDaemon(true);}
  30. public void run(){
  31. al.lock.lock();
  32. System.out.println("acquired");
  33. }
  34. }.start();
  35. Thread.yield();
  36. al.untimed();
  37. al.timed();
  38. }
  39. }

ReentrantLock允许尝试获取锁,如果没有获取锁的话,可以离开去做一些其他的事情,而不是一直等待这个锁被释放

原子性与易变性

没有中间操作的操作,比如boolean,Atomicxxx的类都是原子类,这些代码不需要被同步
volitile关键字确保了应用中的可视性,如果将一个域声明为volatile的,那么只要对这个域进行了写操作,所有的读操作都会看到这个修改,修改volatile之后会被立即写入主存中,volatile一般配合着原子操作一起工作

  1. public class AtomicityTest implements Runnable {
  2. private int i = 0;
  3. public int getValue(){
  4. return i;
  5. }
  6. private synchronized void evenIncrement(){
  7. i++;//这种操作都属于分布操作,所以可能会被读取到中间值
  8. //必须将getValue也给他设置成为synchronized的
  9. i++;
  10. }
  11. @Override
  12. public void run() {
  13. while (true) {
  14. evenIncrement();
  15. }
  16. }
  17. public static void main(String[] args) {
  18. ExecutorService exec = Executors.newCachedThreadPool();
  19. AtomicityTest at = new AtomicityTest();
  20. exec.execute(at);
  21. while (true) {
  22. int val = at.getValue();
  23. if (val % 2 != 0) {
  24. System.out.println(val);
  25. System.exit(0);
  26. }
  27. }
  28. }
  29. }
  1. public class SerialNumberGenerator {//产生序列数字的类
  2. private static volatile int serialNumber = 0;
  3. public static int nextSerialNumber() {
  4. return serialNumber++;
  5. }
  6. }

如果一个域可能会被多个任务同时访问,或者这些任务中至少有一个是写入任务,那么就应该将这个域设置成为volatile的

  1. class CircularSet {
  2. private int[] array;
  3. private int len;
  4. private int index = 0;
  5. public CircularSet(int size) {
  6. array = new int[size];
  7. len = size;
  8. for (int i = 0; i < size; i++)
  9. array[i] = -1;
  10. }
  11. public synchronized void add(int i) {
  12. array[index] = i;
  13. index = ++index % len;
  14. }
  15. public synchronized boolean contains(int val) {
  16. for (int i = 0; i < len; i++)
  17. if (array[i] == val) return true;
  18. return false;
  19. }
  20. }
  21. public class SerialNumberChecker {
  22. private static final int SIZE = 10;
  23. private static CircularSet serials =
  24. new CircularSet(1000);
  25. private static ExecutorService exec =
  26. Executors.newCachedThreadPool();
  27. static class SerialChecker implements Runnable {
  28. public void run() {
  29. while (true) {
  30. int serial =
  31. SerialNumberGenerator.nextSerialNumber();//产生多线程问题的源头
  32. if (serials.contains(serial)) {
  33. System.out.println("Duplicate: " + serial);
  34. System.exit(0);
  35. }
  36. serials.add(serial);
  37. }
  38. }
  39. }
  40. public static void main(String[] args) throws Exception {
  41. for (int i = 0; i < SIZE; i++)
  42. exec.execute(new SerialChecker());
  43. if (args.length > 0) {
  44. TimeUnit.SECONDS.sleep(new Integer(args[0]));
  45. System.out.println("No duplicates detected");
  46. System.exit(0);
  47. }
  48. }
  49. }

CircularSet:持有所产生的所有序列数,另外还包括一个内嵌的SerialChecker类,它可以保证序列数是唯一的,创建多个任务来竞争序列数,最终会获得重复的序列数

原子类

AtomicInteger(乐观锁的原理),AtomicLong,AtomicReference等特殊的原子性变量类,提供以下形式的原子性条件更新操作:boolean comparaAndSet(expectedValue,updateValue);
类似boolean,进行原子操作,没有中间状态

  1. public class AtomicIntegerTest implements Runnable {
  2. private AtomicInteger i = new AtomicInteger(0);//给定了初始值
  3. private int getValue(){return i.get();}//获取当前值
  4. private void evenIncrement(){
  5. i.addAndGet(2);}//以原子方式将给定值添加到当前值,返回更新后的值
  6. @Override
  7. public void run() {
  8. while (true) {
  9. evenIncrement();
  10. }
  11. }
  12. public static void main(String[] args) {
  13. ExecutorService exec = Executors.newCachedThreadPool();
  14. final AtomicIntegerTest ait = new AtomicIntegerTest();
  15. exec.execute(ait);
  16. while (true) {
  17. int val = ait.getValue();
  18. if (val % 2 != 0) {//原子操作没有中间变换的状态,所以不会出错的
  19. System.out.println(val);
  20. System.exit(0);
  21. }
  22. }
  23. }
  24. }
  1. public class AtomicEvenGenerator extends IntGenerator{
  2. private AtomicInteger currentEvenValue = new AtomicInteger(0);
  3. @Override
  4. public int next() {
  5. return currentEvenValue.addAndGet(2);
  6. }
  7. public static void main(String[] args) {
  8. EvenChecker.test(new AtomicEvenGenerator());
  9. }
  10. }

临界区

有时,只希望防止多个线程同时访问方法内部的部分代码而不是访问整个方法。通过这种方式分离出来的代码被称为临界区,它也使用synchronized关键字建立,这里synchronized被用来指定某个对象,此对象的锁被用来对花括号内的代码进行同步控制
通过使用同步控制块,而不是对整个方法进行同步控制,可以使多个任务访问对象的时间新能进行显著提高
如何将一个线程不安全类,变成线程安全的:

  1. class Pair { // Not thread-safe
  2. private int x, y;
  3. public Pair(int x, int y) {
  4. this.x = x;
  5. this.y = y;
  6. }
  7. public Pair() {
  8. this(0, 0);
  9. }
  10. public int getX() {
  11. return x;
  12. }
  13. public int getY() {
  14. return y;
  15. }
  16. public void incrementX() {
  17. x++;
  18. }
  19. public void incrementY() {
  20. y++;
  21. }
  22. public String toString() {
  23. return "x: " + x + ", y: " + y;
  24. }
  25. public class PairValuesNotEqualException
  26. extends RuntimeException {
  27. public PairValuesNotEqualException() {
  28. super("Pair values not equal: " + Pair.this);
  29. }
  30. }
  31. // Arbitrary invariant -- both variables must be equal:
  32. public void checkState() {
  33. if (x != y)
  34. throw new PairValuesNotEqualException();
  35. }
  36. }
  37. // Protect a Pair inside a thread-safe class:
  38. abstract class PairManager {
  39. AtomicInteger checkCounter = new AtomicInteger(0);
  40. protected Pair p = new Pair();
  41. private List<Pair> storage =
  42. Collections.synchronizedList(new ArrayList<Pair>());//线程安全的容器持有一个线程不安全的类
  43. //重新构建了一个pair的备份,该方法时线程安全的
  44. public synchronized Pair getPair() {//读的时候保证线程安全
  45. return new Pair(p.getX(), p.getY());
  46. }
  47. //仅仅为线程安全的容器中存入pair
  48. protected void store(Pair p) {
  49. storage.add(p);
  50. try {
  51. TimeUnit.MILLISECONDS.sleep(50);
  52. } catch (InterruptedException ignore) {
  53. }
  54. }
  55. //对x,y不断地生成一个点(x,y),并存储在线程安全的容器中
  56. public abstract void increment();
  57. }
  58. // Synchronize the entire method:
  59. class PairManager1 extends PairManager {//"写"的时候保证线程安全
  60. public synchronized void increment() {
  61. p.incrementX();
  62. p.incrementY();
  63. store(getPair());
  64. }
  65. }
  66. // Use a critical section:
  67. class PairManager2 extends PairManager {
  68. public void increment() {
  69. Pair temp;
  70. synchronized (this) {
  71. p.incrementX();
  72. p.incrementY();
  73. temp = getPair();
  74. }
  75. store(temp);
  76. }
  77. }
  78. class PairManipulator implements Runnable {
  79. private PairManager pm;
  80. public PairManipulator(PairManager pm) {
  81. this.pm = pm;
  82. }
  83. public void run() {
  84. while (true) //不断的生成一个平面轴上的点(x,y)
  85. pm.increment();
  86. }
  87. //pm.getPair() 拿到increment()生成的pair备份
  88. public String toString() {
  89. return "Pair: " + pm.getPair() +
  90. " checkCounter = " + pm.checkCounter.get();//用来统计操作的
  91. }
  92. }
  93. class PairChecker implements Runnable {
  94. private PairManager pm;
  95. public PairChecker(PairManager pm) {
  96. this.pm = pm;
  97. }
  98. public void run() {
  99. while (true) {
  100. pm.checkCounter.incrementAndGet();//不断的递增,不断的记录操作的次数
  101. pm.getPair().checkState();//同步的获取一个pair的备份,判断x,y是否相等
  102. }
  103. }
  104. }
  105. public class CriticalSection {
  106. static void
  107. testApproaches(PairManager pman1, PairManager pman2) {
  108. ExecutorService exec = Executors.newCachedThreadPool();
  109. PairManipulator
  110. pm1 = new PairManipulator(pman1),
  111. pm2 = new PairManipulator(pman2);
  112. PairChecker
  113. pcheck1 = new PairChecker(pman1),
  114. pcheck2 = new PairChecker(pman2);
  115. exec.execute(pm1);
  116. exec.execute(pm2);
  117. exec.execute(pcheck1);
  118. exec.execute(pcheck2);
  119. try {
  120. TimeUnit.MILLISECONDS.sleep(500);
  121. } catch (InterruptedException e) {
  122. System.out.println("Sleep interrupted");
  123. }
  124. System.out.println("pm1: " + pm1 + "\npm2: " + pm2);
  125. System.exit(0);
  126. }
  127. public static void main(String[] args) {
  128. PairManager
  129. pman1 = new PairManager1(),
  130. pman2 = new PairManager2();
  131. testApproaches(pman1, pman2);
  132. }
  133. }

在其他对象上同步

synchronized块必须给定一个在其上同步的对象,并且最合理的方式是:使用其方法正在被调用的对象:synchronized(this)。有时必须在另一个对象上同步,必须保证所有相关的任务都是在同一个对象上同步的

  1. class DualSynch {
  2. private Object syncObject = new Object();
  3. public void f(){
  4. synchronized(this) {//把他放在这里和直接放在方法里作用是一样的
  5. //锁的是SyncObject中的实例
  6. for (int i = 0; i < 5; i++) {
  7. try {
  8. TimeUnit.SECONDS.sleep(1);//不休眠不好看出来
  9. } catch (InterruptedException e) {
  10. e.printStackTrace();
  11. }
  12. System.out.println("f()");
  13. Thread.yield();
  14. }
  15. }
  16. }
  17. public void g() {
  18. synchronized (syncObject) {//这个锁的是Object的实例,所以和上面的方法互不相干
  19. for (int i = 0; i < 5; i++) {
  20. try {
  21. TimeUnit.SECONDS.sleep(1);
  22. } catch (InterruptedException e) {
  23. e.printStackTrace();
  24. }
  25. System.out.println("g()");
  26. Thread.yield();
  27. }
  28. }
  29. }
  30. }
  31. public class SyncObject {
  32. public static void main(String[] args) {
  33. final DualSynch dualSynch = new DualSynch();
  34. new Thread(){
  35. public void run() {
  36. dualSynch.f();
  37. }
  38. }.start();
  39. new Thread(){
  40. public void run(){
  41. dualSynch.g();
  42. }
  43. }.start();
  44. }
  45. }

上述代码中的两个方法是相互独立的:一个是对象锁,一个是实例锁

线程本地存储

防止任务在共享资源上产生冲突的第二种方式是根除对变量的共享。线程本地存储是一种自动化机制,可以为使用相同变量的每一个不同的线程都创建不同的存储
创建和管理线程本地存储可以由ThreadLocal类来实现

  1. class Accessor implements Runnable {
  2. private final int id;
  3. public Accessor(int idn) {
  4. id = idn;
  5. }
  6. public void run() {
  7. while (!Thread.currentThread().isInterrupted()) {//这个代码中并没有打断操作,所以是个死循环
  8. ThreadLocalVariableHolder.increment();
  9. System.out.println(this);
  10. Thread.yield();
  11. }
  12. }
  13. public String toString() {
  14. return "#" + id + ": " +
  15. ThreadLocalVariableHolder.get();
  16. }
  17. }
  18. public class ThreadLocalVariableHolder {
  19. private static ThreadLocal<Integer> value =
  20. new ThreadLocal<Integer>() {
  21. private Random rand = new Random(47);//每个线程都有专门的存储区域
  22. protected synchronized Integer initialValue() {
  23. return rand.nextInt(10000);
  24. }
  25. };
  26. public static void increment() {
  27. value.set(value.get() + 1);
  28. }//获取值之后加1
  29. public static int get() {
  30. return value.get();
  31. }
  32. public static void main(String[] args) throws Exception {
  33. ExecutorService exec = Executors.newCachedThreadPool();
  34. for (int i = 0; i < 5; i++)
  35. exec.execute(new Accessor(i));//id相同的都只加一
  36. TimeUnit.SECONDS.sleep(3);
  37. exec.shutdownNow();
  38. }
  39. }

如何证明ThreadLocal持有的内容是该线程原有的内容
ThreadLocal对象通常当作静态域存储
increament()和get()方法都不是synchronized的,因为ThreadLocal保证不会出现竞争条件

终结任务

首先装饰性花园记录每一个门的人数和总人数

  1. class Count {
  2. private int count = 0;
  3. private Random rand = new Random(47);
  4. public synchronized int increment() {
  5. return ++count;
  6. }
  7. public synchronized int value() {
  8. return count;
  9. }
  10. }
  11. class Entrance implements Runnable {
  12. private static Count count = new Count();
  13. private static List<Entrance> entrances =
  14. new ArrayList<>();
  15. private int number = 0;//表示的是每个入口的人数
  16. private final int id;
  17. private static volatile boolean canceled = false;
  18. public static void cancel() {
  19. canceled = true;
  20. }
  21. public Entrance(int id) {
  22. this.id = id;
  23. entrances.add(this);
  24. }
  25. public void run() {
  26. while (!canceled) {//单个入口的进入人数,以不断的递增1的方式统计,没有步进
  27. synchronized (this) {
  28. ++number;
  29. }
  30. print(this + " Total: " + count.increment());//总人数
  31. try {
  32. TimeUnit.MILLISECONDS.sleep(100);
  33. } catch (InterruptedException e) {
  34. print("sleep interrupted");
  35. }
  36. }
  37. print("Stopping " + this);
  38. }
  39. public synchronized int getValue() {
  40. return number;
  41. }
  42. public String toString() {
  43. return "Entrance " + id + ": " + getValue();
  44. }
  45. public static int getTotalCount() {
  46. return count.value();
  47. }
  48. public static int sumEntrances() {
  49. int sum = 0;
  50. for (Entrance entrance : entrances)
  51. sum += entrance.getValue();
  52. return sum;
  53. }
  54. }
  55. public class OrnamentalGarden {
  56. public static void main(String[] args) throws Exception {
  57. ExecutorService exec = Executors.newCachedThreadPool();
  58. for (int i = 0; i < 5; i++)
  59. exec.execute(new Entrance(i));
  60. TimeUnit.SECONDS.sleep(3);
  61. Entrance.cancel();
  62. exec.shutdown();
  63. if (!exec.awaitTermination(250, TimeUnit.MILLISECONDS))//250毫秒没有终止的话,就会打印这个信息
  64. print("Some tasks were not terminated!");
  65. print("Total: " + Entrance.getTotalCount());
  66. print("Sum of Entrances: " + Entrance.sumEntrances());
  67. }
  68. }

Entrance.canceled是一个volatile布尔标志,他只能被读取和赋值,所以不需要同步对其的访问,就可以安全的操作他

在阻塞时终结

image.png
image.png

中断

中断什么情况下才有用?
Thread类中的interrupt()方法,可以中介阻塞的任务
如果使用Executor,通过调用sumbit()而不是excutor()来启动任务,就可以持有该任务的上下文

  1. class SleepBlocked implements Runnable {
  2. public void run() {
  3. try {
  4. TimeUnit.SECONDS.sleep(100);
  5. } catch (InterruptedException e) {
  6. print("InterruptedException");
  7. }
  8. print("Exiting SleepBlocked.run()");
  9. }
  10. }
  11. class IOBlocked implements Runnable {
  12. private InputStream in;
  13. public IOBlocked(InputStream is) {
  14. in = is;
  15. }
  16. public void run() {
  17. try {
  18. print("Waiting for read():");
  19. in.read();
  20. } catch (IOException e) {
  21. if (Thread.currentThread().isInterrupted()) {
  22. print("Interrupted from blocked I/O");
  23. } else {
  24. throw new RuntimeException(e);
  25. }
  26. }
  27. print("Exiting IOBlocked.run()");
  28. }
  29. }
  30. class SynchronizedBlocked implements Runnable {
  31. public synchronized void f() {
  32. while (true)
  33. Thread.yield();
  34. }
  35. public SynchronizedBlocked() {
  36. new Thread() {
  37. public void run() {
  38. f();
  39. }
  40. }.start();
  41. }
  42. public void run() {
  43. print("Trying to call f()");
  44. f();
  45. print("Exiting SynchronizedBlocked.run()");
  46. }
  47. }
  48. public class Interrupting {
  49. private static ExecutorService exec =
  50. Executors.newCachedThreadPool();
  51. static void test(Runnable r) throws InterruptedException {
  52. Future<?> f = exec.submit(r);
  53. TimeUnit.MILLISECONDS.sleep(100);
  54. print("Interrupting " + r.getClass().getName());
  55. f.cancel(true);//取消任务的执行
  56. print("Interrupt sent to " + r.getClass().getName());
  57. }
  58. public static void main(String[] args) throws Exception {
  59. // test(new SleepBlocked()); //成功打断,没有问题
  60. // test(new IOBlocked(System.in));//无法打断
  61. test(new SynchronizedBlocked());
  62. TimeUnit.SECONDS.sleep(3);
  63. print("Aborting with System.exit(0)");
  64. System.exit(0);
  65. }
  66. }

I/O和在synchronized块上的等待时不可中断的,不能中断正在试图获取synchornized锁或者试图执行I/O操作的线程
I/O阻塞无法通过外部的形式进行打断,I/OException属于当前线程中断的一种形式

  1. class IOBlocked implements Runnable {
  2. private InputStream is;
  3. public IOBlocked(InputStream inputStream) {
  4. is = inputStream;
  5. }
  6. @Override
  7. public void run() {
  8. System.out.println("waiting for read");
  9. try {
  10. while (true) {
  11. int temp = is.read();//虽然发送了取消得指令,但依旧在此阻塞
  12. if((char)temp == 'q'){
  13. throw new IOException("quit");
  14. //break;
  15. }
  16. }
  17. } catch (IOException e) {
  18. if (Thread.currentThread().isInterrupted()) {
  19. System.out.println("Interrupted from IO");
  20. } else {
  21. throw new RuntimeException(e);
  22. }
  23. }
  24. System.out.println("Exiting IOBlocked run()...");
  25. }
  26. }

如何跳出死循环

  1. private static AtomicInteger atomicInteger = new AtomicInteger();
  2. private static volatile boolean flag = false;
  3. public synchronized void f(){
  4. /* while (true) {
  5. Thread.yield();
  6. }*/
  7. while (!flag) { //跳出死循环的处理
  8. //Thread.yield();
  9. atomicInteger.addAndGet(2);
  10. if(atomicInteger.get()==50){
  11. flag = true;
  12. }
  13. }
  14. }
  1. public class CloseResource {
  2. public static void main(String[] args) throws Exception {
  3. ExecutorService exec = Executors.newCachedThreadPool();
  4. ServerSocket server = new ServerSocket(8080);
  5. InputStream socketInput =
  6. new Socket("localhost", 8080).getInputStream();
  7. exec.execute(new IOBlocked(socketInput));
  8. exec.execute(new IOBlocked(System.in));
  9. TimeUnit.MILLISECONDS.sleep(100);
  10. print("Shutting down all threads");
  11. exec.shutdownNow();
  12. TimeUnit.SECONDS.sleep(1);
  13. print("Closing " + socketInput.getClass().getName());
  14. socketInput.close();
  15. TimeUnit.SECONDS.sleep(1);
  16. print("Closing " + System.in.getClass().getName());
  17. System.in.close();
  18. }
  19. }

在shutdownNow()被调用之后以及在两个输入流上调用close()之前的延迟强调的是一旦底层资源被关闭,任务将接触阻塞

被互斥所阻塞

如果尝试在一个对象上调用其synchronized方法,如果这个对象的锁已经被其他任务获得,那么调用任务将被挂起,知道这个锁可以被获得
同一个互斥可以如何能被同一个任务多次获得

  1. public class MultiLock {
  2. public synchronized void f1(int count) {
  3. if (count-- > 0) {
  4. System.out.println("f1() calling f2() with count " + count);
  5. f2(count);
  6. }
  7. }
  8. public synchronized void f2(int count) {
  9. if (count-- > 0) {
  10. System.out.println("f2() calling f1() with count " + count);
  11. f1(count);
  12. }
  13. }
  14. public static void main(String[] args) {
  15. final MultiLock multiLock = new MultiLock();
  16. new Thread(){
  17. public void run(){
  18. multiLock.f1(5);
  19. }
  20. }.start();
  21. }
  22. }

一个任务应该能够调用同一个对象的其他的synchronized方法,并且这个任务已经持有锁了
如果将if方法换成while的话,从四开始f1调用f2 而且他还在循环,不断的调用

ReentrantLock上阻塞的任务具有可以被打断的特性

  1. class BlockedMutex {
  2. private Lock lock = new ReentrantLock();
  3. public BlockedMutex(){
  4. //lock.lock();//在构造器中就获取了一把锁,并且没有被释放,这样接下来的获取锁操作就会被阻塞
  5. }
  6. public void f() {
  7. try {
  8. //此方法获取可以被打断的锁,
  9. // lock.lockInterruptibly();
  10. //这个获取的锁是不会被打断的
  11. lock.lock();
  12. System.out.println("lock acquired in f()");
  13. } catch (Exception e) {
  14. System.out.println("Interrupted from lock acquisition");
  15. }
  16. }
  17. }
  18. class Block2 implements Runnable {
  19. BlockedMutex blocked = new BlockedMutex();
  20. @Override
  21. public void run() {
  22. System.out.println("Waiting for f() in BlockedMutex");
  23. blocked.f();
  24. System.out.println("Broken out of blocked call");
  25. }
  26. }
  27. public class Interrupting2 {
  28. public static void main(String[] args) throws InterruptedException {
  29. Thread t = new Thread(new Block2());
  30. t.start();
  31. TimeUnit.SECONDS.sleep(1);
  32. System.out.println("Issuing t.interrupt()");
  33. t.interrupt();
  34. }
  35. }

检查中断

当在线程上调用interrupt()是,中断发生的唯一时刻是任务要进入到阻塞操作中,或者已经在阻塞操作内部了(除了不可被中断的I/O操作和synchronized方法之外)
如果在run跑的时候没有任何阻塞操作,那将如何退出?

线程之间的协作

如何使任务彼此之间可以协作,使得多个任务可以一起工作去解决某个问题
当任务协作的时候,关键使这些任务之间的握手,为了实现这些握手,我们使用了相同的基础特性“互斥”
在互斥之上,我们为任务添加了一种途径,可以将自身挂起,直到外部条件发生变化比如volatile boolean,这中握手可以通过Object中的wait()方法和notify(),notifyAll()方法来安全的实现

wait()和notifyAll

wait()等待某个条件发生变化,这中条件一般是由另一个任务来改变,wait在等待外部条件产生变化的时候会将任务挂起,只有notify或notifyAll发生的时候,这个任务才会被唤醒并去检查锁产生的变化。可以说wait提供了一种在任务之间对活动同步的方式
sleep和yield方法都并没有释放锁,但是,当一个任务在方法中遇见了对wait的调用的时候,线程的执行将会被挂起,对象上的锁将会被释放,因为wait方法会释放锁,这就表明另一个任务可以获得这个锁,因此在该对象中的其他synchronized方法可以在wait期间被调用,因为此时未被锁
对于wait而言:在wait期间锁使被释放的,并且可以通过notify或者notifyAll,或者 零时间到期,从wait中恢复执行
wait,notify,notifyAll方法都是属于Object的一部分,而不是属于Thread的一部分

  1. class Car {
  2. private boolean waxOn = false;
  3. public synchronized void waxed() {
  4. waxOn = true;
  5. notifyAll();
  6. }
  7. public synchronized void buffed() {
  8. waxOn = false;
  9. notifyAll();
  10. }
  11. public synchronized void waitForWaxing()
  12. throws InterruptedException {
  13. while (waxOn == false)
  14. wait();
  15. }
  16. public synchronized void waitForBuffing()
  17. throws InterruptedException {
  18. while (waxOn == true)
  19. wait();
  20. }
  21. }
  22. class WaxOn implements Runnable {
  23. private Car car;
  24. public WaxOn(Car c) {
  25. car = c;
  26. }
  27. public void run() {
  28. try {
  29. while (!Thread.interrupted()) {
  30. printnb("Wax On! ");
  31. TimeUnit.MILLISECONDS.sleep(200);
  32. car.waxed();
  33. car.waitForBuffing();
  34. }
  35. } catch (InterruptedException e) {
  36. print("Exiting via interrupt");
  37. }
  38. print("Ending Wax On task");
  39. }
  40. }
  41. class WaxOff implements Runnable {
  42. private Car car;
  43. public WaxOff(Car c) {
  44. car = c;
  45. }
  46. public void run() {
  47. try {
  48. while (!Thread.interrupted()) {
  49. car.waitForWaxing();
  50. printnb("Wax Off! ");
  51. TimeUnit.MILLISECONDS.sleep(200);
  52. car.buffed();
  53. }
  54. } catch (InterruptedException e) {
  55. print("Exiting via interrupt");
  56. }
  57. print("Ending Wax Off task");
  58. }
  59. }
  60. public class WaxOMatic {
  61. public static void main(String[] args) throws Exception {
  62. Car car = new Car();
  63. ExecutorService exec = Executors.newCachedThreadPool();
  64. exec.execute(new WaxOff(car));
  65. exec.execute(new WaxOn(car));
  66. TimeUnit.SECONDS.sleep(5);
  67. exec.shutdownNow();
  68. }
  69. }

错误的信号

假设T1通知T2使以以下的方式实现的:
image.png
应该将while循环放在synchronized的下面,这样避免T1通知T2时T2已经进入下一次循环,错过了这次信号

notify和notifyAll

因为可能会有多个任务在单个Car对象上处于wait状态,因此调用notifyAll比调用notify更加的安全
并且notifyAll还是同一把锁上面的正在wait的线程

  1. class Blocker {
  2. synchronized void waitingCall() {
  3. try {
  4. while (!Thread.interrupted()) {
  5. wait();
  6. System.out.println(Thread.currentThread() + " ");
  7. }
  8. } catch (InterruptedException e) {
  9. }
  10. }
  11. synchronized void prod() {
  12. notify();
  13. }
  14. synchronized void prodAll() {
  15. notifyAll();
  16. }
  17. }
  18. class Task implements Runnable {
  19. static Blocker blocker = new Blocker();
  20. @Override
  21. public void run() {
  22. blocker.waitingCall();
  23. }
  24. }
  25. class Task2 implements Runnable {
  26. static Blocker blocker = new Blocker();
  27. @Override
  28. public void run() {
  29. blocker.waitingCall();
  30. }
  31. }
  32. public class NotifyVsNotifyAll {
  33. public static void main(String[] args) throws InterruptedException {
  34. ExecutorService exec = Executors.newCachedThreadPool();
  35. for (int i = 0; i < 5; i++) {
  36. exec.execute(new Task());
  37. }
  38. Timer timer = new Timer();
  39. timer.scheduleAtFixedRate(new TimerTask() {
  40. boolean prod = true;
  41. @Override
  42. public void run() {
  43. if (prod) {
  44. System.out.println("\nnotify()");
  45. Task.blocker.prod();
  46. prod = false;
  47. } else {
  48. System.out.println("\nnotifyall");
  49. Task.blocker.prodAll();
  50. prod = true;
  51. }
  52. }
  53. }, 400, 400);
  54. TimeUnit.SECONDS.sleep(5);
  55. timer.cancel();
  56. System.out.println("\nTimer canceled");
  57. TimeUnit.MILLISECONDS.sleep(500);
  58. System.out.println("Task2.blocker.prodAll");
  59. Task2.blocker.prodAll();//blocker是static的,所以只有一份,调不调这个无所谓
  60. TimeUnit.MILLISECONDS.sleep(500);
  61. System.out.println("\nShutting Down");
  62. exec.shutdown();
  63. }
  64. }

因为循环的原因,释放某一个线程之后,这个线程又走循环,再次阻塞,所以再次notifyAll的时候该线程再次被恢复执行

生产者和消费者实例

  1. class Meal {
  2. private final int orderNum;
  3. public Meal(int orderNum) {
  4. this.orderNum = orderNum;
  5. }
  6. public String toString() {
  7. return "Meal " + orderNum;
  8. }
  9. }
  10. class WaitPerson implements Runnable {
  11. private final Restaurant restaurant;
  12. public WaitPerson(Restaurant r) {
  13. restaurant = r;
  14. }
  15. public void run() {
  16. try {
  17. while (!Thread.interrupted()) {
  18. synchronized (this) {
  19. while (restaurant.meal == null)//第一次之后就、 由于厨师那里一直在count++,所里这里不会为空了
  20. //会一直打印信息
  21. wait();
  22. }
  23. print("Waitperson got " + restaurant.meal);
  24. synchronized (restaurant.chef) {
  25. restaurant.meal = null;
  26. restaurant.chef.notifyAll();
  27. }
  28. }
  29. } catch (InterruptedException e) {
  30. print("WaitPerson interrupted");
  31. }
  32. }
  33. }
  34. class Chef implements Runnable {
  35. private final Restaurant restaurant;
  36. private int count = 0;
  37. public Chef(Restaurant r) {
  38. restaurant = r;
  39. }
  40. public void run() {
  41. try {
  42. while (!Thread.interrupted()) {
  43. synchronized (this) {
  44. while (restaurant.meal != null)
  45. wait();
  46. }
  47. if (++count == 10) { //不断的循环,对meal进行++
  48. print("Out of food, closing");
  49. restaurant.exec.shutdownNow();//对当前线程设置成为打断状态
  50. }
  51. printnb("Order up! ");
  52. synchronized (restaurant.waitPerson) {
  53. //设置Meal初始是0
  54. restaurant.meal = new Meal(count);
  55. restaurant.waitPerson.notifyAll();
  56. }
  57. TimeUnit.MILLISECONDS.sleep(100);
  58. }
  59. } catch (InterruptedException e) {
  60. print("Chef interrupted");
  61. }
  62. }
  63. }
  64. public class Restaurant {
  65. Meal meal;
  66. ExecutorService exec = Executors.newCachedThreadPool();
  67. final WaitPerson waitPerson = new WaitPerson(this);
  68. final Chef chef = new Chef(this);
  69. public Restaurant() {
  70. exec.execute(chef);
  71. exec.execute(waitPerson);
  72. }
  73. public static void main(String[] args) {
  74. new Restaurant();
  75. }
  76. }

使用显式的Lock和Condition对象
使用互斥并且允许将任务挂起的类是Condition,可以通过在Condition上调用await来挂起一个任务

  1. class Car {
  2. private Lock lock = new ReentrantLock();
  3. private Condition condition = lock.newCondition();
  4. private boolean waxOn = false;
  5. public void waxed() {
  6. lock.lock();
  7. try {
  8. waxOn = true; // Ready to buff
  9. condition.signalAll();
  10. } finally {
  11. lock.unlock();
  12. }
  13. }
  14. public void buffed() {
  15. lock.lock();
  16. try {
  17. waxOn = false; // Ready for another coat of wax
  18. condition.signalAll();
  19. } finally {
  20. lock.unlock();
  21. }
  22. }
  23. public void waitForWaxing() throws InterruptedException {
  24. lock.lock();
  25. try {
  26. while(waxOn == false)
  27. condition.await();
  28. } finally {
  29. lock.unlock();
  30. }
  31. }
  32. public void waitForBuffing() throws InterruptedException{
  33. lock.lock();
  34. try {
  35. while(waxOn == true)
  36. condition.await();
  37. } finally {
  38. lock.unlock();
  39. }
  40. }
  41. }
  42. class WaxOn2 implements Runnable {
  43. private Car car;
  44. public WaxOn2(Car c) { car = c; }
  45. public void run() {
  46. try {
  47. while(!Thread.interrupted()) {
  48. printnb("Wax On! ");
  49. TimeUnit.MILLISECONDS.sleep(200);
  50. car.waxed();
  51. car.waitForBuffing();
  52. }
  53. } catch(InterruptedException e) {
  54. print("Exiting via interrupt");
  55. }
  56. print("Ending Wax On task");
  57. }
  58. }
  59. class WaxOff2 implements Runnable {
  60. private Car car;
  61. public WaxOff2(Car c) { car = c; }
  62. public void run() {
  63. try {
  64. while(!Thread.interrupted()) {
  65. car.waitForWaxing();
  66. printnb("Wax Off! ");
  67. TimeUnit.MILLISECONDS.sleep(200);
  68. car.buffed();
  69. }
  70. } catch(InterruptedException e) {
  71. print("Exiting via interrupt");
  72. }
  73. print("Ending Wax Off task");
  74. }
  75. }
  76. public class WaxOMatic2 {
  77. public static void main(String[] args) throws Exception {
  78. Car car = new Car();
  79. ExecutorService exec = Executors.newCachedThreadPool();
  80. exec.execute(new WaxOff2(car));
  81. exec.execute(new WaxOn2(car));
  82. TimeUnit.SECONDS.sleep(5);
  83. exec.shutdownNow();
  84. }
  85. }

对lock的调用都必须紧跟一个try-finally子句,来保证在所有的情况下都可以释放锁
Lock和Condition只有在更加困难或者复杂的多线程问题中是必须的

生产者消费者与队列

使用同步队列来解决任务协作问题,同步队列在任何时刻,都只允许一个任务接口插入或者移除元素

  1. class LiftOffRunner implements Runnable {
  2. private BlockingQueue<LiftOff> rockets;
  3. public LiftOffRunner(BlockingQueue<LiftOff> rockets) {
  4. this.rockets = rockets;
  5. }
  6. public void add(LiftOff lo) {
  7. try {
  8. rockets.put(lo);//生产者:在队列中添加元素
  9. } catch (InterruptedException e) {
  10. System.out.println("Interrupted during put()");
  11. }
  12. }
  13. @Override
  14. public void run() {
  15. try {
  16. while (!Thread.interrupted()) {
  17. LiftOff rocket = rockets.take();//消费者:从队列中取出元素
  18. rocket.run();
  19. }
  20. } catch (InterruptedException e) {
  21. System.out.println("Waking from take()");
  22. }
  23. System.out.println("Exiting LiftOffRunner");
  24. }
  25. }
  26. public class TestBlockingQueues {
  27. /*static void getKey() {
  28. try {
  29. new BufferedReader(new InputStreamReader(System.in)).readLine();
  30. } catch (IOException e) {
  31. throw new RuntimeException();
  32. }
  33. }
  34. static void getKey(String msg) {
  35. System.out.println(msg);
  36. getKey();
  37. }*/
  38. static void test(String msg, BlockingQueue<LiftOff> queue) {
  39. System.out.println(msg);
  40. LiftOffRunner runner = new LiftOffRunner(queue);
  41. Thread t = new Thread(runner);
  42. t.start();
  43. try {
  44. TimeUnit.SECONDS.sleep(3);
  45. } catch (InterruptedException e) {
  46. e.printStackTrace();
  47. }
  48. for (int i = 0; i < 5; i++) {
  49. runner.add(new LiftOff(5));
  50. }
  51. //getKey("press 'Enter'(" + msg + ")");
  52. t.interrupt();
  53. System.out.println("Finish" + msg + "test");
  54. }
  55. public static void main(String[] args) {
  56. //test("LinkedBlockingQueue",new LinkedBlockingQueue<>());//无界的阻塞队列
  57. //test("ArrayBlockQueue",new ArrayBlockingQueue<>(3));//有容量的阻塞队列,要初始化他的容量
  58. test("SynchronousQueue",new SynchronousQueue<>());//同步队列,一个一个来
  59. }
  60. }

容量为null的时候,消费者挂起。容量满的时候,生产者挂起。待消费一个任务的时候,再去添加任务。

土司BlockingQueue

  1. class Toast {
  2. public enum Status {DRY, BUTTERED, JAMMED}
  3. private Status status = Status.DRY;
  4. private final int id;
  5. public Toast(int idn) {
  6. id = idn;
  7. }
  8. public void butter() {//抹上黄油
  9. status = Status.BUTTERED;
  10. }
  11. public void jam() {//抹上果酱
  12. status = Status.JAMMED;
  13. }
  14. public Status getStatus() {
  15. return status;
  16. }
  17. public int getId() {
  18. return id;
  19. }
  20. public String toString() {
  21. return "Toast " + id + ": " + status;
  22. }
  23. }
  24. class ToastQueue extends LinkedBlockingQueue<Toast> {
  25. }
  26. class Toaster implements Runnable {
  27. private ToastQueue toastQueue;
  28. private int count = 0;
  29. private Random rand = new Random(47);
  30. public Toaster(ToastQueue tq) {
  31. toastQueue = tq;
  32. }
  33. public void run() {
  34. try {
  35. while (!Thread.interrupted()) {
  36. TimeUnit.MILLISECONDS.sleep(
  37. 100 + rand.nextInt(500));
  38. Toast t = new Toast(count++);//生产吐司实例
  39. print(t);
  40. toastQueue.put(t);//将吐司放进队列
  41. }
  42. } catch (InterruptedException e) {
  43. print("Toaster interrupted");
  44. }
  45. print("Toaster off");
  46. }
  47. }
  48. class Butterer implements Runnable {
  49. private ToastQueue dryQueue, butteredQueue;
  50. public Butterer(ToastQueue dry, ToastQueue buttered) {
  51. dryQueue = dry;
  52. butteredQueue = buttered;
  53. }
  54. public void run() {
  55. try {
  56. while (!Thread.interrupted()) {
  57. // Blocks until next piece of toast is available:
  58. Toast t = dryQueue.take();//从吐司队列中取出生产后的土司
  59. t.butter();//抹上黄油
  60. print(t);
  61. butteredQueue.put(t);//将从吐司队列中取出的,并且抹好了黄油的吐司放进黄油队列
  62. }
  63. } catch (InterruptedException e) {
  64. print("Butterer interrupted");
  65. }
  66. print("Butterer off");
  67. }
  68. }
  69. // Apply jam to buttered toast:
  70. class Jammer implements Runnable {
  71. private ToastQueue butteredQueue, finishedQueue;
  72. public Jammer(ToastQueue buttered, ToastQueue finished) {
  73. butteredQueue = buttered;
  74. finishedQueue = finished;
  75. }
  76. public void run() {
  77. try {
  78. while (!Thread.interrupted()) {
  79. Toast t = butteredQueue.take();//从黄油队列中取出吐司
  80. t.jam();//再抹上果酱
  81. print(t);
  82. finishedQueue.put(t);//将从黄油队列中取出的,抹好果酱的放在完成队列
  83. }
  84. } catch (InterruptedException e) {
  85. print("Jammer interrupted");
  86. }
  87. print("Jammer off");
  88. }
  89. }
  90. class Eater implements Runnable {
  91. private ToastQueue finishedQueue;
  92. private int counter = 0;
  93. public Eater(ToastQueue finished) {
  94. finishedQueue = finished;
  95. }
  96. public void run() {
  97. try {
  98. while (!Thread.interrupted()) {
  99. Toast t = finishedQueue.take();//从完成队列中取出吐司
  100. if (t.getId() != counter++ ||
  101. t.getStatus() != Toast.Status.JAMMED) {//确保是按照顺序过来的或者确保已经是抹好果酱的
  102. print(">>>> Error: " + t);
  103. System.exit(1);
  104. } else
  105. print("Chomp! " + t);
  106. }
  107. } catch (InterruptedException e) {
  108. print("Eater interrupted");
  109. }
  110. print("Eater off");
  111. }
  112. }
  113. public class ToastOMatic {
  114. public static void main(String[] args) throws Exception {
  115. ToastQueue dryQueue = new ToastQueue(),
  116. butteredQueue = new ToastQueue(),
  117. finishedQueue = new ToastQueue();
  118. ExecutorService exec = Executors.newCachedThreadPool();
  119. exec.execute(new Toaster(dryQueue));
  120. exec.execute(new Butterer(dryQueue, butteredQueue));
  121. exec.execute(new Jammer(butteredQueue, finishedQueue));
  122. exec.execute(new Eater(finishedQueue));
  123. TimeUnit.SECONDS.sleep(5);
  124. exec.shutdownNow();
  125. }
  126. }

注意:这个实例没有任何的显示的同步,(Lock和synchronized),因为同步队列和系统隐式的管理了,每片吐司任何时刻只能由一个任务在操作

死锁

  1. public class DeadLockDemo {
  2. private final Object left = new Object();
  3. private final Object right = new Object();
  4. public void tom() {
  5. synchronized (left) {
  6. try {
  7. TimeUnit.SECONDS.sleep(1);
  8. } catch (InterruptedException e) {
  9. e.printStackTrace();
  10. }
  11. }
  12. synchronized (right) {
  13. System.out.println("tom 拿到了筷子");//拿到右边的筷子
  14. }
  15. }
  16. public void kobe() {
  17. synchronized (right) {
  18. try {
  19. TimeUnit.SECONDS.sleep(1);
  20. } catch (InterruptedException e) {
  21. e.printStackTrace();
  22. }
  23. }
  24. synchronized (left) {
  25. System.out.println("kobe 拿到了筷子");//拿到左边的筷子
  26. }
  27. }
  28. public static void main(String[] args) {
  29. final DeadLockDemo deadLockDemo = new DeadLockDemo();
  30. new Thread(deadLockDemo::tom);
  31. new Thread(deadLockDemo::kobe);
  32. }
  33. }

image.png

新类库中的构件

CountDownLarch(底层由Node实现)

用来同步一个或者多个任务,强制他们等待其他任务执行的一组操作完成,并且他被设计为只触发一次,计数值不能被重置。CountDownLatch的典型用法是将一个程序分为n个互相独立的可解决的任务,并且创建值为0的CountDownLatch,当每个任务完成的时候,都会在这个锁村器上调用countDoun,等待问题被解决的任务在这个锁存器上调用await,将他们自己拦住,直到锁存器计数结束

  1. class TaskPortion implements Runnable {
  2. private static int count = 0;
  3. private final int id = count++;
  4. private static Random rand = new Random(47);
  5. private final CountDownLatch latch;
  6. TaskPortion(CountDownLatch latch) {
  7. this.latch = latch;
  8. }
  9. @Override
  10. public void run() {
  11. try {
  12. doWork();
  13. latch.countDown();//countDown--,直到为0,所有的任务才完成
  14. } catch (InterruptedException e) {
  15. }
  16. }
  17. public void doWork() throws InterruptedException {
  18. TimeUnit.MILLISECONDS.sleep(rand.nextInt(2000));//随机休眠,模拟工作时间
  19. System.out.println(this + "completed");
  20. }
  21. @Override
  22. public String toString() {
  23. return String.format("%1$-3d", id);
  24. }
  25. }
  26. class WaitingTask implements Runnable {
  27. private static int count = 0;
  28. private final int id = count++;
  29. private CountDownLatch latch;
  30. WaitingTask(CountDownLatch latch) {
  31. this.latch = latch;
  32. }
  33. @Override
  34. public void run() {
  35. try {
  36. latch.await();//如果上面的任务没有完成的话,会一直在这里等待
  37. System.out.println("Latch barrier passed for" + this);
  38. } catch (InterruptedException e) {
  39. System.out.println(this + "interruption");
  40. }
  41. }
  42. @Override
  43. public String toString() {
  44. return String.format("Waiting %1$-3d", id);
  45. }
  46. }
  47. public class CountDownLatchDemo {
  48. static final int SIZE = 100;
  49. public static void main(String[] args) {
  50. final ExecutorService exec = Executors.newCachedThreadPool();
  51. CountDownLatch latch = new CountDownLatch(SIZE);
  52. for (int i = 0; i < 10; i++) {
  53. exec.execute(new WaitingTask(latch));
  54. }
  55. for (int i = 0; i < SIZE ; i++) { //如果不是SIZE的个数的话(小于SIZE) WaitingTask会一直在那里等待
  56. exec.execute(new TaskPortion(latch));
  57. }
  58. System.out.println("Launched all tasks");
  59. exec.shutdown();
  60. }
  61. }

CyclicBarrier

适用于以下情况:创建一组任务,他们并行的执行工作,然后在进行下一个步骤之前等待,直到所有任务都完成(看起来有些像join())和CountDownLatch的区别:后者是只触发一次的时间,而CyclicBarrier可以多次重用
自己写一个例子

  1. class Horse implements Runnable {
  2. private static int counter = 0;
  3. private final int id = counter++;//计数
  4. private int strides = 0;//初始步数为0
  5. private static Random rand = new Random(47);
  6. private static CyclicBarrier barrier;
  7. public Horse(CyclicBarrier b) { //传进来CyclicBarrier
  8. barrier = b;
  9. }
  10. public synchronized int getStrides() {//获取步数
  11. return strides;
  12. }
  13. public void run() {
  14. try {
  15. while (!Thread.interrupted()) {
  16. synchronized (this) {
  17. strides += rand.nextInt(3);//随机前进0,1,2步
  18. }
  19. barrier.await();//等待所有的马都随机获取完了步数再继续
  20. }
  21. } catch (InterruptedException e) {
  22. } catch (BrokenBarrierException e) {
  23. throw new RuntimeException(e);
  24. }
  25. }
  26. public String toString() {
  27. return "Horse " + id + " ";
  28. }
  29. public String tracks() {
  30. StringBuilder s = new StringBuilder();
  31. for (int i = 0; i < getStrides(); i++)
  32. s.append("*");
  33. s.append(id);
  34. return s.toString();
  35. }
  36. }
  37. public class HorseRace {
  38. static final int FINISH_LINE = 75;
  39. private final List<Horse> horses = new ArrayList<>();
  40. private final ExecutorService exec =
  41. Executors.newCachedThreadPool();
  42. private CyclicBarrier barrier;
  43. public HorseRace(int nHorses, final int pause) {//传进来马的个数和每次所有的马获取步数后的休眠时间
  44. barrier = new CyclicBarrier(nHorses, () -> {
  45. StringBuilder s = new StringBuilder();
  46. for (int i = 0; i < FINISH_LINE; i++)
  47. s.append("=");
  48. print(s);
  49. for (Horse horse : horses)
  50. print(horse.tracks());//打印每批马的轨迹
  51. for (Horse horse : horses)
  52. if (horse.getStrides() >= FINISH_LINE) {//当马的步数大于75的时候就停止
  53. print(horse + "won!");
  54. exec.shutdownNow();
  55. return;
  56. }
  57. try {
  58. TimeUnit.MILLISECONDS.sleep(pause);
  59. } catch (InterruptedException e) {
  60. print("barrier-action sleep interrupted");
  61. }
  62. });
  63. for (int i = 0; i < nHorses; i++) {
  64. Horse horse = new Horse(barrier);
  65. horses.add(horse);
  66. exec.execute(horse);
  67. }
  68. }
  69. public static void main(String[] args) {
  70. int nHorses = 7;
  71. int pause = 200;
  72. new HorseRace(nHorses, pause);
  73. }
  74. }

DelayQueue

是一个无界的阻塞队列,队列中的对象只能等到他到期时候才能从队列中取出,先取出时间最短的

  1. class DelayedTask implements Runnable, Delayed {
  2. private static int counter = 0;
  3. private final int id = counter++;
  4. private final int delta;//初始延迟时间
  5. private final long trigger;//触发时间
  6. protected static List<DelayedTask> sequence =
  7. new ArrayList<>();
  8. public DelayedTask(int delayInMilliseconds) {//在构造器中传进来初始延迟时间,设置触发时间,添加到集合中
  9. delta = delayInMilliseconds;
  10. trigger = System.nanoTime() +
  11. NANOSECONDS.convert(delta, MILLISECONDS);
  12. sequence.add(this);
  13. }
  14. public long getDelay(TimeUnit unit) {
  15. return unit.convert(
  16. trigger - System.nanoTime(), NANOSECONDS);
  17. }
  18. public int compareTo(Delayed arg) {//比较触发时间
  19. DelayedTask that = (DelayedTask) arg;
  20. if (trigger < that.trigger) return -1;
  21. if (trigger > that.trigger) return 1;
  22. return 0;
  23. }
  24. public void run() {
  25. printnb(this + " ");
  26. }
  27. public String toString() {
  28. return String.format("[%1$-4d]", delta) +
  29. " Task " + id;
  30. }
  31. public String summary() {
  32. return "(" + id + ":" + delta + ")";
  33. }
  34. public static class EndSentinel extends DelayedTask { //末端哨兵机制
  35. private ExecutorService exec;
  36. public EndSentinel(int delay, ExecutorService e) {
  37. super(delay);
  38. exec = e;
  39. }
  40. public void run() { //将集合中元素id和延迟时间打印出来
  41. for (DelayedTask pt : sequence) {
  42. printnb(pt.summary() + " ");
  43. }
  44. print();
  45. print(this + " Calling shutdownNow()");
  46. exec.shutdownNow();
  47. }
  48. }
  49. }
  50. class DelayedTaskConsumer implements Runnable {
  51. private DelayQueue<DelayedTask> q;
  52. public DelayedTaskConsumer(DelayQueue<DelayedTask> q) {
  53. this.q = q;
  54. }
  55. public void run() {
  56. try {
  57. while (!Thread.interrupted())
  58. q.take().run(); //取出来元素并且打印出来
  59. } catch (InterruptedException e) {
  60. }
  61. print("Finished DelayedTaskConsumer");
  62. }
  63. }
  64. public class DelayQueueDemo {
  65. public static void main(String[] args) {
  66. Random rand = new Random(47);
  67. ExecutorService exec = Executors.newCachedThreadPool();
  68. DelayQueue<DelayedTask> queue =
  69. new DelayQueue<>();
  70. for (int i = 0; i < 20; i++)
  71. queue.put(new DelayedTask(rand.nextInt(5000)));
  72. queue.add(new DelayedTask.EndSentinel(5000, exec));
  73. exec.execute(new DelayedTaskConsumer(queue));
  74. }
  75. }

PriorityBlockingQueue

优先级队列,具有可阻塞的读取操作

  1. class PrioritizedTask implements
  2. Runnable, Comparable<PrioritizedTask> {
  3. private Random rand = new Random(47);
  4. private static int counter = 0;
  5. private final int id = counter++;
  6. private final int priority;//优先级
  7. protected static List<PrioritizedTask> sequence =
  8. new ArrayList<>();
  9. public PrioritizedTask(int priority) {//从构造器中设置优先级,并且将其添加到队列中
  10. this.priority = priority;
  11. sequence.add(this);
  12. }
  13. public int compareTo(PrioritizedTask arg) {//比较优先级 正序
  14. return priority < arg.priority ? 1 :
  15. (priority > arg.priority ? -1 : 0);
  16. }
  17. public void run() {
  18. try {
  19. TimeUnit.MILLISECONDS.sleep(rand.nextInt(250));//模拟耗时操作
  20. } catch (InterruptedException e) {
  21. // Acceptable way to exit
  22. }
  23. print(this);
  24. }
  25. public String toString() {
  26. return String.format("[%1$-3d]", priority) +
  27. " Task " + id;
  28. }
  29. public String summary() {
  30. return "(" + id + ":" + priority + ")";
  31. }
  32. public static class EndSentinel extends PrioritizedTask {
  33. private ExecutorService exec;
  34. public EndSentinel(ExecutorService e) {
  35. super(-1); // Lowest priority in this program
  36. exec = e;
  37. }
  38. public void run() {
  39. int count = 0;
  40. for (PrioritizedTask pt : sequence) {//遍历队列打印优先级
  41. printnb(pt.summary());
  42. if (++count % 5 == 0)
  43. print();
  44. }
  45. print();
  46. print(this + " Calling shutdownNow()");
  47. exec.shutdownNow();
  48. }
  49. }
  50. }
  51. class PrioritizedTaskProducer implements Runnable {
  52. private Random rand = new Random(47);
  53. private Queue<Runnable> queue;
  54. private ExecutorService exec;
  55. public PrioritizedTaskProducer(
  56. Queue<Runnable> q, ExecutorService e) {
  57. queue = q;
  58. exec = e;
  59. }
  60. public void run() {
  61. for (int i = 0; i < 20; i++) {
  62. queue.add(new PrioritizedTask(rand.nextInt(10)));//先加20个
  63. Thread.yield();
  64. }
  65. try {
  66. for (int i = 0; i < 10; i++) {
  67. TimeUnit.MILLISECONDS.sleep(250);
  68. queue.add(new PrioritizedTask(10)); //在加10个
  69. }
  70. for (int i = 0; i < 10; i++)//在加10个
  71. queue.add(new PrioritizedTask(i));
  72. queue.add(new PrioritizedTask.EndSentinel(exec));
  73. } catch (InterruptedException e) {
  74. }
  75. print("Finished PrioritizedTaskProducer");
  76. }
  77. }
  78. class PrioritizedTaskConsumer implements Runnable {
  79. private PriorityBlockingQueue<Runnable> q;
  80. public PrioritizedTaskConsumer(
  81. PriorityBlockingQueue<Runnable> q) {
  82. this.q = q;
  83. }
  84. public void run() {
  85. try {
  86. while (!Thread.interrupted())
  87. q.take().run();
  88. } catch (InterruptedException e) {
  89. }
  90. print("Finished PrioritizedTaskConsumer");
  91. }
  92. }
  93. public class PriorityBlockingQueueDemo {
  94. public static void main(String[] args) throws Exception {
  95. Random rand = new Random(47);
  96. ExecutorService exec = Executors.newCachedThreadPool();
  97. PriorityBlockingQueue<Runnable> queue =
  98. new PriorityBlockingQueue<Runnable>();
  99. exec.execute(new PrioritizedTaskProducer(queue, exec));
  100. exec.execute(new PrioritizedTaskConsumer(queue));
  101. }
  102. }

该队列的阻塞特性提供了所有的必需的同步,这里不需要任何显式的同步,如果这个队列中没有元素的话会直接阻塞消费者

Semaphore

正常的锁在任何时刻都只允许一个任务访问一项资源,但是计数信号量允许n个任务同时访问这个资源

  1. class A {
  2. private static int count = 0;
  3. private final int id = count++;
  4. @Override
  5. public String toString() {
  6. return "A{" + "id=" + id + '}';
  7. }
  8. }
  9. public class Pool<T> {
  10. private final boolean[] checkOut;
  11. private final Semaphore semaphore;
  12. private final List<T> items = new ArrayList<>();
  13. private int size;
  14. public Pool(Class<T> t, int size) {
  15. this.size = size;
  16. checkOut = new boolean[size];
  17. semaphore = new Semaphore(size, true);
  18. for (int i = 0; i < size; i++) {
  19. try {
  20. items.add(t.newInstance());
  21. } catch (InstantiationException | IllegalAccessException e) {
  22. e.printStackTrace();
  23. }
  24. }
  25. System.out.println(items);
  26. }
  27. private T getItem() {
  28. for (int i = 0; i < size; i++) {
  29. if (!checkOut[i]) {//将获取的元素更改为true
  30. checkOut[i] = true;
  31. return items.get(i);
  32. }
  33. }
  34. return null;
  35. }
  36. private boolean releaseItem(T t) {
  37. int i = items.indexOf(t);
  38. if (i == -1) {
  39. return false;
  40. }
  41. if (checkOut[i]) { //将回来的更改为false
  42. checkOut[i] = false;
  43. return true;
  44. }
  45. return false;
  46. }
  47. public T checkOut() throws InterruptedException {
  48. semaphore.acquire();//获取凭证
  49. return getItem();//获取元素
  50. }
  51. public T checkIn(T t) {
  52. if (releaseItem(t)) {//如果有这个元素
  53. semaphore.release();//回收凭证
  54. }
  55. return null;
  56. }
  57. public static void main(String[] args) throws InterruptedException {
  58. final Pool<A> aPool = new Pool<>(A.class, 5);
  59. //TimeUnit.SECONDS.sleep(5);
  60. for (int i = 0; i <5; i++) {
  61. new Thread(()->{
  62. A a;
  63. try {
  64. a = aPool.checkOut();
  65. System.out.println(a);
  66. } catch (InterruptedException e) {
  67. e.printStackTrace();
  68. }
  69. }).start();
  70. }
  71. //A item = aPool.checkOut();
  72. //System.out.println(item);
  73. }
  74. }

Exchanger

两个任务之间交换对象的栅栏。典型应用场景:一个任务在创建对象,这些对象的生产代价很高昂,另一个任务在消费这些对象,通过这种方式,可以有更多对象在创建的同时被消费
并且没有消费者的时候会阻塞

  1. class Fat {
  2. private volatile double d;
  3. private static int count;
  4. private final int id = count++;
  5. public Fat() {
  6. for (int i = 0; i < 10000; i++) {
  7. d += (Math.PI + Math.E);
  8. }
  9. }
  10. public void operation() {
  11. System.out.println(this);
  12. }
  13. @Override
  14. public String toString() {
  15. return "Fat id : " + id;
  16. }
  17. }
  18. class ExchangerProducer<T> implements Runnable {
  19. private Generator<T> generator;
  20. private Exchanger<List<T>> exchanger;
  21. private List<T> holder;
  22. ExchangerProducer(Exchanger<List<T>> exchanger, Generator<T> gen, List<T> holder) {
  23. this.exchanger = exchanger;
  24. generator = gen;
  25. this.holder = holder;
  26. }
  27. @Override
  28. public void run() {
  29. try {
  30. while (!Thread.interrupted()) {
  31. for (int i = 0; i < ExchangerDemo.size; i++) {
  32. holder.add(generator.next());
  33. }
  34. holder = exchanger.exchange(holder);//把自己集合中的元素交给别人
  35. }
  36. } catch (InterruptedException e) {
  37. e.printStackTrace();
  38. }
  39. }
  40. }
  41. class ExchangeConsumer<T> implements Runnable {
  42. private Exchanger<List<T>> exchanger;
  43. private List<T> holder;
  44. private volatile T value;
  45. ExchangeConsumer(Exchanger<List<T>> exchanger, List<T> holder) {
  46. this.exchanger = exchanger;
  47. this.holder = holder;
  48. }
  49. @Override
  50. public void run() {
  51. try {
  52. while (!Thread.interrupted()) {
  53. holder = exchanger.exchange(holder);//把别人给自己的元素加到自己集合里面
  54. for (T x : holder) {
  55. value = x;
  56. holder.remove(x);//移除完再去交换
  57. }
  58. }
  59. } catch (InterruptedException e) {
  60. e.printStackTrace();
  61. }
  62. System.out.println("Final value : " + value);
  63. }
  64. }
  65. public class ExchangerDemo {
  66. static int size = 0;
  67. static int delay = 5;
  68. public static void main(String[] args) throws InterruptedException {
  69. final ExecutorService exec = Executors.newCachedThreadPool();
  70. Exchanger<List<Fat>> xc = new Exchanger<>();
  71. List<Fat> produceList = new CopyOnWriteArrayList<>();
  72. List<Fat> consumerList = new CopyOnWriteArrayList<>();
  73. exec.execute(new ExchangerProducer<>(xc, BasicGenerator.creat(Fat.class), produceList));
  74. exec.execute(new ExchangeConsumer<>(xc, consumerList));
  75. TimeUnit.SECONDS.sleep(delay);
  76. exec.shutdown();
  77. }
  78. }

CopyOnWriteArrayList,这个特定的List允许在列表遍历时调用remove方法。

性能调优

比较各类互斥技术

  1. abstract class Incrementable {
  2. protected long counter = 0;
  3. public abstract void increment();
  4. }
  5. class SynchronizingTest extends Incrementable {
  6. public synchronized void increment() {
  7. ++counter;
  8. }
  9. }
  10. class LockingTest extends Incrementable {
  11. private Lock lock = new ReentrantLock();
  12. public void increment() {
  13. lock.lock();
  14. try {
  15. ++counter;
  16. } finally {
  17. lock.unlock();
  18. }
  19. }
  20. }
  21. public class SimpleMicroBenchmark {
  22. static long test(Incrementable incr) {
  23. long start = System.nanoTime();
  24. for (long i = 0; i < 10000000L; i++)
  25. incr.increment();
  26. return System.nanoTime() - start;
  27. }
  28. public static void main(String[] args) {
  29. long synchTime = test(new SynchronizingTest());
  30. long lockTime = test(new LockingTest());
  31. System.out.printf("synchronized: %1$10d\n", synchTime);
  32. System.out.printf("Lock: %1$10d\n", lockTime);
  33. System.out.printf("Lock/synchronized = %1$.3f",
  34. (double) lockTime / (double) synchTime);
  35. }
  36. }

输出结果:synchronized: 201171900
Lock: 156996600
Lock/synchronized : 0.780
复杂一点:

  1. abstract class Accumulator {
  2. public static long cycles = 50000L;
  3. // Number of Modifiers and Readers during each test:
  4. private static final int N = 4;
  5. public static ExecutorService exec =
  6. Executors.newFixedThreadPool(N * 2);
  7. private static CyclicBarrier barrier =
  8. new CyclicBarrier(N * 2 + 1);
  9. protected volatile int index = 0;
  10. protected volatile long value = 0;
  11. protected long duration = 0;
  12. protected String id = "error";
  13. protected final static int SIZE = 10000000;
  14. protected static int[] preLoaded = new int[SIZE];
  15. static {
  16. // Load the array of random numbers:
  17. Random rand = new Random(47);
  18. for (int i = 0; i < SIZE; i++)
  19. preLoaded[i] = rand.nextInt();
  20. }
  21. public abstract void accumulate();
  22. public abstract long read();
  23. private class Modifier implements Runnable {
  24. public void run() {
  25. for (long i = 0; i < cycles; i++)
  26. accumulate();
  27. try {
  28. barrier.await();
  29. } catch (Exception e) {
  30. throw new RuntimeException(e);
  31. }
  32. }
  33. }
  34. private class Reader implements Runnable {
  35. private volatile long value;
  36. public void run() {
  37. for (long i = 0; i < cycles; i++)
  38. value = read();
  39. try {
  40. barrier.await();
  41. } catch (Exception e) {
  42. throw new RuntimeException(e);
  43. }
  44. }
  45. }
  46. public void timedTest() {
  47. long start = System.nanoTime();
  48. for (int i = 0; i < N; i++) {
  49. exec.execute(new Modifier());
  50. exec.execute(new Reader());
  51. }
  52. try {
  53. barrier.await();
  54. } catch (Exception e) {
  55. throw new RuntimeException(e);
  56. }
  57. duration = System.nanoTime() - start;
  58. printf("%-13s: %13d\n", id, duration);
  59. }
  60. public static void
  61. report(Accumulator acc1, Accumulator acc2) {
  62. printf("%-22s: %.2f\n", acc1.id + "/" + acc2.id,
  63. (double) acc1.duration / (double) acc2.duration);
  64. }
  65. }
  66. class BaseLine extends Accumulator {
  67. {
  68. id = "BaseLine";
  69. }
  70. public void accumulate() {
  71. value += preLoaded[index++];
  72. if (index >= SIZE) index = 0;
  73. }
  74. public long read() {
  75. return value;
  76. }
  77. }
  78. class SynchronizedTest extends Accumulator {
  79. {
  80. id = "synchronized";
  81. }
  82. public synchronized void accumulate() {
  83. value += preLoaded[index++];
  84. if (index >= SIZE) index = 0;
  85. }
  86. public synchronized long read() {
  87. return value;
  88. }
  89. }
  90. class LockTest extends Accumulator {
  91. {
  92. id = "Lock";
  93. }
  94. private Lock lock = new ReentrantLock();
  95. public void accumulate() {
  96. lock.lock();
  97. try {
  98. value += preLoaded[index++];
  99. if (index >= SIZE) index = 0;
  100. } finally {
  101. lock.unlock();
  102. }
  103. }
  104. public long read() {
  105. lock.lock();
  106. try {
  107. return value;
  108. } finally {
  109. lock.unlock();
  110. }
  111. }
  112. }
  113. class AtomicTest extends Accumulator {
  114. {
  115. id = "Atomic";
  116. }
  117. private AtomicInteger index = new AtomicInteger(0);
  118. private AtomicLong value = new AtomicLong(0);
  119. public void accumulate() {
  120. // Oops! Relying on more than one Atomic at
  121. // a time doesn't work. But it still gives us
  122. // a performance indicator:
  123. int i = index.getAndIncrement();
  124. value.getAndAdd(preLoaded[i]);
  125. if (++i >= SIZE)
  126. index.set(0);
  127. }
  128. public long read() {
  129. return value.get();
  130. }
  131. }
  132. public class SynchronizationComparisons {
  133. static BaseLine baseLine = new BaseLine();
  134. static SynchronizedTest synch = new SynchronizedTest();
  135. static LockTest lock = new LockTest();
  136. static AtomicTest atomic = new AtomicTest();
  137. static void test() {
  138. print("============================");
  139. printf("%-12s : %13d\n", "Cycles", Accumulator.cycles);
  140. baseLine.timedTest();
  141. synch.timedTest();
  142. lock.timedTest();
  143. atomic.timedTest();
  144. Accumulator.report(synch, baseLine);
  145. Accumulator.report(lock, baseLine);
  146. Accumulator.report(atomic, baseLine);
  147. Accumulator.report(synch, lock);
  148. Accumulator.report(synch, atomic);
  149. Accumulator.report(lock, atomic);
  150. }
  151. public static void main(String[] args) {
  152. int iterations = 5; // Default
  153. if (args.length > 0) // Optionally change iterations
  154. iterations = new Integer(args[0]);
  155. // The first time fills the thread pool:
  156. print("Warmup");
  157. baseLine.timedTest();
  158. // Now the initial test doesn't include the cost
  159. // of starting the threads for the first time.
  160. // Produce multiple data points:
  161. for (int i = 0; i < iterations; i++) {
  162. test();
  163. Accumulator.cycles *= 2;
  164. }
  165. Accumulator.exec.shutdown();
  166. }
  167. }

以synchronized关键字入手,只有在性能调优时才替换为Lock对象这种做法,是有实际意义的
虚拟机锁优化

免锁容器

免锁容器背后的通用策略:对容器的修改可以与读取操作同时发生,只需要能看到完成修改之后的结果就可以,修改是在容器数据结构的某个部分的一个单独副本上执行的,并且这个副本在修改过程中是不可视的,修改完成后,被修改的结构才会主动的和主数据进行交换,之后就可以看到修改后的结果

乐观锁

如果主要是从免锁容器中读取,那么他就会比synchronized快许多,因为获取锁和释放锁的操作被取消了

  1. public abstract class Tester<C> {
  2. static int testReps = 10;
  3. static int testCycles = 1000;
  4. static int containerSize = 1000;
  5. abstract C containerInitializer();
  6. abstract void startReadersAndWriters();
  7. C testContainer;
  8. String testId;
  9. int nReaders;
  10. int nWriters;
  11. volatile long readResult = 0;
  12. volatile long readTime = 0;
  13. volatile long writeTime = 0;
  14. CountDownLatch endLatch;
  15. static ExecutorService exec =
  16. Executors.newCachedThreadPool();
  17. Integer[] writeData;
  18. Tester(String testId, int nReaders, int nWriters) {
  19. this.testId = testId + " " +
  20. nReaders + "r " + nWriters + "w";
  21. this.nReaders = nReaders;
  22. this.nWriters = nWriters;
  23. writeData = Generated.array(Integer.class,
  24. new RandomGenerator.Integer(), containerSize);
  25. // for (int i = 0; i < testReps; i++) {
  26. runTest();
  27. readTime = 0;
  28. writeTime = 0;
  29. // }
  30. }
  31. void runTest() {
  32. endLatch = new CountDownLatch(nReaders + nWriters);
  33. testContainer = containerInitializer();
  34. startReadersAndWriters();
  35. try {
  36. endLatch.await();
  37. } catch (InterruptedException ex) {
  38. System.out.println("endLatch interrupted");
  39. }
  40. System.out.printf("%-27s %14d %14d\n",
  41. testId, readTime, writeTime);
  42. if (readTime != 0 && writeTime != 0)
  43. System.out.printf("%-27s %14d\n",
  44. "readTime + writeTime =", readTime + writeTime);
  45. }
  46. abstract class TestTask implements Runnable {
  47. abstract void test();
  48. abstract void putResults();
  49. long duration;
  50. public void run() {
  51. long startTime = System.nanoTime();
  52. test();
  53. duration = System.nanoTime() - startTime;
  54. synchronized (Tester.this) {
  55. putResults();
  56. }
  57. endLatch.countDown();
  58. }
  59. }
  60. public static void initMain(String[] args) {
  61. if (args.length > 0)
  62. testReps = new Integer(args[0]);
  63. if (args.length > 1)
  64. testCycles = new Integer(args[1]);
  65. if (args.length > 2)
  66. containerSize = new Integer(args[2]);
  67. System.out.printf("%-27s %14s %14s\n",
  68. "Type", "Read time", "Write time");
  69. }
  70. }
  1. abstract class ListTest extends Tester<List<Integer>> {
  2. ListTest(String testId, int nReaders, int nWriters) {
  3. super(testId, nReaders, nWriters);
  4. }
  5. class Reader extends TestTask {
  6. long result = 0;
  7. void test() {
  8. for (long i = 0; i < testCycles; i++)
  9. for (int index = 0; index < containerSize; index++)
  10. result += testContainer.get(index);
  11. }
  12. void putResults() {
  13. readResult += result;
  14. readTime += duration;
  15. }
  16. }
  17. class Writer extends TestTask {
  18. void test() {
  19. for (long i = 0; i < testCycles; i++)
  20. for (int index = 0; index < containerSize; index++)
  21. testContainer.set(index, writeData[index]);
  22. }
  23. void putResults() {
  24. writeTime += duration;
  25. }
  26. }
  27. void startReadersAndWriters() {
  28. for (int i = 0; i < nReaders; i++)
  29. exec.execute(new Reader());
  30. for (int i = 0; i < nWriters; i++)
  31. exec.execute(new Writer());
  32. }
  33. }
  34. class SynchronizedArrayListTest extends ListTest {
  35. List<Integer> containerInitializer() {
  36. return Collections.synchronizedList(
  37. new ArrayList<Integer>(
  38. new CountingIntegerList(containerSize)));
  39. }
  40. SynchronizedArrayListTest(int nReaders, int nWriters) {
  41. super("Synched ArrayList", nReaders, nWriters);
  42. }
  43. }
  44. class CopyOnWriteArrayListTest extends ListTest {
  45. List<Integer> containerInitializer() {
  46. return new CopyOnWriteArrayList<Integer>(
  47. new CountingIntegerList(containerSize));
  48. }
  49. CopyOnWriteArrayListTest(int nReaders, int nWriters) {
  50. super("CopyOnWriteArrayList", nReaders, nWriters);
  51. }
  52. }
  53. public class ListComparisons {
  54. public static void main(String[] args) {
  55. Tester.initMain(args);
  56. new SynchronizedArrayListTest(10, 0);
  57. new SynchronizedArrayListTest(9, 1);
  58. new SynchronizedArrayListTest(5, 5);
  59. new CopyOnWriteArrayListTest(10, 0);
  60. new CopyOnWriteArrayListTest(9, 1);
  61. new CopyOnWriteArrayListTest(5, 5);
  62. Tester.exec.shutdown();
  63. }
  64. }

image.png

比较各种Map实现:

  1. abstract class MapTest
  2. extends Tester<Map<Integer, Integer>> {
  3. MapTest(String testId, int nReaders, int nWriters) {
  4. super(testId, nReaders, nWriters);
  5. }
  6. class Reader extends TestTask {
  7. long result = 0;
  8. void test() {
  9. for (long i = 0; i < testCycles; i++)
  10. for (int index = 0; index < containerSize; index++)
  11. result += testContainer.get(index);
  12. }
  13. void putResults() {
  14. readResult += result;
  15. readTime += duration;
  16. }
  17. }
  18. class Writer extends TestTask {
  19. void test() {
  20. for (long i = 0; i < testCycles; i++)
  21. for (int index = 0; index < containerSize; index++)
  22. testContainer.put(index, writeData[index]);
  23. }
  24. void putResults() {
  25. writeTime += duration;
  26. }
  27. }
  28. void startReadersAndWriters() {
  29. for (int i = 0; i < nReaders; i++)
  30. exec.execute(new Reader());
  31. for (int i = 0; i < nWriters; i++)
  32. exec.execute(new Writer());
  33. }
  34. }
  35. class SynchronizedHashMapTest extends MapTest {
  36. Map<Integer, Integer> containerInitializer() {
  37. return Collections.synchronizedMap(
  38. new HashMap<Integer, Integer>(
  39. MapData.map(
  40. new CountingGenerator.Integer(),
  41. new CountingGenerator.Integer(),
  42. containerSize)));
  43. }
  44. SynchronizedHashMapTest(int nReaders, int nWriters) {
  45. super("Synched HashMap", nReaders, nWriters);
  46. }
  47. }
  48. class ConcurrentHashMapTest extends MapTest {
  49. Map<Integer, Integer> containerInitializer() {
  50. return new ConcurrentHashMap<Integer, Integer>(
  51. MapData.map(
  52. new CountingGenerator.Integer(),
  53. new CountingGenerator.Integer(), containerSize));
  54. }
  55. ConcurrentHashMapTest(int nReaders, int nWriters) {
  56. super("ConcurrentHashMap", nReaders, nWriters);
  57. }
  58. }
  59. public class MapComparisons {
  60. public static void main(String[] args) {
  61. Tester.initMain(args);
  62. new SynchronizedHashMapTest(10, 0);
  63. new SynchronizedHashMapTest(9, 1);
  64. new SynchronizedHashMapTest(5, 5);
  65. new ConcurrentHashMapTest(10, 0);
  66. new ConcurrentHashMapTest(9, 1);
  67. new ConcurrentHashMapTest(5, 5);
  68. Tester.exec.shutdown();
  69. }
  70. }

image.png

乐观锁

  1. public class FastSimulation2 {
  2. static AtomicInteger ato = new AtomicInteger(10);
  3. public static void main(String[] args) throws InterruptedException {
  4. new Thread(() -> {
  5. int oldValue = ato.get();
  6. try {
  7. TimeUnit.MILLISECONDS.sleep(100);
  8. } catch (InterruptedException e) {
  9. e.printStackTrace();
  10. }
  11. System.out.println("原来获取的oldValue为:" + oldValue + "查看是否被其他的线程修改了" + ato.get());
  12. int newValue = oldValue + 3;
  13. if (!ato.compareAndSet(oldValue, newValue)) {
  14. System.out.println(Thread.currentThread().getName() + "将要去修改的旧值为:" + oldValue);
  15. } else {
  16. System.out.println(Thread.currentThread().getName() + "修改成功了 " + ato.get());
  17. }
  18. }).start();
  19. new Thread(() ->{
  20. int oldValue = ato.get();
  21. int newValue = oldValue + 3;
  22. if (ato.compareAndSet(oldValue, newValue)) {
  23. System.out.println(oldValue);
  24. } else {
  25. System.out.println(Thread.currentThread().getName() + "修改成功了");
  26. }
  27. }).start();
  28. TimeUnit.MILLISECONDS.sleep(100);
  29. }
  30. }

假设有B,C两个线程都要去修改A中的值,都是将其加3,B线程会在修改前查看是否还是原来值,如果还是原来的值就对其进行修改,此时线程C进来看到A中已经不是原来的值了,于是就不对其进行修改

ReadWriteLock

ReadWriteLock对数据结构相对不频繁的写入,可以与多个读取者,只要他们不试图写入即可,如果写的这个锁已经被其他的任务持有,那么任何读取者都不能访问,直到这个写锁被释放