java多线程高并发入门 weir 2018-03-05 13:30:42.0 java 1556 过年在家学习java多线程并发问题,说实话工作7 8年了没写过这些底层的东西,前两年才听说netty,去年学习了一下NIO,也想读读netty的源码结果卡在了netty的内存分配这里理解不了了。复杂程度不是我一个人去钻研能理解透的所以也算是搁置了,最近在看多线程并发,面对java1.5之后的改进也开始吃力了,发现好多东西如果不理解他的原理,想研究下去基本不可能。我之前还想研究LSM-tree、还有Paxos和Raft都是没下文,因为理解不了再加上网上资料太少英文水平又不好感觉没前途的样子,我现在真想找个大神带带我,我现在所了解的什么高性能呀大数据呀高可用呀基本都算了解,属于刚明白所谓的大神是怎么回事了,其实不是代码写的溜溜的,而是从底层就知道计算机是怎么一回事,还有就是算法要好,其实算法是什么在我看来是找到了解决某一类问题的最佳途径,如果你不知道什么算法自己钻研多了也能找出一些算法来,在一个领域干的时间长的你的经验就是最好的算法。只不过在计算机领域大家总结出来了,就像设计模式一样,但是总结的人不是你这样你就不好掌握,我们所谓的计算机革命其实还是人的经验的革命,我扯这么多就是想说搞计算机的如果能搞懂计算机的底层技术,你基本就可以算是大神级别了,反正我是发现虽然我是学计算机的可是基础非常差,学习NIO、并发就发现好多知识需要基础的认知。 大部分文章将多线程都会先拿进程来说事儿,进程又是什么玩意儿呢?恐怕讲的人并不多,进程的出现其实才是个里程碑的时刻,进程其实是用空间换时间怎么理解?打开你的win任务管理器会看到很多进程,你打开很多应用程序为什么相互不受影响,这里面的玄机就是进程之间的内存隔离。再说的通俗一点就是,每一个应用程序都有自己的内存空间,大家有没有疑问内存是什么鬼,其实计算机刚开始是没有内存这个概念的,我想大家应该知道这个历史,内存出现我想只有一个目的就是暂时存在运行程序的当前(实时)状态,大家可以思考一下这个问题,最开始CPU也是很简单的并不是我们现在看到的多核还有高速缓存,而且高速缓存还分好几级,有了这些才使得我们的操作系统、编程语言复杂起来同时也带来了更大的便捷和智能。 其实刚开始的计算机真的简单的可怕而且笨重,你想想嘛就是用电流的正负原理,电的出现才多少年,计算机的出现就像是婴儿出生一步一步长大,并不是一下子就成人。 线程的出现是为了解决什么问题呢?更好地利用CPU资源。CPU的速度是惊人的,内存跟CPU比差的不是一丁点儿,磁盘跟内存比用差的不是一丁点儿,这就使得存放在磁盘上面的程序运行起来变得复杂起来。刚开始计算机需要人工才能计算,到后来我们写出来程序存放在磁盘上面只需要点击一下就可以自动运行,这个过程需要磁盘到内存再到CPU开始运行起来。 在我看来内存是到现在为止最神秘的计算机硬件,然而对现代计算机影响最大作用最大的也是内存了,内存你只能看到用了多少,然后用进程体现出来,内部是什么在发生着什么变化都是描述出来的很不直观。再看看今天的服务器高性能高并发,没有内存在发挥至关重要的作用谁能说高性能,这就是CPU和内存和磁盘之间不可弥补的速度差距,目前全靠内存和CPU的高速缓存来弥补,这就是目前计算机没有质的飞跃的本质。正好线程也是在内存中而且是在一个进程里面,由于CPU速度越来越快,CPU在运行的时候就会出现等待其他资源加载到内存再被CPU运行的情况,所以后来就出现了在一个进程里面产生很多线程而且CPU的调度也开始针对线程而不是进程了,也就是CPU每次计算指令都是和线程打交道了。这样一来就使得线程开始成为主角,此时大家要清楚的是线程是存在于进程里面的,是需要共享进程里面的资源的,也就是给进程分配好的内存资源。现在大家应该知道计算机里面各个部件是怎么一回事了吧,磁盘用来存放程序来解放人工,内存用来存放程序的运行状态和磁盘与CPU的一个速度缓冲区,CPU也出现多核还有高速缓存来缓冲内存和CPU交互的速度不匹配问题。 其实并发真是比较新的东西,没有多核CPU和操作系统底层封装恐怕不会有并发这玩意儿,就像没有异步处理也不会有NIO,多线程并发和NIO的出现才使得Java语言有了第二次生命。相对于NIO多线程并发要比NIO容易理解,但是往往这两个在一起才能发挥出来威力,NIO得益于底层封装的异步接口比如linux的select/poll和epoll、信号驱动I/O、内存拷贝等,而线程则是利用主内存和CPU高速缓存来进行同步和并发处理。大家看JDK源码会发现: private static final Unsafe unsafe = Unsafe.getUnsafe(); private native void write0(int b) throws IOException; Unsafe 和 native都是调用的C接口,通过C调用更加底层的代码直接作用于CPU和内存,Java的高性能离不开这两个东西,甚至netty的内存分配就是直接用Unsafe接口开分配管理内存的而绕过了JVM的垃圾回收机制,这两个东西的封装才使得Java在IO和线程方面可以匹敌C/C++的性能,其实底层还是调用的C程序。还有一个关键字大家也不会陌生: private volatile int value; 这个是干嘛用的,同步CPU的高速缓存里面的变量与主内存里面的变量,这两个变量同步了数据就正确了,CPU高速缓存就是用来存放主内存里面的部分变量而且不会马上返回给主内存这才出现了同一个变量在多线程调用情况下出现不一致问题。 Java毕竟还会高级语言,不像C那样灵活的高效的来管理内存和发出CPU指令,那么怎么做到高效那就是隐藏调用,给大家的感觉就是我是在用java语言而且还是跨平台的但是一步一步到底层还是需要更底层的语言C甚至汇编,大家有没有想过什么时候可以在内存或CPU层面封装一些接口直接调用这些接口就可以达到异步、同步或者并发的处理,我想这点会很困难毕竟他们都是硬件资源在硬件资源里面来完成代码逻辑这点恐怕不是质的飞跃那么简单。 这就是整个java NIO和多线程并发的来空去脉,这些东西理解了你在看java代码就不迷茫了,就知道这个时候为什么回去调用C代码来做事情,为什么这时候要调用volatile来处理变量,为什么java代码里面还有用native修饰的方法哦原来这个方法直接调用的是C代码被封装到jdk或JVM里面了,还会知道Unsafe原来功能这么强大可以直接分配无法让java垃圾自动回收的堆外内存(堆是什么咱们另外说),顺便多说两句,unsafe之前有人说jdk9之后不能用了会隐藏掉,现在看来则不然而且还提供了静态方法可以调用,大家可以去试试。