Java多线程/并发01、新建线程的3种方法

引子

首先要理解并发(Concurrency)并行(Parallelism)的区别:

  • 并发是在同一时段发生。多线程就是分时利用CPU,宏观上让所有线程一起执行,也叫并发。但在微观上不是同时执行的,只是把时间分成若干段,使多个进程快速交替的执行。这就好像两个人用同一把铁锨,轮流挖坑,一小时后,两个人各挖一个小一点的坑,要想挖两个大一点得坑,一定会用两个小时。
  • 并行是在同一时刻发生。无论从微观还是宏观,二者都是一起执行的,就好像两个人各拿一把铁锨在挖坑,一小时后,每人一个大坑。

从以上本质不难看出,“并发”执行,在多个进程存在资源冲突时,并没有从根本提高执行效率。

异步和多线程也不是一个同等关系,异步是最终目的,多线程只是我们实现异步的一种手段。异步是当一个调用请求发送给被调用者,而调用者不用等待其结果的返回而可以做其它的事情。实现异步可以采用多线程技术。

下面看看新建线程的3种方法 :

一、继承Thread,实现run()方法

只要两步即可创建并开启一个线程:
继承Thread类,并实现run()方法;
调用start()方法开启线程。

package twm.JConcurrence.demo;

public class NewThreadDemo {
    public static void main(String[] args) {
        /*在主线程中开启10个线程,每个线程需要耗时2秒。但因为线程并发,所以总体运行时间也在2秒左右,并非20秒。*/
        for (int i = 0; i < 10; i++) {
            demothread abc= new demothread("chapter1_thread"+String.valueOf(i));
            abc.start();
        }
    }
    static class demothread extends Thread {
        String put;
        public demothread(String name) {
            super(name);
            this.put = name;
        }
        /*覆写run()方法*/
        public void run() {
            try {
                /*模拟耗时操作,让线程休眠2秒*/
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out
            .println("Class name is : "+this.put+",thread name is :" + Thread.currentThread().getName());
        }
    }

}

输出:

Class name is : chapter1_thread0,thread name is chapter1_thread0
Classname is : chapter1_thread2,thread name is chapter1_thread2
Class name is : chapter1_thread1,thread name is chapter1_thread1
Class name is : chapter1_thread4,thread name is chapter1_thread4
Class name is : chapter1_thread6,thread name is chapter1_thread6
Class name is : chapter1_thread8,thread name is chapter1_thread8
Class name is : chapter1_thread9,thread name is chapter1_thread9
Class name is : chapter1_thread3,thread name is chapter1_thread3
Class name is : chapter1_thread7,thread name is chapter1_thread7
Class name is : chapter1_thread5,thread name is chapter1_thread5

由于只要实现一个run()方法即可,所以可以改写一下上面程序,让代码更简洁。
使用Java中的匿名内部类(原型:new Thread(){ void run{ 实现 } }.start())来实现,代码如下:

package JConcurrence.Study;
public class chapter1 {

    public static void main(String[] args) {
        for (int i = 0; i < 10; i++) {
            final int m=i;
            new Thread(){
                /*覆写run()方法*/
                public void run() {
                    try {
                        /*模拟耗时操作,让线程休眠2秒*/
                        Thread.sleep(2000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out
                    .println("thread name is" + Thread.currentThread().getName()+"输出:"+m);
                }
            }.start();
        }
    }

}

在for循环体内加了一句final int m=i;这是因为使用了匿名内部类,在类的内部隐式调用外部变量i,外部变量需要final修饰,不可修改值。否则会报错:Cannot refer to a non-final variable i inside an inner class defined in a different method

二 、实现Runnable接口

同样只要两步即可创建并开启一个线程:
创建一个实现了Runnable接口的类,并重写run()方法;
将该类的实例化对像作为参数传入Thread类的构造方法中,并调用Thread类的start()方法启动。

package JConcurrence.Study;
public class chapter1 {
    public static void main(String[] args) {
        /*在主线程中开启10个线程,每个线程需要耗时2秒,但因为线程并发,所以总体运行时间也在2秒左右,并非20秒。*/
        for (int i = 0; i < 10; i++) {
            Runnable demoRunnable=new DemoRunable("chapter1runable"+String.valueOf(i));
            new Thread(demoRunnable).start();
        }
    }

    static class DemoRunable implements Runnable {
        String put;
        public DemoRunable(String name) {
            super();
            this.put = name;
        }
        /*覆写run()方法*/
        public void run() {
            try {
                /*模拟耗时操作,让线程休眠2秒*/
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out
            .println("Class name is : "+this.put+",thread name is" + Thread.currentThread().getName());
            }

    }

}

由于只要实现Runnable的一个run()方法,所以可以改写一下上面程序,让代码更简洁。
使用Java中的匿名内部类(原型:new Thread( new Runnable(){ void run{ 实现 } } ).start())来实现,代码如下:

package JConcurrence.Study;
public class chapter1 {

    public static void main(String[] args) {
        /* 在主线程中开启10个线程,每个线程需要耗时2秒,但因为线程并发,所以总体运行时间也在2秒左右,并非20秒。 */
        for (int i = 0; i < 10; i++) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        /* 模拟耗时操作,让线程休眠2秒 */
                        Thread.sleep(2000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("thread name is"
                            + Thread.currentThread().getName());

                }
            }).start();
        }
    }
}

三、实现Callable接口

使用方法和Runable类似,但应用场景不同,会在后面的Callable和Future接口中详细说到Callable。

Runnable和Callable接口的区别:

  1. Callable重写的方法是call(),Runnable重写的方法是run();
  2. Callable的任务执行后可返回值,而Runnable不能返回值;
  3. call方法可以抛出异常,run()不可以;
  4. 运行Callable任务可以拿到一个future对象,表示异步计算的结果,
    它供检查计算是否完成的方法,以等待计算完成,并检索计算的结果。通过Future对象可以了解任务的执行情况,可取消任务的执行,还可以获取执行的结果。

四、总结Run()方法

无论继承Thread类还是实现Runnable接口,代码基本高度一致,核心就是在run()方法的实现中。
通过Thread源码中run()方法,可以看到它自己并不做什么(只是简单的判断一下是否可以调用),而是调用了target的run()方法

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

这个target到底是哪位大侠呢?跟踪发现是这样定义的:

private Runnable target;
target就是Runnable。

Runable的定义是:
@FunctionalInterface
public interface Runnable {
    public abstract void run();
}

我了个去,满山遍野都是run,是不是感觉有零乱。
其实梳理一下就很好理解了。

1、如果我们要让Thread听我们指挥,做我们安排的工作,那么就直接重写Thread类run函数。甭管他里面什么target.run乱七八糟的,你自己想怎么写就怎么写,空着也行。这就是“1、继承Thread,实现run()方法”说讲的内容。

2、如果我们实现Runable接口,并重写他的run方法。而后将这个对像作为参数传入Thread,那么在Thread类内部,会把该参数赋值到target。线程启动后调用Thread.run时运行的代码是target.run(),最终调用了runable实现类中的run方法。所以可以你在runable中的run方法中自己想怎么写就怎么写,空着也行。这就是“实现Runnable接口”讲的内容

3、如果有个哥们他硬要把两样都加上,即:同时把将runable传入Thread,然后又实现Thread中的run方法,那么程序最终会运行runable中的run(),还是Thread中的run()呢?….舌头差点打结了。很明显因为重写Thread中的run方法后,就不会再执行target.run了。因此无论你传什么runable进来,Thread都不会理睬了。看代码:

package JConcurrence.Study;
public class chapter1 {

    public static void main(String[] args) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    /* 模拟耗时操作,让线程休眠2秒 */
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("Executive agent:Runable 's run()");
            }
        }) {
            public void run() {
                try {
                    /* 模拟耗时操作,让线程休眠2秒 */
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out
                        .println("Executive agent:Thread 's Override run()");

            }
        }.start();
    }
}

执行结果:

Executive agent:Thread ‘s Override run()

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值