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

Java语言从设计之初就把多线程作为语言的核心, 至少从以下几点可以看出:

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

虽然多线程是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的简化版, 因为它只能适用于某些特定的场景:

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

原文地址:https://blog.51cto.com/sbp810050504/2360608

时间: 2024-10-24 15:19:51

Java基础学习之-多线程学习知识点的学习的相关文章

Java基础】并发 - 多线程

Java基础]并发 - 多线程 分类: Java2014-05-03 23:56 275人阅读 评论(0) 收藏 举报 Java 目录(?)[+] 介绍 Java多线程 多线程任务执行 大多数并发应用程序时围绕执行任务(task)进行管理的:所谓任务就是抽象的,离散的工作单元. 围绕执行任务来管理应用程序时,第一步是要指明一个清晰的任务边界.大多数应用服务器程序都选择了下面这个自然的任务辩解:单独的客户请求: 任务时逻辑上的单元: 任务 Runnable 表示一个任务单元(java.lang)

黑马程序员——Java基础知识之多线程协同

多线程协同 线程间的通讯:对资源的操作动作不同,比如说两个卡车一个拉煤一个装煤,但是他们共享了一个资源. 怎么样把这个资源拿出来?怎样把车装满?这个资源当然是一个类,他里面的组成元素就是对象!!现在我们就要有操作对象的思想了,用对象把这车装满,现在一车装一个对象. 等待唤醒机制: 用的不是sleep是wait.flag标记,这是两人沟通的方式.其实每个标记就要做一次等待或者notify,判断wait,改值notify.线程池.notify唤醒里面的线程,按顺序唤醒.wait和notify必须用在

Java基础教程:多线程基础(2)——线程间的通信

Java基础教程:多线程基础(2)--线程间的通信 使线程间进行通信后,系统之间的交互性会更强大,在大大提高CPU利用率的同时还会使程序员对各线程任务在处理的过程中进行有效的把控与监督. 线程间的通信 思维导图 等待中 等待/通知机制 不使用等待/通知机制 我们可以使用使用sleep()与 whle(true) 死循环来实现多个线程间的通信. 虽然两个线程实现了通信,但是线程B必须不断的通过while语句轮训机制来检测某一个条件,这样会浪费CPU资源. 如果轮询间隔较小,更浪费时间间隔.如果轮训

Java基础加强之多线程篇(线程创建与终止、互斥、通信、本地变量)

线程创建与终止 线程创建 Thread类与Runnable接口的关系 public interface Runnable { public abstract void run(); } public class Thread implements Runnable { /* What will be run. */ private Runnable target; ...... /** * Causes this thread to begin execution; the Java Virtu

java基础3:多线程

关于Java基础的文章,我觉得写得还可以,以前发在了我其它的博客了,肯定是原创,现在再分享给大家出来. ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

java基础知识总结--多线程

1.扩展Java.lang.Thread类 1.1.进程和线程的区别: 进程:每个进程都有自己独立的代码和数据空间(进程上下文),进程间的切换会有较大的开销,一个进程包含1~n个线程. 线程:同一类线程共享代码和数据空间,每一个线程有独立的运行栈和程序计数器,线程切换开销比较小. 进程和线程一样都分为五个阶段:创建.就绪.运行.阻塞.终止. 多进程是指操作系统能同时运行多个任务(程序). 多线程是指在同一个程序中有多个顺序流在执行. 1.2.在Java中要想实现多线程,有两种方法:继承Threa

黑马程序员——JAVA基础之Day24 多线程 ,死锁,线程间通信 ,线程组,线程池,定时器。

------- android培训.java培训.期待与您交流! ---------- Lock()实现提供了比使用synchronized方法和语句可获得更广泛的锁定操作. private Lock lock =new ReentrantLock(); 被锁的代码要用   lock.lock()                lock.unlock()    包括.其中用try   ...finally包围 同步:效率低,如果出现同步嵌套,会出现死锁.  但是安全. 死锁问题:两个或者两个以上

Java基础知识之多线程(1)

1.认识线程 a) 对于线程来说,容易混淆的还有进程,我们用一个例子来区分它们,每次打开一个word文档,这就是一个进程,一个word中的拼写检查这是一个线程,再如每登陆一个QQ,这是一个进程,QQ中的每一个会话都是一个线程. b)对于只有一个CPU的电脑来说,一个时间点只有一个进程占用CPU 2.Java中如何实现线程 Java中实现多线程可以采用:继承Thread类,或,实现Runnable接口 a)继承Thread类,这个方式必须覆写run()方法 class 类名称 extends Th

【Java基础总结】多线程

1. java中实现多线程的两种方式 1 //第一种:继承Thread类,重写run()方法 2 class ThreadTest1 extends Thread{ 3 public void run(){ 4 String threadName = Thread.currentThread().getName(); 5 for(int i=0;i<10;i++){ 6 System.out.println("ThreadTest1 "+threadName+" run

java基础10(多线程2)

线程的常用方法 public final void join()    线程加入 作用:等待该线程中止,其他线程才能继续抢着执行 public static void yield():  线程礼让 作用:暂停当前正在执行的线程对象,并执行其他线程.让线程间的执行更和谐一些,但是实际上做不到. public final void stop():线程死亡:直接杀死 public void interrupt():线程死亡:直接杀死,在死前,还可以有遗言(会执行后面的代码,然后死去). static