Java多线程系列

1、参考资料(多线程系列)

1、:Java多线程系列目录

1.1、基础篇

01. Java多线程系列–“基础篇”01之 基本概念

02. Java多线程系列–“基础篇”02之 常用的实现多线程的两种方式

03. Java多线程系列–“基础篇”03之 Thread中start()和run()的区别

04. Java多线程系列–“基础篇”04之 synchronized关键字

05. Java多线程系列–“基础篇”05之 线程等待与唤醒

06. Java多线程系列–“基础篇”06之 线程让步

07. Java多线程系列–“基础篇”07之 线程休眠 

08. Java多线程系列–“基础篇”08之 join()

09. Java多线程系列–“基础篇”09之 interrupt()和线程终止方式

10. Java多线程系列–“基础篇”10之 线程优先级和守护线程

11. Java多线程系列–“基础篇”11之 生产消费者问题

1.2、JUC原子类 

12. Java多线程系列–“JUC原子类”01之 框架 

13. Java多线程系列–“JUC原子类”02之 AtomicLong原子类

14. Java多线程系列–“JUC原子类”03之 AtomicLongArray原子类

15. Java多线程系列–“JUC原子类”04之 AtomicReference原子类

16. Java多线程系列–“JUC原子类”05之 AtomicLongFieldUpdater原子类

 1.3、JUC锁

17. Java多线程系列–“JUC锁”01之 框架

18. Java多线程系列–“JUC锁”02之 互斥锁ReentrantLock

19. Java多线程系列–“JUC锁”03之 公平锁(一) 

20. Java多线程系列–“JUC锁”04之 公平锁(二) 

21. Java多线程系列–“JUC锁”05之 非公平锁 

22. Java多线程系列–“JUC锁”06之 Condition条件

23. Java多线程系列–“JUC锁”07之 LockSupport

24. Java多线程系列–“JUC锁”08之 共享锁和ReentrantReadWriteLock 

25. Java多线程系列–“JUC锁”09之 CountDownLatch原理和示例

26. Java多线程系列–“JUC锁”10之 CyclicBarrier原理和示例

27. Java多线程系列–“JUC锁”11之 Semaphore信号量的原理和示例 

1.4、JUC集合

28. Java多线程系列–“JUC集合”01之 框架

29. Java多线程系列–“JUC集合”02之 CopyOnWriteArrayList

30. Java多线程系列–“JUC集合”03之 CopyOnWriteArraySet

31. Java多线程系列–“JUC集合”04之 ConcurrentHashMap

32. Java多线程系列–“JUC集合”05之 ConcurrentSkipListMap

33. Java多线程系列–“JUC集合”06之 ConcurrentSkipListSet

34. Java多线程系列–“JUC集合”07之 ArrayBlockingQueue

35. Java多线程系列–“JUC集合”08之 LinkedBlockingQueue 

36. Java多线程系列–“JUC集合”09之 LinkedBlockingDeque

37. Java多线程系列–“JUC集合”10之 ConcurrentLinkedQueue

1.5、JUC线程池

38. Java多线程系列–“JUC线程池”01之 线程池架构

39. Java多线程系列–“JUC线程池”02之 线程池原理(一)

40. Java多线程系列–“JUC线程池”03之 线程池原理(二)

41. Java多线程系列–“JUC线程池”04之 线程池原理(三)

42. Java多线程系列–“JUC线程池”05之 线程池原理(四)

43. Java多线程系列–“JUC线程池”06之 Callable和Future

2、简要总结

  多线程是Java中不可避免的一个重要主体。从本章开始,我们将展开对多线程的学习。接下来的内容,是对“JDK中新增JUC包”之前的Java多线程内容的讲解,涉及到的内容包括,Object类中的wait(), notify()等接口;Thread类中的接口;synchronized关键字。

注:JUC包是指,Java.util.concurrent包,它是由Java大师Doug Lea完成并在JDK1.5版本添加到Java中的。

2.1、线程状态

《Java多线程系列》

说明
线程共包括以下5种状态。
1. 新建状态(New)         : 线程对象被创建但还没有调用start方法时,处于新建状态。例如,Thread thread = new Thread()。
2. 就绪状态(Runnable): 也被称为“可执行状态”。线程对象被创建后,其它线程调用了该对象的start()方法,从而来启动该线程。例如,thread.start()。处于就绪状态的线程,随时可能被CPU调度执行。
3. 运行状态(Running) : 线程获取CPU权限进行执行。需要注意的是,线程只能从就绪状态进入到运行状态。
4. 阻塞状态(Blocked)  : 阻塞状态是线程因为某种原因放弃CPU使用权,暂时停止运行。直到线程进入就绪状态,才有机会转到运行状态。阻塞的情况分三种:
    (01) 等待阻塞 — 通过调用线程的wait()方法,让线程等待某工作的完成。
    (02) 同步阻塞 — 线程在获取synchronized同步锁失败(因为锁被其它线程所占用),它会进入同步阻塞状态。
    (03) 其他阻塞 — 通过调用线程的sleep()或join()或发出了I/O请求时,线程会进入到阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。
5. 死亡状态(Dead)    : 线程执行完了或者因异常退出了run()方法,该线程结束生命周期。

线程的5种状态涉及到的内容包括Object类, Thread和synchronized关键字。这些内容我们会在后面的章节中逐个进行学习。
Object类,定义了wait(), notify(), notifyAll()等休眠/唤醒函数。
Thread类,定义了一些列的线程操作函数。例如,sleep()休眠函数, interrupt()中断函数, getName()获取线程名称等。

Java线程状态:

注:其实Java在Thread类中定义了线程的状态(enum State),共有六种:NEW、RUNNABLE、TERMINATED、BLOCKED、WAITING、TIMED_WAITING。

《Java多线程系列》

 

  • RUNNABLE包括上述的Runnable和Running。
  • WAITING表示无线等待,需要被唤醒,如执行wait()、join()方法
  • TIMED_WAITING表示有限等待,在一定时间后自动唤醒,如执行wait(long timems)、join(long timems)、sleep(long timems)方法
  • BLOCKED表示阻塞,线程等待进入同步区时进入此状态

 

2.2、线程状态转换方法

详见:Java线程状态及 join、sleep、wait、notify、yield等的区别

2.3、同步互斥

同步:在多个线程并发访问共享数据时,保证数据在同一时刻只被一个(或一些,使用信号量的时候)线程使用。可以分为阻塞同步(如互斥)和非阻塞同步(需要硬件指令集的发展以保障“操作和冲突检测”的原子性,CAS操作等)。

互斥:实现同步的一种手段,属于阻塞同步。临界区(Critical Section)、互斥量(Mutex)、信号量(Semaphore)是主要的互斥实现方式

互斥是因,同步是果;互斥是方法,同步是目的。

2.4、线程协作

待…

2.5、线程竞争

多线程共享内存(竞争访问同一资源)带来的问题:(参考资料:http://mp.weixin.qq.com/s/DC6JlRo7LEx4rRsz9U_Xpw

1、竞态条件(race condition):当多个线程访问和操作同一个对象时,最终执行结果与执行时序有关,可能正确也可能不正确

解决:

  • 使用synchronized关键字(相关:synchronized关键字
  • 使用显式锁
  • 使用原子变量(计数、序号等场景)或原子更新(被更新的变量需被volatile修饰) 《Java多线程系列》 《Java多线程系列》
     1 // 2、原子变量
     2 // 对于count++这种操作来说,使用synchronzied成本太高了,需要先获取锁,最后还要释放锁,获取不到锁的情况下还要等待,还会有线程的上下文切换,这些都需要成本。
     3 //
     4 // 对于这种情况,完全可以使用原子变量代替,Java并发包中的基本原子变量类型有:
     5 // AtomicBoolean:原子Boolean类型
     6 // AtomicInteger:原子Integer类型
     7 // AtomicLong:原子Long类型
     8 // AtomicReference:原子引用类型
     9 class AtomicIntegerDemo {
    10     private static AtomicInteger counter = new AtomicInteger(0);
    11 
    12     static class Visitor extends Thread {
    13         @Override
    14         public void run() {
    15             for (int i = 0; i < 100; i++) {
    16                 counter.incrementAndGet();
    17                 Thread.yield();
    18             }
    19         }
    20     }
    21 
    22     public static void main(String[] args) throws InterruptedException {
    23         int num = 100;
    24         Thread[] threads = new Thread[num];
    25         for (int i = 0; i < num; i++) {
    26             threads[i] = new Visitor();
    27             threads[i].start();
    28         }
    29         for (int i = 0; i < num; i++) {
    30             threads[i].join();
    31         }
    32         System.out.println(counter.get());
    33     }
    34 }
    35 
    36 // 原子更新
    37 class FieldUpdaterDemo {
    38     static class DemoObject {// 类DemoObject中有两个成员num和ref,声明为volatile,但不是原子变量,不过DemoObject对外提供了原子更新方法compareAndSet,它是使用字段对应的FieldUpdater实现的,FieldUpdater是一个静态成员,通过newUpdater工厂方法得到,newUpdater需要的参数有类型、字段名、对于引用类型,还需要引用的具体类型。
    39         private volatile int num;
    40         private volatile Object ref;
    41 
    42         private static final AtomicIntegerFieldUpdater<DemoObject> numUpdater = AtomicIntegerFieldUpdater
    43                 .newUpdater(DemoObject.class, "num");
    44         private static final AtomicReferenceFieldUpdater<DemoObject, Object> refUpdater = AtomicReferenceFieldUpdater
    45                 .newUpdater(DemoObject.class, Object.class, "ref");
    46 
    47         public boolean compareAndSetNum(int expect, int update) {
    48             return numUpdater.compareAndSet(this, expect, update);
    49         }
    50 
    51         public int getNum() {
    52             return num;
    53         }
    54 
    55         public Object compareAndSetRef(Object expect, Object update) {
    56             return refUpdater.compareAndSet(this, expect, update);
    57         }
    58 
    59         public Object getRef() {
    60             return ref;
    61         }
    62     }
    63 
    64     public static void main(String[] args) {
    65         DemoObject obj = new DemoObject();
    66         obj.compareAndSetNum(0, 100);
    67         obj.compareAndSetRef(null, new String("hello"));
    68         System.out.println(obj.getNum());
    69         System.out.println(obj.getRef());
    70     }
    71 }

    View Code

2、内存可见性:多个线程可以共享访问和操作相同的变量,但一个线程对一个共享变量的修改,另一个线程不一定马上就能看到,甚至永远也看不到

解决:

  • 使用volatile关键字(相关:volatile关键字
  • 使用synchronized关键字同步
  • 使用显式锁同步

2.6、 对象的线程安全

1、线程安全:(Brian Goetz《Java Concurrency In Practice》)当多个线程访问一个对象时,如果不用考虑这些线程在运行时环境下的调度和交替执行,也不需要进行额外的同步或在调用方进行任何其他的协调操作,调用这个对象的行为都可以获得正确的结果,那此对象是线程安全的。

此定义要求线程安全的代码都必须具备一个特征:代码本身封装了所有必要的正确性保障手段(如互斥同步等),令调用者无须关心多线程的问题,更无须采取任何措施保证多线程的正确调用。此并不易做到,在大多数场景中,会将此定义弱化些,若把“调用这个对象的行为”限定为“单次调用”,此定义的其他描述也能成立,则称之是线程安全的了。

2、保证线程安全的思路

  1. 锁,使用synchronized或ReentrantLock(悲观策略),参看理解synchronized
  2. 原子操作,循环CAS(乐观策略),参看原子变量和CAS
  3. 写时拷贝,如CopyOnWriteArrayList,适用于元素个数不多,绝大部分访问都是读,且有大量并发线程要求读,只有个别线程进行写,且只是偶尔写的场合。(参看写时拷贝的List和Set

 

    原文作者:March On
    原文地址: http://www.cnblogs.com/z-sm/p/5281315.html
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞