资讯专栏INFORMATION COLUMN

慕课网_《细说多线程之Thread VS Runnable》学习总结

netScorpion / 2131人阅读

摘要:时间年月日星期六说明本文部分内容均来自慕课网。慕课网教学源码无学习源码第一章课前准备前言课程说明比较和这两种线程创建的方式,需要知道和的基本创建方式。一旦主线程获取到了用户的输入,这时候,阻塞就会解除掉,主线程继续运行,直到结束。

时间:2017年07月08日星期六
说明:本文部分内容均来自慕课网。@慕课网:http://www.imooc.com
教学源码:无
学习源码:https://github.com/zccodere/s...

第一章:课前准备 1-1 前言

课程说明

比较Thread和Runnable这两种线程创建的方式,需要知道Thread和Runnable的基本创建方式。

课程目标和学习内容

线程创建的两种方式比较
线程的生命周期
线程的守护神:守护线程
第二章:Thread VS Runnable 2-1 回顾线程创建的两种方式

方式一:继承Thread类

方式二:实现Runnable接口

线程创建的两种方式

2-2 应用Thread模拟卖票

两种方式的比较

Runnable方式可以避免Thread方式由于Java单继承特性带来的缺陷
Runnable的代码可以被多个线程(Thread实例)共享,适合于多个线程处理同一个资源的情况

案例:模拟买票

代码演示

1.编写MyThread类

package com.myimooc.ticketsthread;

/**
 * 使用 Thread 创建线程
 * @author ZhangCheng on 2017-07-08
 *
 */
public class MyThread extends Thread {
    
    /** 一共有5张火车票 */
    private int ticketsCont = 5;
    /** 窗口,也即是线程的名字 */
    private String name;
    
    public MyThread(String name){
        this.name = name;
    }
    
    // 写买票逻辑
    @Override
    public void run() {
        while(ticketsCont > 0 ){
            // 如果还有票,就卖掉一张
            ticketsCont--;
            System.out.println(name + "卖了1张票,剩余票数为:"+ticketsCont);
        }
    }
}

2.编写TicketsThread类

package com.myimooc.ticketsthread;

/**
 * 主类-启动线程类
 * @author ZhangCheng on 2017-07-08
 *
 */
public class TicketsThread {
    
    public static void main(String[] args) {
        
        // 创建三个线程,模拟三个窗口卖票
        MyThread mt1 = new MyThread("窗口1");
        MyThread mt2 = new MyThread("窗口2");
        MyThread mt3 = new MyThread("窗口3");
        
        // 启动这三个线程,即窗口开始卖票
        mt1.start();
        mt2.start();
        mt3.start();
    }
    
}

运行结果

总共有5张票,但是三个窗口加在一起卖了15张票。造成有些人买了票,上不了车,这种情况不是我们愿意看到的。具体原因,写完Runnable后,会讲解。

2-3 应用Runnable模拟卖票

代码演示

1.编写MyThread类

package com.myimooc.ticketsrunnable;

/**
 * 使用 Runnable 创建线程
 * @author ZhangCheng on 2017-07-08
 *
 */
public class MyThread implements Runnable {
    
    /** 一共有5张火车票 */
    private int ticketsCont = 5;
    
    // 写买票逻辑
    @Override
    public void run() {
        while(ticketsCont > 0 ){
            // 如果还有票,就卖掉一张
            ticketsCont--;
            System.out.println(Thread.currentThread().getName() + "卖了1张票,剩余票数为:"+ticketsCont);
        }
    }
}

2.编写TicketsRunnable类

package com.myimooc.ticketsrunnable;

/**
 * 主类-启动线程类
 * @author ZhangCheng on 2017-07-08
 *
 */
public class TicketsRunnable {
    
    public static void main(String[] args) {
        
        MyThread mt = new MyThread();
        
        // 创建三个线程,模拟三个窗口卖票
        Thread th1 = new Thread(mt,"窗口1");
        Thread th2 = new Thread(mt,"窗口2");
        Thread th3 = new Thread(mt,"窗口3");
        
        // 启动这三个线程,即窗口开始卖票
        th1.start();
        th2.start();
        th3.start();
        
    }
    
}

运行结果

2-4 应用揭秘

两种方式的区别

第三章:线程的生命周期和守护线程 3-1 线程的生命周期

线程的生命周期

创建

新建一个线程对象,如Threaf thd = new Thread()

就绪

创建了线程对象后,调用了线程的start()方法(注意:此时线程只是进入了线程队列,等待获取CPU服务,具备了运行的条件,但并不一定已经开始运行了)

运行

处于就绪状态的线程,一旦获取了CPU资源,便进入到运行状态,开始执行run()方法里面的逻辑

终止

线程的run()方法执行完毕,或者线程调用了stop()方法,线程便进入终止状态    

阻塞

一个正在执行的线程在某些情况下,由于某种原因而暂时让出了CPU资源,暂停了自己的执行,便进入了阻塞状态,如调用了sleep()方法

阻塞状态示意图

3-2 守护线程理论知识

Java线程有两类

用户线程:运行在前台,执行具体的任务
    程序的主线程、连接网络的子线程等都是用户线程
守护线程:运行在后台,为其他前台线程服务
    特点:一旦所有用户线程都结束运行,守护线程会随JVM一起结束工作
    应用:数据库连接池中的监测线程、JVM虚拟机启动后的监测线程
    最常见的守护线程:垃圾回收线程

如何设置守护线程

可以通过调用Thread类的setDaemon(true)方法来设置当前的线程为守护线程

注意事项

setDaemon(true)必须在start()方法之前调用,否则会抛出IllegalThreadStateException异常
在守护线程中产生的新线程也是守护线程
不是所有的任务都可以分配给守护线程来执行,比如读写操作或者计算逻辑
3-3 守护线程代码示例

模拟场景示意图

模拟场景说明

一共有两个线程,一个主线程,一个守护线程。守护线程会在很长的时间内不停的往文件中写数据,主线程会阻塞等待来自键盘的输入。一旦主线程获取到了用户的输入,这时候,阻塞就会解除掉,主线程继续运行,直到结束。而一旦主线程结束,用户线程就没有了。这时候即使数据还没有写完,守护线程也会随虚拟机一起结束运行。

代码演示

1.编写DaemonThread类

package com.myimooc.daemonthread;

import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStream;

/**
 * 守护线程。使用 Runnable 创建线程
 * @author ZhangCheng on 2017-07-08
 *
 */
public class DaemonThread implements Runnable {

    @Override
    public void run() {
        System.out.println("进入守护线程" + Thread.currentThread().getName());
        
        try {
            writeToFile();
        } catch (Exception e) {
            e.printStackTrace();
        }
        
        System.out.println("退出守护线程" + Thread.currentThread().getName());
    }

    private void writeToFile() throws Exception{
        
        File fileName = new File("d:" + File.separator + "daemon.txt");
        // 向文件中追加数据
        OutputStream os = new FileOutputStream(fileName,true);
        int count = 0;
        while(count < 999){
            os.write(("
word" + count).getBytes());
            System.out.println("守护线程" + Thread.currentThread().getName() 
                + "向文件中写入了word" + count);
            count++;
            // 线程休眠1秒
            Thread.sleep(1000);
        }
        os.close();
    }
}

2.编写DaemonThreadDemo类

package com.myimooc.daemonthread;

import java.util.Scanner;

/**
 * 主线程
 * @author ZhangCheng on 2017-07-08
 *
 */
public class DaemonThreadDemo {

    public static void main(String[] args) {
        
        System.out.println("进入主线程" + Thread.currentThread().getName());
        
        DaemonThread daemonThread = new DaemonThread();
        Thread thread = new Thread(daemonThread);
        thread.setDaemon(true);
        thread.start();
        
        Scanner sc = new Scanner(System.in);
        sc.next();
        sc.close();
        
        System.out.println("退出主线程" + Thread.currentThread().getName());
        
    }

}
3-4 使用jstack生成线程快照

常用查看线程工具

jstack

作用:生成JVM当前时刻线程的快照(threaddump,即当前进程中所有线程的信息)
目的:帮助定位程序问题出现的原因,如长时间停顿、CPU占用率过高等

使用命令

jstack -l PID
生成线程快照

快照案例

2017-07-08 23:49:46
Full thread dump Java HotSpot(TM) 64-Bit Server VM (25.101-b13 mixed mode):

"Thread-0" #10 daemon prio=5 os_prio=0 tid=0x000000001d209800 nid=0x2e00 waiting on condition [0x000000001dd2f000]
   java.lang.Thread.State: TIMED_WAITING (sleeping)
        at java.lang.Thread.sleep(Native Method)
        at com.myimooc.daemonthread.DaemonThread.writeToFile(DaemonThread.java:39)
        at com.myimooc.daemonthread.DaemonThread.run(DaemonThread.java:19)
        at java.lang.Thread.run(Thread.java:745)

   Locked ownable synchronizers:
        - None

"Service Thread" #9 daemon prio=9 os_prio=0 tid=0x000000001d1b9800 nid=0x2480 runnable [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

   Locked ownable synchronizers:
        - None

"C1 CompilerThread2" #8 daemon prio=9 os_prio=2 tid=0x000000001d13b000 nid=0x2078 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

   Locked ownable synchronizers:
        - None

"C2 CompilerThread1" #7 daemon prio=9 os_prio=2 tid=0x000000001be2d800 nid=0x24f4 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

   Locked ownable synchronizers:
        - None

"C2 CompilerThread0" #6 daemon prio=9 os_prio=2 tid=0x000000001bddf000 nid=0x2f64 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

   Locked ownable synchronizers:
        - None

"Attach Listener" #5 daemon prio=5 os_prio=2 tid=0x000000001bdde000 nid=0x1c1c waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

   Locked ownable synchronizers:
        - None

"Signal Dispatcher" #4 daemon prio=9 os_prio=2 tid=0x000000001bdc8800 nid=0x247c runnable [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

   Locked ownable synchronizers:
        - None

"Finalizer" #3 daemon prio=8 os_prio=1 tid=0x000000001bdba800 nid=0x1f10 in Object.wait() [0x000000001d12f000]
   java.lang.Thread.State: WAITING (on object monitor)
        at java.lang.Object.wait(Native Method)
        - waiting on <0x000000076b108ee0> (a java.lang.ref.ReferenceQueue$Lock)
        at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:143)
        - locked <0x000000076b108ee0> (a java.lang.ref.ReferenceQueue$Lock)
        at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:164)
        at java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:209)

   Locked ownable synchronizers:
        - None

"Reference Handler" #2 daemon prio=10 os_prio=2 tid=0x00000000027a2800 nid=0x2214 in Object.wait() [0x000000001d02f000]
   java.lang.Thread.State: WAITING (on object monitor)
        at java.lang.Object.wait(Native Method)
        - waiting on <0x000000076b106b50> (a java.lang.ref.Reference$Lock)
        at java.lang.Object.wait(Object.java:502)
        at java.lang.ref.Reference.tryHandlePending(Reference.java:191)
        - locked <0x000000076b106b50> (a java.lang.ref.Reference$Lock)
        at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:153)

   Locked ownable synchronizers:
        - None

"main" #1 prio=5 os_prio=0 tid=0x000000000099d800 nid=0xf2c runnable [0x000000000228e000]
   java.lang.Thread.State: RUNNABLE
        at java.io.FileInputStream.readBytes(Native Method)
        at java.io.FileInputStream.read(FileInputStream.java:255)
        at java.io.BufferedInputStream.read1(BufferedInputStream.java:284)
        at java.io.BufferedInputStream.read(BufferedInputStream.java:345)
        - locked <0x000000076b159560> (a java.io.BufferedInputStream)
        at sun.nio.cs.StreamDecoder.readBytes(StreamDecoder.java:284)
        at sun.nio.cs.StreamDecoder.implRead(StreamDecoder.java:326)
        at sun.nio.cs.StreamDecoder.read(StreamDecoder.java:178)
        - locked <0x000000076b1b9ce8> (a java.io.InputStreamReader)
        at java.io.InputStreamReader.read(InputStreamReader.java:184)
        at java.io.Reader.read(Reader.java:100)
        at java.util.Scanner.readInput(Scanner.java:804)
        at java.util.Scanner.next(Scanner.java:1369)
        at com.myimooc.daemonthread.DaemonThreadDemo.main(DaemonThreadDemo.java:22)

   Locked ownable synchronizers:
        - None

"VM Thread" os_prio=2 tid=0x000000001bd98000 nid=0x2998 runnable

"GC task thread#0 (ParallelGC)" os_prio=0 tid=0x00000000026c6800 nid=0x11b0 runnable

"GC task thread#1 (ParallelGC)" os_prio=0 tid=0x00000000026c8000 nid=0x26a4 runnable

"GC task thread#2 (ParallelGC)" os_prio=0 tid=0x00000000026c9800 nid=0x2d6c runnable

"GC task thread#3 (ParallelGC)" os_prio=0 tid=0x00000000026cb000 nid=0xab0 runnable

"VM Periodic Task Thread" os_prio=2 tid=0x000000001d1f3000 nid=0x2b08 waiting on condition

JNI global references: 6

总结

1.如果有daemon标记,则当前线程为守护线程
2.通过查看线程状态(java.lang.Thread.State: TIMED_WAITING (sleeping))
    可以帮助我们定位到导致程序出现死锁,或者是阻塞问题的原因
3.tid和nid字段可以帮助我们找到CPU占有率很高的线程
第四章:课程总结 4-1 课程总结

课程总结

线程创建的两种方式回顾
线程创建的两种方式比较
线程的声明周期
守护线程
jsrack生成线程快照

建议

多使用Runnable这种方式创建线程

补充

1.程序中的同一资源指的是同一个Runnable对象
2.安全的卖票程序中需要加入同步(Synchronized)

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/67333.html

相关文章

  • 课网_细说Java线程内存可见性》学习总结

    时间:2017年07月09日星期日说明:本文部分内容均来自慕课网。@慕课网:http://www.imooc.com教学源码:无学习源码:https://github.com/zccodere/s... 第一章:课程简介 1-1 课程简介 课程目标和学习内容 共享变量在线程间的可见性 synchronized实现可见性 volatile实现可见性 指令重排序 as-if-seria...

    wupengyu 评论0 收藏0
  • 课网_《使用Google Guice实现依赖注入》学习总结

    摘要:时间年月日星期六说明本文部分内容均来自慕课网。慕课网教学源码学习源码第一章课程介绍课程简介是啥读音是轻量级的依赖注入框架说明一个的框架需要有基础什么是剥离注入轻量级代码少易维护性能优异,跟比较。 时间:2017年10月14日星期六说明:本文部分内容均来自慕课网。@慕课网:http://www.imooc.com 教学源码:https://github.com/zccodere/s......

    2450184176 评论0 收藏0
  • 课网_《Spring Boot热部署》学习总结

    时间:2017年12月01日星期五说明:本文部分内容均来自慕课网。@慕课网:http://www.imooc.com 教学源码:无 学习源码:https://github.com/zccodere/s... 第一章:课程介绍 1-1 课程介绍 热部署的使用场景 本地调式 线上发布 热部署的使用优点 无论本地还是线上,都适用 无需重启服务器:提高开发、调式效率、提升发布、运维效率、降低运维成本 前置...

    Channe 评论0 收藏0
  • 课网_《Java定时任务调度工具详解Timer篇》学习总结

    时间:2017年05月24日星期三说明:本文部分内容均来自慕课网。@慕课网:http://www.imooc.com教学示例源码:无个人学习源码:https://github.com/zccodere/s... 第一章:课程介绍 1-1 课程介绍 什么是定时任务调度 基于给定的时间点,给定的时间间隔或者给定的执行次数自动执行的任务 在Java中的定时调度工具 Timer:小弟,能实现日常60%的定...

    wind5o 评论0 收藏0
  • 《Java编程方法论:响应式RxJava与代码设计实战》序

    摘要:原文链接编程方法论响应式与代码设计实战序,来自于微信公众号次灵均阁正文内容在一月的架构和设计趋势报告中,响应式编程和函数式仍旧编列在第一季度的早期采纳者中。 原文链接:《Java编程方法论:响应式RxJava与代码设计实战》序,来自于微信公众号:次灵均阁 正文内容 在《2019 一月的InfoQ 架构和设计趋势报告》1中,响应式编程(Reactive Programming)和函数式...

    PAMPANG 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<