02 简单实现多线程

一般来说java实现多线程都会说继承Thread并重写run方法或者实现Runnable接口,但是为什么会是这两种方式?学习还是要知其所以然,我们分析一下源代码,然后写几个简单实现的例子。

Thread和Runnable

首先查看Thread源代码只有一个启动方法start(),研究start()方法的执行过程,发现真正的执行方法体是Thread.start0()调用的Thread.run();
方法注释中包含以下信息:
1.此线程从此开始执行
2.将调用此线程的run()方法
3.当前线程的start方法和其他线程的run方法同时调用时两个线程会同时运行
4.多次启动线程是不合法的,特别是完成后不能重新启动

Thread.start()方法源码:
/**
     * Causes this thread to begin execution; the Java Virtual Machine
     * calls the <code>run</code> method of this thread.
     * <p>
     * The result is that two threads are running concurrently: the
     * current thread (which returns from the call to the
     * <code>start</code> method) and the other thread (which executes its
     * <code>run</code> method).
     * <p>
     * It is never legal to start a thread more than once.
     * In particular, a thread may not be restarted once it has completed
     * execution.
     *
     * @exception  IllegalThreadStateException  if the thread was already
     *               started.
     * @see        #run()
     * @see        #stop()
     */
    public synchronized void start() {
        /**
         * This method is not invoked for the main method thread or "system"
         * group threads created/set up by the VM. Any new functionality added
         * to this method in the future may have to also be added to the VM.
         *
         * A zero status value corresponds to state "NEW".
         */
        if (threadStatus != 0)
            throw new IllegalThreadStateException();

        /* Notify the group that this thread is about to be started
         * so that it can be added to the group's list of threads
         * and the group's unstarted count can be decremented. */
        group.add(this);

        boolean started = false;
        try {
            start0();
            started = true;
        } finally {
            try {
                if (!started) {
                    group.threadStartFailed(this);
                }
            } catch (Throwable ignore) {
                /* do nothing. If start0 threw a Throwable then
                  it will be passed up the call stack */
            }
        }
    }

    private native void start0();

继续分析run()方法:
1.如果此线程是一个Runnable的运行对象,将会调用Runnable对象的run()方法
2.Thread的子类应该重写此方法
由代码可知,如果Runnable为null且子类没有重写run()方法,此run()是个空方法,Thread不做任何操作直接返回

Thread.run()源码:
/**
     * If this thread was constructed using a separate
     * <code>Runnable</code> run object, then that
     * <code>Runnable</code> object's <code>run</code> method is called;
     * otherwise, this method does nothing and returns.
     * <p>
     * Subclasses of <code>Thread</code> should override this method.
     *
     * @see     #start()
     * @see     #stop()
     * @see     #Thread(ThreadGroup, Runnable, String)
     */
    @Override
    public void run() {
        if (target != null) {
            target.run();
        }
    }

查看Runnable赋值过程,发现只有一个构造方法传入了Runnable参数,并通过init()方法赋值给target属性,target仅有两处使用:
1.在start0()方法中调用了target的run()方法
2.在exit()方法中设置target为null

Thread的一个构造函数:
 /**
     * Allocates a new {@code Thread} object. This constructor has the same
     * effect as {@linkplain #Thread(ThreadGroup,Runnable,String) Thread}
     * {@code (null, target, gname)}, where {@code gname} is a newly generated
     * name. Automatically generated names are of the form
     * {@code "Thread-"+}<i>n</i>, where <i>n</i> is an integer.
     *
     * @param  target
     *         the object whose {@code run} method is invoked when this thread
     *         is started. If {@code null}, this classes {@code run} method does
     *         nothing.
     */
    public Thread(Runnable target) {
        init(null, target, "Thread-" + nextThreadNum(), 0);
       //作者注:init方法中进行了赋值:this.target = target;
 }

查看java.lang.Runnable源代码,它只有一个抽象方法run(),void没有返回值并且不能抛出异常;

public interface Runnable {
    public abstract void run();
}
简单总结

通过分析源代码可以Java线程最终要执行的方法体就是run。

 @Override
 public void run() {
     if (target != null) {
         target.run();
     }
 }

所以新建一个自定义线程有两种方法:
1.继承Tread并重写run()方法
2.在构造Thread的时候把要执行的方法用Runnable接口的实现作为参数传入,最终Runnable.run()在Thread.run()中调用执行。

简单实现

简单介绍一下Java多线程的几种基本实现方式,不过Thread和Runnable都无法获取执行结果,需要执行结果可以使用Callable。
新建一个类继承Thread类或者实现Runnable接口
使用匿名内部类
Lambda表达式

继承Thread类的 MyThread

package com.example.demo.thread;

public class MyThread extends Thread {
    private String name;
    public MyThread(String name) {
        this.name = name;
    }
    public void run() {
        for (int i = 0; i < 5; i++) {
            try {
                sleep((int) Math.ceil(Math.random() * 100) * 100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(name + "运行  :  " + i);
        }
    }
}

实现Runnable接口的 MyThreadRunnable

package com.example.demo.thread;

public class MyThreadRunnable  implements Runnable{
    private String name;
    public MyThreadRunnable(String name) {
        this.name=name;
    }
    @Override
    public void run() {
        for (int i = 0; i < 5; i++) {
            try {
                Thread.sleep((int) Math.ceil(Math.random() * 100) * 100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(name + "运行  :  " + i);
        }
    }
}

测试类:

package com.example.demo.thread;

public class TestThread {
    public static void main(String[] args) {
        /**
         * 原始方法实现thread
         * 新建一个类MyThread继承Thread
         * 并重写run方法
         */
        MyThread myThread1 = new MyThread("A");
        MyThread myThread2 = new MyThread("B");
        myThread1.start();
        myThread2.start();

        /**
         * 原始方式通过实现Runnable接口
         *
         */
        new Thread(new MyThreadRunnable("C")).start();
        new Thread(new MyThreadRunnable("D")).start();

        final String E="E";
        final String F="F";
        final String G="G";
        final String H="H";
        /**
         * 匿名内部类方式实现
         * 代码内添加下面代码
         */
        new Thread() {
            @Override
            public void run() {
                for (int i = 0; i < 5; i++) {
                    try {
                        sleep((int) Math.ceil(Math.random() * 100) * 100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(E+"运行  :  " + i);
                }
            }
        }.start();
        new Thread() {
            @Override
            public void run() {
                for (int i = 0; i < 5; i++) {
                    try {
                        sleep((int) Math.ceil(Math.random() * 100) * 100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(F+"运行  :  " + i);
                }
            }
        }.start();


        /**
         * java8+新特特性
         * Lambda 表达式
         */
        new Thread(() -> {
            for (int i = 0; i < 5; i++) {
                try {
                    Thread.sleep((int) Math.ceil(Math.random() * 100) * 100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(G+"运行  :  " + i);
            }
        }).start();
        new Thread(() -> {
            for (int i = 0; i < 5; i++) {
                try {
                    Thread.sleep((int) Math.ceil(Math.random() * 100) * 100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(H+"运行  :  " + i);
            }
        }).start();
    }
}

测试方法运行结果:

A运行  :  0
C运行  :  0
H运行  :  0
D运行  :  0
F运行  :  0
F运行  :  1
C运行  :  1
B运行  :  0
C运行  :  2
F运行  :  2
F运行  :  3
C运行  :  3
A运行  :  1
G运行  :  0
E运行  :  0
C运行  :  4
B运行  :  1
G运行  :  1
D运行  :  1
G运行  :  2
H运行  :  1
D运行  :  2
E运行  :  1
B运行  :  2
B运行  :  3
D运行  :  3
E运行  :  2
A运行  :  2
F运行  :  4
G运行  :  3
H运行  :  2
D运行  :  4
G运行  :  4
B运行  :  4
A运行  :  3
E运行  :  3
H运行  :  3
A运行  :  4
E运行  :  4
H运行  :  4

使用线程池

强烈建议使用线程池获取线程,不建议自己显示建立线程

测试方法:
 public static void testThreadPool(){
        ExecutorService executor = Executors.newCachedThreadPool();
        for(int i=0;i<10;i++){
            final int num=i;
            executor.execute(() -> {
                try {
                    Thread.sleep((int) Math.ceil(Math.random() * 100) * 100);
                }catch (Exception e){
                    e.printStackTrace();
                }
                System.out.println("线程"+num+" 执行了");
            });
        }
    }

执行结果:

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