多线程 - Day28,29 - 2019-04-25

总览

  1. 多线程概述
  2. 多线程的实现方式
  3. 线程控制
  4. 线程的声明周期
  5. 线程同步
  6. 死锁问题
  7. 线程间通信机制(等待唤醒机制)
  8. 线程池
  9. 定时器
  10. 与Collection间关系

1. 多线程概述

什么是多线程?为什么要使用多线程?

了解多线程必须先了解线程,要了解线程,必须先了解进程。因为线程是依赖于进程存在的。

多进程:一边玩游戏,一边听音乐。(多进程的意义:充分利用计算机资源)
单核CPU:在同一时刻只能执行一条指令。系统在不同进程之间进行高速的切换(时间片,一般是毫秒级),给人类一种错觉,好像计算机在同一时刻只执行一个程序

并发:在同一时间段内,执行多个程序,在同一时刻只执行一个程序
并行:在同一时刻执行多个程序

Java命令会启动JVM,即启动了一个进程(JVM 就是一个进程)
该进程会启动一个主线程,然后主线程调用某个类的main方法,所以main方法都是运行在主线程里。(之前我们写的基本都是单线程程序 )

JVM是单线程还是多线程?
JVM是多线程的,至少会创建一个main线程和 一个GC线程。

几个JVM? 每个Java进程 分配一jvm个实例

Java 字节码 解释器。管理内存。

  • 进程:
    正在运行的程序,是系统进行资源分配和调用的独立单位。
    每一个进程都有它自己的内存空间和系统资源。
  • 线程:
    进程的执行路径(任务)
    一个进程如果只有一条执行路径,则称为 单线程程序。
    一个进程如果有多条执行路径,则称为多线程程序(意义:提高程序的执行率)。

为什么要用多线程:

①. 为了更好的利用cpu的资源,如果只有一个线程,则第二个任务必须等到第一个任务结束后才能进行,如果使用多线程则在主线程执行任务的同时可以执行其他任务,而不需要等待;

②. 进程之间不能共享数据,线程可以;

③. 系统创建进程需要为该进程重新分配系统资源,创建线程代价比较小;

④. Java语言内置了多线程功能支持,简化了java多线程编程。


2. 多线程的实现方式

一、继承Thread
二、实现Runnable接口
三、线程池 (实现Callable接口,带返回值

//1. 写一个类继承Thread
public class MyThread extends Thread{

    @Override
    public void run() {
        // super.run(); 没有做任何事情
        // 如果只是一些简单的任务,没有必要新建一个线程。因为线程的创建和销毁要消耗一定的系统资源。
        // 线程里面执行的往往是一些比较耗时的任务。
        // System.out.println("Hello, Thread");

        // 2. 重写run方法
        for (int i = 0; i < 100; i++) {
            System.out.println(getName() + ": " + i);
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

Thread:
    线程是程序中的执行线程。Java 虚拟机允许应用程序并发地运行多个执行线程。

方法一:
    1. 写一个类继承Thread。
    2. 重写run()方法。
    3. 创建子类对象
    4. 启动线程

几个小问题:
1. 为什么要重写run()方法
    把线程要执行的代码封装到run()里面。

2. 启动线程使用的是那个方法?
    start()

3. 线程能不能多次启动?
    不能多次启动,如果启动多次会报 IllegalThreadStateException。

4. run()和start()方法的区别
    run(): 封装线程要执行的代码,直接调用相当于普通的调用,不会创建新的线程。
    start(): 新建一个线程,该线程执行run().

 */
public class ThreadDemo1 {
    public static void main(String[] args) {
        // 3. 创建子类对象
        Thread thread = new MyThread();
        // 4. 启动线程
        // 直接执行run()方法相当于简单的方法调用
        // thread.run();
        // 启动线程:start(), start()会调用系统的API创建一个新的线程,该线程会执行该对象的run()方法

        thread.start();
        thread.start();

        for (int i = 100; i < 2000; i++) {
            System.out.println(i);
        }
    }
}

如何获取线程的名字:
    构造方法:
        Thread(String name)

    String getName()
    void setName(String name)

问题:如何获取主线程的名字呢?
    static Thread currentThread()
          返回对当前正在执行的线程对象的引用。

有了方式一,为什么还需要方式二呢?
    1. 解决Java的单继承的局限性
    2. 便于多线程共享数据
    3. 将任务和线程分离,降低耦合性。


3. 线程的控制、调度

  • 线程睡眠
    static void sleep(long millis)
    在指定的毫秒数内让当前正在执行的线程休眠(暂停执行)
    static void sleep(long millis, int nanos)
    在指定的毫秒数加指定的纳秒数内让当前正在执行的线程休眠(暂停执行)

  • 线程加入
    void join()
    等待该线程终止
    void join(long millis)
    等待该线程终止的时间最长为 millis 毫秒。
    void join(long millis, int nanos)
    等待该线程终止的时间最长为 millis 毫秒 + nanos 纳秒。

  • 线程礼让
    static void yield()
    暂停当前正在执行的线程对象,并执行其他线程
    当前线程放弃执行权,重新加入抢夺CPU的执行权的行列。

  • 守护线程
    void setDaemon(boolean on)
    如果on为true, 将该线程标记为守护线程。
    守护线程:被守护的线程如果结束了,那么守护也会结束。

    问题:
    谁守护谁?
    b.setDaemon(true), 将b为守护线程, 守护的对象:哪个线程运行这行代码,就守护那个线程。

  • 线程中断
    void stop()
    中断线程, 直接将线程干掉,进入死亡。(上课睡觉,被AK扫射
    public void interrupt()
    中断线程。会抛出InterriptedException, 走的异常处理的机制,后面的代码还可以执行。(上课睡觉,被敲醒)

  • 线程的优先级

    void setPriority(int newPriority)
    更改线程的优先级

    int getPriority()
    返回线程的优先级

    默认为5
    public static final int MAX_PRIORITY 10
    public static final int MIN_PRIORITY 1
    public static final int NORM_PRIORITY 5

  • 线程调度

    1. 分时调度(雨露均沾)
    2. 抢占式调度(Java)

4. 线程的生命周期

  • 新建
  • 就绪
  • 运行
  • 死亡
  • 阻塞
    • 一般阻塞
    • 等待阻塞
    • 同步阻塞

5. 线程的安全问题

电影院售票问题,引出线程安全问题。
如何解决线程安全问题?

  1. 同步代码块
    synchronized() 实质上是锁机制
  2. 同步方法
    synchronized
    • 普通成员方法:锁的对象是调用此方法的对象,即this
    • 静态方法:锁的对象是字节码文件对象,class类对象
  3. 锁Lock接口

6. 死锁问题

死锁问题:
在同步代码块嵌套的时候,可能会出现多个线程相互等待的现象。

为了解决死锁问题,线程之间应该相互配合。


7. 线程间的通信机制

线程之间通信的机制(等待唤醒机制):
Object(锁):
void notify() 唤醒一个正在等待获取这个锁对象的线程(随机)
void notifyAll() 唤醒所有正在等待获取这个锁对象的线程
void wait() 当前线程进入阻塞状态,并释放锁对象,等待被唤醒。
void wait(long timeout) 当前线程进入阻塞状态,并释放锁对象,等待被唤醒。如果在这个时间内没有被唤醒,就自动醒来。
void wait(long timeout, int nanos)

思考,为什么不定义在 Thread 类中?
线程之间通信的媒介是锁对象,但是锁对象可以是任意对象。所以等待唤醒这些应该定义Object类中。

举个栗子:生产者 - 消费者模型
线程间还可以配合一起做某些事,如交替数数


8. 线程池

为什么会出现线程池?
启动一个新线程的成本是比较高的,因为它涉及到与操作系统进行交互。这种情况下使用线程池可以更好的提高性能,尤其当前程序需要创建大量的生存周期很短的线程时,更应该考虑线程池

  • 概述
    JDK5后出现的,养线程,当线程执行完任务后,不会销毁线程,而是在回到线程池中成为空闲状态,等待分配任务

  • Executor:
    void execute(Runnable command)

  • Executors:
    public static ExecutorService newCachedThreadPool()
    // 创建一个默认线程池,都是临时工

    public static ExecutorService newFixedThreadPool(int nThreads)
    // 创建一个指定数量初始线程的线程池,都是正式员工

    public static ExecutorService newSingleThreadExecutor()
    // 创建单个线程的线程池,只养一个

  • ExecutorService(线程池) extends Executor:

    • 提交任务:
      void execute(Runnable command)

    Future<T> submit(Callable<T> task)
    Future<?> submit(Runnable task)
    <T> Future<T> submit(Runnable task, T result)

    • 关闭线程池:
      void shutdown()
      启动一次顺序关闭,执行以前提交的任务,但不接受新任务。
      List<Runnable> shutdownNow()
      试图停止所有正在执行的活动任务,暂停处理正在等待的任务,并返回等待执行的任务列表。

Callable<T>: 返回结果并且可能抛出异常的任务
T call()

Future<T>:
V get()
如有必要,等待计算完成,然后获取其结果。
V get(long timeout, TimeUnit unit)
如有必要,最多等待为使计算完成所给定的时间之后,获取其结果(如果结果可用)。


9. 定时器

Timer(定时器)、TimerTask(定时器任务)

  • 概述
    一种线程工具,以后台线程的方式去执行任务。可安排任务执行一次,或者定期重复执行。以后使用定时任务,常常用任务调度框架: quartz。而不会使用Timer。但其底层原理是一样的。

  • 方法
    void schedule(TimerTask task, Date time)
    // 在指定的时间执行指定任务,执行一次。
    void schedule(TimerTask task, Date firstTime, long period)
    // 在指定的时间第一次执行指定任务,每个多少毫秒,再重复的执行这个任务
    void schedule(TimerTask task, long delay)
    // 在指定延迟时间后,执行执行的任务,执行一次。
    void schedule(TimerTask task, long delay, long period)
    // 在指定延迟时间后第一次执行指定任务,每个多少毫秒,再重复的执行这个任务
    void cancel()
    // 终止此计时器,丢弃所有当前已安排的任务。

  • TimerTask:
    abstract void run() // 安排为一次执行或重复执行的任务。


10. Collection集合的线程安全

线程安全的集合:
Vector
HashTable
|-- Properties
StringBuffer
即使Vector是线程安全,但是我们也不使用它。

Collections:
static <T> List<T> synchronizedList(List<T> list) // 视图技术
static <K,V> Map<K,V> synchronizedMap(Map<K,V> m)
static <T> Set<T> synchronizedSet(Set<T> s)

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,386评论 6 479
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,939评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,851评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,953评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,971评论 5 369
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,784评论 1 283
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,126评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,765评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,148评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,744评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,858评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,479评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,080评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,053评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,278评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,245评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,590评论 2 343

推荐阅读更多精彩内容