J.U.C是java系列一块看似简单,水很深的区域,但是不论是深入java还是分布式的一些东西,这都算是基础,虽然以前乱七八糟写过一些多线程的文章,不过都比较乱了一点,最近有打算逐步深入来写多篇文章来说说我对这些东西的小理解。
1、首先线程分为内核线程、用户线程;在Linux下java的线程其实是在java私有栈上有一个用户线程,和OS级别有一个轻量级的进程来实现。
2、在操作java多线程的时候必然会遇到锁的问题,在锁的问题中,线程会首先进入一个所谓的Entry Set的集合中,然后尝试去征用锁,征用到锁的就是他的owner,没有征用到,调用wait(),那么就进入Wait Set的区域中,使用完后,锁被释放,调用notfiy或notifyAll欢迎Wait Set区域的内容重新尝试,和Entry Set里面的信息一起征用;调用sleep是没有锁的;synchronized是系统级别的锁;notify是唤醒一个Wait Set区域的等待线程,而notifyAll是唤醒所有相关的线程,join是当前线程会等待对应的几个线程执行完再向下走;Daemon设置为true设置为后台线程,指:主线程执行完后,后台线程自动完成,否则不会;yield是当前让出一个CPU的时间,给其他线程来做,但是让多久,谁也不知道,要知道让多久,sleep知道。
3、blocked状态的线程一般是挂住的线程,由于锁机制的原因导致,或一些网络原因导致,并且此时interrupt是无法断开的,锁机制的问题在后续的文章中会介绍很多机制来避免,但是网络机制的在java中请用socket的超时soTimeout来设置超时,否则无法断开(包括连接数据库等)soTimeout意义在于发生一次read操作的时候,连接超时时间,也就是等结果的时候一直没等到,而不是总的连接超时;
4、interrupt只会对wait和time_wait状态的线程有效,如果程序是处于循环运行状态,外部发起一个interrupt命令要求停止这个线程,此时程序默认是不会被停止的,所以你在看某些框架的时候,里面会在循环体内部有一个操作是:
Thread.currentThread().isInterrupted()
就是判定当前线程时候被中断,从这里你可以看出中断只是在线程上打了一个tag(true|false),此时你发现被中断就应该break跳出循环;注意还有一个方法是:interrupted();这个方法不仅仅会设置方法是否被中断,而且还会设置线程是未中断的状态,也就是将interrupt的状态重新会设置为false,不过当前会返回false,可以在任意一个main方法中写一下代码测试:
Thread.currentThread().interrupt(); System.out.println(Thread.currentThread().interrupted()); System.out.println(Thread.currentThread().interrupted()); //Thread.currentThread().interrupt(); System.out.println(Thread.currentThread().isInterrupted()); System.out.println(Thread.currentThread().isInterrupted());
然后将注释掉的代码打开再看看结果;源码部分很简单:
public boolean isInterrupted() { return isInterrupted(false); } public static boolean interrupted() { return currentThread().isInterrupted(true); }
上一篇文章说过一些关于CPU Cache Line的一些问题,其实可以看出,如果你想要自己在java的内存中设置缓存的话,建议这些缓存是基本不被修改的,最好是final的;如果是变化的保证可见性的情况下需要选择volatile、保证原子性优先选择Atomic*,不到万不得已,不自己玩锁,尤其是在静态方法或Class上去做锁;关于可见性的描述,后面的文章中还会更加深入的阐述下。
5、java 现在提供了很多java.util.concurrent包来替代原来的很多集合类,但并不代表可以解决所有问题,也不代表原来的集合类没用,更加更加不代表不需要了解各种集合类的特征和使用场景;在后面的文章中我们会介绍这些内容的细节,总之如果你发现你所要访问的内容存在多线程的访问,并且他们是可能被修改的,那么就存在并发,并发就存在诸多的问题需要处理;
6、有人认为,并发时我不确保数据的正确性的情况下或者报个错也可以接受,不使用非并发包(HashMap这类)也觉得可以,NO,在并发的情况下有些时候不仅仅是报错的问题,有些时候可能会引起并发时Key重复死循环情况。
7、ABA问题是我们比较痛苦的问题,即使使用很多并发的机制来解决也未必是真实的,所以对于ABA问题,java为我们提供了版本控制的方法,后续文章也逐步说明。
8、ThreadLocal这把双刃剑,能爽死你,也能玩死你;ThreadLoad在传递参数的时候,是非常有效的,加参数也很方便,他是与当前线程绑定的,不过查问题痛苦了,尤其是这些东西被封装到三方包里面的时候,因为WEB模型的线程是不会被释放的,所以ThreadLocal内部的参数也不会释放,在什么地方被修改一个不知道,其次容易引起内存泄露。
9、JVM也为你提供了各种各样的线程池,他们能为你解决什么,和普通线程的区别,线程池的处理模型和策略;通过线程池如何完成一个调度器的功能;通过线程池完成很多的现实模型而不需要你自己去写算法去解决一些复杂恶心的线程交互问题;在这里还有一个Future、FutureTask是咋回事;这个我们在后面说
10、锁机制的到底是啥玩意,现在的java如何玩锁,怎么玩清楚锁;synchronized、Lock(ReentrantLock、ReadWriteLock)如何选择,等待队列分组;啥时候死锁,除了交叉死锁还有什么死锁?tryLock咋玩的?如何提升一些性能?
11、Queue、List、Map并发容器的介绍和使用;CountDownLatch、Semaphone、CyclicBarrier、Exchanger使用(AbstractQueuedSynchronizer)工具篇;后续专门介绍。
12、Atomic系列之Atomic<基本的变量>、Atomic<基本变量>FiledUpdater、Atomic<基本变量>Array,以及AtomicReference(引用)、AtomicStampedReference(带版本号的引用)、AtomicMarkableReference(可以进行计数);后续专门说明;
本文除了前面的简单介绍外,后续部分就是一个大概介绍,由于篇幅所限,只能逐步完善后面的内容。