慕课细说多线程 Thread VS Runnable 总结

多线程Thread VS Runnable两个案例

线程的启动方式

  线程的启动方式,最直观的有两种:

  • 继承Thread类
  • 实现Runable接口

下面来看一段代码:
1. 继承了Thread类的HelloWorld


/**
 * @author sy
 * Thread 版 HelloWorld
 */
public class ThreadTest {

    public static void main(String[] args) {

        Thread thread1 = new HelloWorld("我是老大线程");
        Thread thread2 = new HelloWorld("我是老二线程");
        Thread thread3 = new HelloWorld("我是老三线程");
        thread1.start();
        thread2.start();
        thread3.start();
    }
}

class HelloWorld extends Thread {

    private String name;

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

    @Override
    public void run() {
        System.out.println("本线程名字:" + name + ",Hello World!");
    }
}

控制台输出:

第一次执行:
本线程名字:我是老大线程,Hello World!
本线程名字:我是老二线程,Hello World!
本线程名字:我是老三线程,Hello World!
第二次执行:
本线程名字:我是老大线程,Hello World!
本线程名字:我是老三线程,Hello World!
本线程名字:我是老二线程,Hello World!

2.实现了Runnable接口的

/**
 * @author sy
 */
public class RuannableTest {

    public static void main(String[] args) {
        HelloWorld2 helloWorld2 = new HelloWorld2();
        Thread thread = new Thread(helloWorld2,"我是老大线程");
        Thread thread2 = new Thread(helloWorld2,"我是老二线程");
        Thread thread3 = new Thread(helloWorld2,"我是老三线程");
        thread.start();
        thread2.start();
        thread3.start();
    }
}

class HelloWorld2 implements Runnable {

    @Override
    public void run() {
        System.out.println("Hello World!"+ "当前线程名字:" + Thread.currentThread().getName());
    }
}

控制台结果:

第一次执行:
Hello World!当前线程名字:我是老大线程
Hello World!当前线程名字:我是老三线程
Hello World!当前线程名字:我是老二线程
第二次执行:
Hello World!当前线程名字:我是老大线程
Hello World!当前线程名字:我是老二线程
Hello World!当前线程名字:我是老三线程

3.二者区别:
①一个是通过继承方式去实现多线程,一个是通过实现接口去实现,在工作中,肯定是实现接口的方式去实现多线程比较灵活,扩展性更高一些。

②在继承了Thread的HelloWorld类中,我们需要手动去创建一个私有属性name,然后通过构造函数的方式来给线程命名,而实现了runnable接口的类可以直接通过Thread.currentThread().getName()的方式来获取新创建的线程对象中构造的name属性。

都是通过start方法去启动线程,但是继承了Thread类的创建实例可以用Thread这个父类去new自己对应的新类对象,再去调用start方法,而实现了接口的对象类,是将新创建的对象类作为参数传进Thread thread = new Thread(xxxx)里。

4.线程的启动“顺序”:

可以从控制台结果看出:每个线程启动的时间是不定的,虽然程序代码是按顺序执行的,先执行了thread1.start(),然后是2,然后是3,于是线程1先启动,但是此时线程1并没有获取到CPU资源,而线程2强占先机,获取到了CPU资源,此时就是2先输出了。而下来2可能执行完了,此时让出CPU资源,线程1再次夺回CPU资源,此时线程1执行,最后是线程3执行了,所以这样的输出结果是2,1,3。如果你执行一次的顺序是1,2,3,多执行几次即可看到结果。

5.继承Thread类需要注意的点
同一个Thread对象,不允许调用两次start方法,否则会报出illegalThreadStateException异常。
例如:

Thread thread1 = new HelloWorld("我是老大线程");
thread1.start()
thread1.start()

通过“卖票”更深一步了解线程

还是两种方式去实现卖票,比如现在构想一个场景是卖票,一共有5张票,同时去让3个窗口去卖票,用多线程去实现!

1.继承Thread类的:

public class TicketThread{

    public static void main(String[] args) {

        //创建三个窗口,模拟卖票
        Thread mt1 = new MyThread("窗口1");
        Thread mt2 = new MyThread("窗口2");
        Thread mt3 = new MyThread("窗口3");

        //启动这三个线程,也是窗口,
        mt1.start();
        mt2.start();
        mt3.start();
    }
}

class MyThread extends Thread{

    /**
     * 一共有5张火车票
     */
    private int ticksCount = 5;
    /**
     * 窗口,也是线程的名字
     */
    private String name;

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

    @Override
    public void run() {
        super.run();
        while (ticksCount>0){
            ticksCount--;   //如果还有一张票,就卖掉一张
            System.out.println(name + "卖了1张票,剩余票数为:" + ticksCount);
        }
    }
}

控制台结果:

/**
     窗口1卖了1张票,剩余票数为:4
     窗口2卖了1张票,剩余票数为:4
     窗口2卖了1张票,剩余票数为:3
     窗口2卖了1张票,剩余票数为:2
     窗口2卖了1张票,剩余票数为:1
     窗口1卖了1张票,剩余票数为:3
     窗口2卖了1张票,剩余票数为:0
     窗口3卖了1张票,剩余票数为:4
     窗口3卖了1张票,剩余票数为:3
     窗口1卖了1张票,剩余票数为:2
     窗口3卖了1张票,剩余票数为:2
     窗口1卖了1张票,剩余票数为:1
     窗口3卖了1张票,剩余票数为:1
     窗口1卖了1张票,剩余票数为:0
     窗口3卖了1张票,剩余票数为:0
 */

2.实现Runnable接口的

public class ticketRunnable {

    public static void main(String[] args) {

        MyThread2 mt = new MyThread2();
        Thread t1 = new Thread(mt,"窗口1");
        Thread t2 = new Thread(mt,"窗口2");
        Thread t3 = new Thread(mt,"窗口3");
        t1.start();
        t2.start();
        t3.start();
    }

}
class MyThread2 implements Runnable{

    /**
     * 买票的票数
     */
    private int ticketCount = 5;


    @Override
    public void run() {
        while(ticketCount > 0){
            ticketCount--;
            System.out.println(Thread.currentThread().getName()+"卖了1张票,剩余票数为:" + ticketCount);
        }
    }
}

控制台结果:

/**
    窗口1卖了1张票,剩余票数为:4
    窗口1卖了1张票,剩余票数为:3
    窗口3卖了1张票,剩余票数为:1
    窗口3卖了1张票,剩余票数为:0
    窗口2卖了1张票,剩余票数为:2
 */

3.卖票两者区别:

①继承Thread类: 我们可以从控制台看到结果,明明是三个窗口去卖5张票(三个窗口加起来一共卖5张,而不是每个窗口各自有5张票),然而执行结果确实每个窗口都卖了5张票,也就是每个线程都执行了5次。这样明显是不符合结果的。

②实现了Runnable接口的:控制台可以看到3个窗口,一个卖了5张票,解决了Thread那种冲突,但是还有个问题,就是剩余票数并不是按照4,3,2,1,0这样的顺序卖完的,而是窗口1在第四行卖完了票,而窗口3在第五却又输出了一个1,这是为什么呢?
其实这点在上面解释过了,线程的启动确实是按照代码顺序去启动,但是强占CPU资源却是不固定的,线程1抢到了第一次的CPU资源,于是卖出1张票,并且执行完了打印输出语句,线程1再次抢到CPU资源继续卖票与打印语句此时还有3张票,接下来是线程2强占了CPU资源,但是还没来得及输出打印语句,就让出了CPU资源,线程3开始占用CPU资源,卖了一张票,并且打印输出语句,控制台第四行线程3再次占用CPU资源,卖完了最后一张票,最终让出CPU资源,让线程2,也就是窗口2执行完了打印语句。。。所以线程2的打印语句才会出现在控制台第5行。

4.出现两种不同情况的原因:
①继承了Thread类卖票为什么会出现3个线程卖了15张票呢?
因为每次新起一个线程,都是新创建了一个对象,而每个对象里的票不是共享的,每次创建一个新的对象,他自己的属性中就含有5张票,所以会出现15张的情况。
②实现Runnable接口的类:
创建一个实例,作为参数传进不同的三个线程中,作为一个共享资源去“卖票”。所以不会有15张票的情况。

线程的生命周期

盗用慕课一张图:

这里写图片描述

创建:对应的代码就是new了一个Thread对象。

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

阻塞状态:一个正在执行的线程在某些情况下,由于某种原因而暂停让出了CPU资源,暂停
了自己的执行,便进入阻塞状态,例如在线程中掉用sleep()方法,wait(),或者join()
终止:线程的run()方法执行完毕,线程便进入终止状态。

这里写图片描述

守护线程

java线程有两类

1.用户线程:运行在前台,执行具体的任务
程序的主线程、连接网络的子线程都是用户线程

2.守护线程:运行在后台,为其他线程服务。
特点:一旦所有用户线程结束运行,守护线程会随着JVM一起结束工作
应用:数据库连接池中的监测线程、JVM虚拟机启动后的监测线程。
最常见的守护线程:垃圾回收线程。

这里写图片描述

jstack生成线程快照

jdk中自带的分析线程的工具,目录如下:

这里写图片描述

我们可以通过CMD去启动查看jstack的帮助,前提是配置了jdk环境变量:

这里写图片描述

一般命令直接输入 jstack pid 即可[-l]可以省略

我用debug打住了刚才java程序的一个断点,然后获取任务管理器的pid:

这里写图片描述

再次打开cmd来输入命令

这里写图片描述

cmd中生成的快照如下:


"NettythreadDeathWatcher-2-1" #12 daemon prio=1 os_prio=-2 tid=0x32d0a000 nid=0x35ec waiting on condition [0x3359f000]
   java.lang.Thread.State: TIMED_WAITING (sleeping)
        at java.lang.Thread.sleep(Native Method)
        at io.netty.util.ThreadDeathWatcher$Watcher.run(ThreadDeathWatcher.java:152)
        at io.netty.util.concurrent.DefaultThreadFactory$DefaultRunnableDecorator.run(DefaultThreadFactory.java:144)
        at java.lang.Thread.run(Thread.java:748)

   Locked ownable synchronizers:
        - None

"DestroyJavaVM" #11 prio=5 os_prio=0 tid=0x03073000 nid=0x4780 waiting on condition [0x00000000]
   java.lang.Thread.State: RUNNABLE

   Locked ownable synchronizers:
        - None

"JPS thread pool" #10 prio=5 os_prio=0 tid=0x32be7000 nid=0x12a0 waiting on condition [0x3330f000]
   java.lang.Thread.State: TIMED_WAITING (parking)
        at sun.misc.Unsafe.park(Native Method)
        - parking to wait for  <0x13d0e9b0> (a java.util.concurrent.SynchronousQueue$TransferStack)
        at java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:215)
        at java.util.concurrent.SynchronousQueue$TransferStack.awaitFulfill(SynchronousQueue.java:460)
        at java.util.concurrent.SynchronousQueue$TransferStack.transfer(SynchronousQueue.java:362)
        at java.util.concurrent.SynchronousQueue.poll(SynchronousQueue.java:941)
        at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1073)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1134)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
        at java.lang.Thread.run(Thread.java:748)

   Locked ownable synchronizers:
        - None

"JPS thread pool" #9 prio=5 os_prio=0 tid=0x32be2400 nid=0x528 waiting on condition [0x3327f000]
   java.lang.Thread.State: TIMED_WAITING (parking)
        at sun.misc.Unsafe.park(Native Method)
        - parking to wait for  <0x13d0e9b0> (a java.util.concurrent.SynchronousQueue$TransferStack)
        at java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:215)
        at java.util.concurrent.SynchronousQueue$TransferStack.awaitFulfill(SynchronousQueue.java:460)
        at java.util.concurrent.SynchronousQueue$TransferStack.transfer(SynchronousQueue.java:362)
        at java.util.concurrent.SynchronousQueue.poll(SynchronousQueue.java:941)
        at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1073)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1134)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
        at java.lang.Thread.run(Thread.java:748)

   Locked ownable synchronizers:
        - None

"JPS thread pool" #8 prio=5 os_prio=0 tid=0x31ea9000 nid=0x2914 runnable [0x3210f000]
   java.lang.Thread.State: RUNNABLE
        at sun.nio.ch.WindowsSelectorImpl$SubSelector.poll0(Native Method)
        at sun.nio.ch.WindowsSelectorImpl$SubSelector.poll(WindowsSelectorImpl.java:296)
        at sun.nio.ch.WindowsSelectorImpl$SubSelector.access$400(WindowsSelectorImpl.java:278)
        at sun.nio.ch.WindowsSelectorImpl.doSelect(WindowsSelectorImpl.java:159)
        at sun.nio.ch.SelectorImpl.lockAndDoSelect(SelectorImpl.java:86)
        - locked <0x13d23688> (a io.netty.channel.nio.SelectedSelectionKeySet)
        - locked <0x13d0fe58> (a java.util.Collections$UnmodifiableSet)
        - locked <0x13d0a880> (a sun.nio.ch.WindowsSelectorImpl)
        at sun.nio.ch.SelectorImpl.select(SelectorImpl.java:97)
        at io.netty.channel.nio.SelectedSelectionKeySetSelector.select(SelectedSelectionKeySetSelector.java:62)
        at io.netty.channel.nio.NioEventLoop.select(NioEventLoop.java:752)
        at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:408)
        at io.netty.util.concurrent.SingleThreadEventExecutor$5.run(SingleThreadEventExecutor.java:858)
        at org.jetbrains.jps.service.impl.SharedThreadPoolImpl.lambda$executeOnPooledThread$0(SharedThreadPoolImpl.java:42)
        at org.jetbrains.jps.service.impl.SharedThreadPoolImpl$$Lambda$2/26379843.run(Unknown Source)
        at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
        at java.util.concurrent.FutureTask.run(FutureTask.java:266)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
        at java.lang.Thread.run(Thread.java:748)

   Locked ownable synchronizers:
        - <0x13d53080> (a java.util.concurrent.ThreadPoolExecutor$Worker)

"Service Thread" #7 daemon prio=9 os_prio=0 tid=0x314ed400 nid=0x2550 runnable [0x00000000]
   java.lang.Thread.State: RUNNABLE

   Locked ownable synchronizers:
        - None

"C1 CompilerThread0" #6 daemon prio=9 os_prio=2 tid=0x314e6800 nid=0x2214 waiting on condition [0x00000000]
   java.lang.Thread.State: RUNNABLE

   Locked ownable synchronizers:
        - None

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

   Locked ownable synchronizers:
        - None

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

   Locked ownable synchronizers:
        - None

"Finalizer" #3 daemon prio=8 os_prio=1 tid=0x314ce400 nid=0x3a4 in Object.wait() [0x317cf000]
   java.lang.Thread.State: WAITING (on object monitor)
        at java.lang.Object.wait(Native Method)
        - waiting on <0x13bc9100> (a java.lang.ref.ReferenceQueue$Lock)
        at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:143)
        - locked <0x13bc9100> (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=0x314b8000 nid=0x1c1c in Object.wait() [0x3173f000]
   java.lang.Thread.State: WAITING (on object monitor)
        at java.lang.Object.wait(Native Method)
        - waiting on <0x13bc92a0> (a java.lang.ref.Reference$Lock)
        at java.lang.Object.wait(Object.java:502)
        at java.lang.ref.Reference.tryHandlePending(Reference.java:191)
        - locked <0x13bc92a0> (a java.lang.ref.Reference$Lock)
        at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:153)

   Locked ownable synchronizers:
        - None

"VM Thread" os_prio=2 tid=0x314b2800 nid=0xd4 runnable

"VM Periodic Task Thread" os_prio=2 tid=0x3151dc00 nid=0x2684 waiting on condition

JNI global references: 315
 //""中的是线程的名字,一般有主线程是“main”这样的
 "VM Periodic Task Thread"
 //这个是线程的状态,在上面的图中对应着每个不同的状态,下面就是等待的状态
 java.lang.Thread.State: WAITING (on object monitor)
 //带有daemon标识的就是守护线程,下面这个就是。
 "Reference Handler" #2 daemon prio=10 os_prio=2 tid=0x314b8000 nid=0x1c1c in Object.wait() [0x3173f000]
 //线程是否处于同步块中
  Locked ownable synchronizers:
        - None

至此完……2017-12-10

已标记关键词 清除标记
相关推荐
©️2020 CSDN 皮肤主题: 创作都市 设计师:CSDN官方博客 返回首页