Java基础学习之-多线程学习知识点的学习

Java语言从设计之初就把多线程做为语言的核心, 至少从如下几点能够看出:html

1. Object对象的wait和notify机制。
2. Thread类在lang包中。
3. synchronized volatile关键字。

虽然多线程是Java语言自己的特性,可是线程并非Java语言独有的东西,而是操做系统的特性。Java在语言层面进行了封装,使其使用更简单。java

多线程存在的价值在哪里呢? 内存读写,磁盘IO, 网络传输的速率远远低于CPU处理数据的速度。因此在大部分场景下,CPU是闲置的。有了多线程,就能够更多地压榨CPU。因此,多线程在Web服务器,Lucene这类IO密集型的应用中大行其道。缓存

多线程,知识点庞杂,但经常使用的核心知识点只有两个: 资源同步 和 线程池。安全

学习多线程,私觉得这样的前后顺序比较好。服务器

step1: 学习多线程的建立和运行, Thread类和Runnable接口, 一般自定义类是实现Runnable接口,并不是Thread类。网络

public class ThreadDemo {

    static class Worker impelements Runnable{

        private String name;

        public Worker(String name){
            this.name = name;
        }

        public void run(){
            System.out.println(this.name+": "+Thread.currentThread().getName());
        }
    }

    public static void main(String[] args) {
        Thread a1 = new Thread(new Worker("nameA"));
        Thread a2 = new Thread(new Worker("nameB"));
        a1.start();
        a2.start();
    }
}

step2: 学习synchronized/volatile关键字数据结构

因为内存读写远比CPU执行的速度慢,出于提高性能的考虑,计算机设计者在CPU和内存之间架设了缓存,就是一般咱们看到的L1, L2, L3。1个CPU周期须要0.3ns,L1缓存只须要0.9ns, 内存访问须要120ns。 差距达百倍, 可见缓存对性能的提高。若是咱们在一个线程里读写一个变量,将初始值加到缓存后, CPU只需和缓存之间交互就能够了。多线程

因为线程之间并不共享缓存,因此多个线程读写同一个变量时,就有可能出现不一致的状况。多个线程出现了信息的不对称, 如何解决这个问题呢? 就像银行办理业务同样,每一个人办理业务前先取个号。这个号就相似Java里面的锁机制。 synchronized就是Java的内置锁。 volatile能够看做synchronized的简化版, 由于它只能适用于某些特定的场景:ide

1. 修改变量不依赖其当前的值
2. 变量不跟其余变量一块儿对外做为总体

关于volatile的学习,我的以为IBM的Java theory and practice Managing volatility是极好的学习资料。工具

step3: 学习ReentrantLock。了解了锁的使用场景后,能够先学习JUC实现的锁,最简单的就是ReentrantLock。ReentrantLock是互斥锁,可做为synchronized的代替品。

import java.util.concurrent.locks.ReentrantLock;

public class ThreadDemo {

    static class Counter implements Runnable{

        private int count;

        private ReentrantLock lock = new ReentrantLock();

        public void run(){
            try{
                lock.lock();
                count +=1;
            }finally {
                lock.unlock();
            }
        }

        public int getCount() {
            return count;
        }
    }

    public static void main(String[] args) throws InterruptedException {
        Counter a1 = new Counter();
        for(int i=0;i<1000;i++){
            new Thread(a1).start();
        }
        Thread.sleep(1000);
        System.out.println(a1.getCount());
    }
}

使用了ReentrantLock后,最好是能理解锁的实现原理,即大名顶顶的AQS。可是学习AQS以前,须要一些预备知识。

1. volatile关键字的使用场景及限制
2. Unsafe.compareAndSwap的使用
3. Unsafe.park/unpark的使用
4. 数据结构链表的原理

整个JUC的锁,线程安全的队列都是基于AQS构建的。

step4: 学习AQS的原理。AQS基于链表构建的队列来存储争用锁的线程。当线程没有获取到锁,就会使用Unsafe.park将线程挂起;其余的线程释放锁时,就会调用Unsafe.unpark将挂起的线程恢复。

step5: 了解了AQS的原理后,再学习ReentrantReadWriteLock, Semaphore, Phase, CountDownLatch, CyclicBarrier 这些同步工具,就没那么难理解了。

step6: 学习BlockingQueue及其子类。 各类阻塞队列,使用频度比较高的是ArrayBlockingQueue, LinkedBlockingQueue.

step7: 学习线程池,主要是ThreadPoolExecutor类, ThreadPoolExecutor的核心是Worker类,每一个Worker对应着一个线程。须要注意的是使用ThreadPoolExecutor时,阻塞队列必定要要指定大小。否则线程池的RejectPolicy就不起做用了。 具体的细节须要更深刻的分析。

关于多线程的知识点, 一篇笔记是远远不够的,本文仅仅梳理学习的脉络。

参考文档:

https://www.ibm.com/developerworks/library/j-jtp06197/index.html

相关文章
相关标签/搜索