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 执行了
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。