• Java多线程系列 基础篇02 线程的创建和运行


    1.线程创建的方式常用有两种

    1. 继承 Thread 类创建线程
    2. 实现 Runnable 接口创建线程
    

    2.Thread 和 Runnable的区别

    Thread和Runnable的相同点:都是"多线程的实现方式”。
    Thread和Runnable的不同点:Thread 是类,而Runnable是接口;Thread本身是实现了 Runnable 接口的类。我们知道“一个类只能有一个父类,但是却能实现多个接口”,因此Runnable具有更好的扩展性。此外,Runnable还可以用于“资源的共享”。即,多个线程都是基于某一个Runnable对象建立的,它们会共享Runnable对象上的资源。通常,建议通过“Runnable”实现多线程!
    

    3.Thread和Runnable的多线程示例(暂不考虑数据竞态)

    继承Thread创建步骤
    1. 定义Thread类的子类,并重写该类的run()方法,该方法为线程执行体
    2. 创建Thread子类的实例,也就是创建了线程对象
    3. 启动线程,通过调用线程对象的start()方法来启动线程
    
    Thread创建代码
    package concurrent;
    
    import java.util.concurrent.atomic.AtomicInteger;
    
    /**
     * @Description: 线程创建
     * @Author: lizhouwei
     * @CreateDate: 2018/5/20 16:17
     * @Modify by:
     * @ModifyDate:
     */
    class MyThread extends Thread {
        private AtomicInteger ticket = new AtomicInteger(10);
    
        @Override
        public void run() {
            for (int i = 1; i <= ticket.intValue(); i++) {
                System.out.println(Thread.currentThread().getName() + " ticket " + i);
            }
        }
    }
    
    public class ThreadDmo {
        public static void main(String[] args) {
            MyThread myThread1 = new MyThread();
            myThread1.start();
        }
    }
    
    
    实现Runnable创建步骤
    1. 定义Runnable接口的实现类,重写run()方法,该方法为线程执行体
    2. 创建Runnable实现类的实例,并用这个实例作为Thread的target来创建Thread对象,这个Thread对象才是真正的线程对象
    3. 启动线程,通过调用线程对象的start()方法来启动线程
    
    Runnable创建代码
     package concurrent;
    
    import java.util.concurrent.atomic.AtomicInteger;
    
    /**
     * @Description: 线程创建
     * @Author: lizhouwei
     * @CreateDate: 2018/5/20 16:17
     * @Modify by:
     * @ModifyDate:
     */
    class MyRunnable implements Runnable {
        private AtomicInteger ticket = new AtomicInteger(10);
    
        public void run() {
            for (int i = 1; i <= ticket.intValue(); i++) {
                System.out.println(Thread.currentThread().getName() + " ticket " + i);
            }
        }
    }
    
    public class RunnableDmo {
        public static void main(String[] args) {
            MyRunnable myRunnable = new MyRunnable();
            Thread myThread = new Thread(myRunnable);
            myThread.start();
        }
    }
    

    4. start() 和 run()的区别

    start():启动一个新线程,新线程会执行相应的run()方法。start()不能被重复调用(会抛出异常)。
    run()  :run()就和普通的成员方法一样,可以被重复调用。单独调用run()的话,会在当前线程中执行run(),而并不会启动新线程! 
    

    2. start() 源码(by jdk1.8)

    public synchronized void start() {
             
             //判断状态是否为 New ;
            if (threadStatus != 0)
                throw new IllegalThreadStateException();
    
            // 将线程添加到ThreadGroup中
            group.add(this);
    
            boolean started = false;
            try {
                // 通过start0()启动线程
                start0();
                // 设置started标记
                started = true;
            } finally {
                try {
                    if (!started) {
                        group.threadStartFailed(this);
                    }
                } catch (Throwable ignore) {
                }
            }
        }
    
    

    5. 通过Thread的实例对象调用start()方法到底是怎么启动线程的?下面对其实现方式做一些简单的补充。

    1. 基于Kernel Thread(KLT)的映射来实现:KLT是内核线程,内核线程由OS直接完成调度切换,它相对应用程序的线程来讲只是一个接口,外部程序会使用一种轻量级进程(Light Weight Process,LWP)来与KLT进行一对一的接口调用。也就是说,进程内部会尝试利用OS的内核线程去参与实际的调度,而自己使用API调用作为中间桥梁与自己的程序进行交互。
    2. 基于用户线程(User Thread,UT)的实现:这种方式是考虑是否可以没有中间这一层映射,自己的线程直接由CPU来调度,或许理论上效率会更高。不过这样实现时,用户进程所需要关注的抽象层次会更低一些,跳过OS更加接近CPU,即自己要去做许多OS做的事情,自然的OS的调度算法、创建、销毁、上下文切换、挂起等都要自己来搞定(因为CPU只做计算)。这样做显然很麻烦,许多人曾经尝试过,后来放弃了。
    3. 混合实现方式:它的设计理念是希望保留Kernel线程原有架构,又想使用用户线程,轻量级进程依然与Kernel线程一一对应保持不变,唯一变化的就是轻量级进程不再与进程直接挂钩,而是与用户线程挂钩,用户线程并不一定必须与轻量级进程一一对应,而是多对多,就像在使用一个轻量级进程列表一样,这样增加了一层来解除轻量级进程与原进程之间的耦合,可能会使得调度更为灵活。

    在以前的JDK版本中,尝试使用UT的方式来实现,但后来放弃了,采用了与Kernel线程对应的方式,至于一些细节,与具体的平台有很大的关系,JVM会适当考虑具体平台的因素去实现,在JVM规范中也没规定过必须如何去实现,所以对于程序员来讲,只需要知道在new Thread(),调用start()方法后,理论上就有一个可以被OS调度的线程了。

  • 相关阅读:
    C++中的指针常量与常量指针
    Ubuntu16.04下安装ROS kinetic常见问题及解决方法
    关于安装ROS的资料备份
    后台模块--删除、修改用户信息
    客车网上售票系统--查询、添加用户
    客车网上售票系统--登录
    客车网上售票系统--需求分析(一)
    简单的邮件发送器(二)
    简单的邮件发送器(一)
    在CMD上用telnet远程登录发送邮件测试记录
  • 原文地址:https://www.cnblogs.com/lizhouwei/p/9063598.html
Copyright © 2020-2023  润新知