【并发与多线程】如何创建、运行java线程,实践和思考

如何创建、运行java线程,实践和思考

实践是检验真理的唯一标准,以下代码和理论都是认真整理,实践所得.

有三种实现方法,
1. 第一种继承Thread类
2. 第二种是实现Runnable接口,两者都去重写run()方法
3. JDK1.5以后,增加有用线程程池创建多线程的方式,是java.util.concurrent包中的内容,此节分析前两种方式,第三种不做讨论,

1. 继承Thread

普通写法与匿名写法,因为Java中单继承的原因,此种方法有他的耦合性和局限,new Thread()建立子类对象的实例时,新线程也被创建,start()开启线程.

/**
 * <p>
 * 第一种方式,继承Thread的普通和匿名写法
 * </p>
 */
public class MyThread001 extends Thread {

    @Override
    public void run() {
        System.out.println("第一种启动线程的普通方法,直接继承thread,单一继承的局限");
    }

    public static void main(String[] args) {
        Thread myThread = new Thread(new MyThread001());
        myThread.start();
}
匿名的方式的最大不同

写法一:匿名Thread的实例对象,执行MyThread001的run()方法,涉及两个类,Thread类和MyThread001,其实就是上述的连写

        new Thread(new MyThread001()).start();

写法二:匿名Thread的实例对象,同时,还包含一个MyThread001的匿名子类,总共涉及三个类,一个继承了MyThread001的匿名子类,一个MyThread001,一个Thread

        new Thread(new MyThread001(){
            @Override
            public void run() {
                System.out.println("我是继承MyThread001的匿名子类,需要注意哦");
            }
        }).start();
    }

2.实现Runnable接口

第二种方式,实现Runnable接口的普通和匿名写法,匿名写法的区别和继承Thread的区别是一样的。清楚有哪些匿名内部类。new Thread(new MyThread003()).start()new的是一个MyThread003对象实例,但是new Thread(new MyThread003(){}).start();new的是一个继承了MyThread003的匿名子类。

/**
 * <p>
 *  第二种方式,实现Runnable接口的普通和匿名写法
 * </p>
 */
public class MyThread003 implements Runnable {

    @Override
    public void run() {
        System.out.println("第二种启动线程的普通方法,去实现Runnable接口");
    }

    public static void main(String[] args) {
       Thread thread = new Thread(new MyThread003());
       thread.start();

       new Thread(new MyThread003()).start();

       new Thread(new MyThread003(){}).start();
    }
}

3.匿名方式写法不同,背后的实现方式也是不同的。

研究和汇总分析(核心)

写法一:继承方式的匿名写法

/**
 * <p>
 *  两种方式的匿名写法
 * </p>
 */
public class MyThread002{

    public static void main(String[] args) {

        /*
         * <p>
         *   继承Thread的匿名子类
         *
         *   new Thread(){}.start();这表示调用继承Thread父类的子类对象的run方法,
         *   new Thread(){}表示一个继承了Thread的匿名子类的实例对象
         * </p>
         */
        new Thread(){
            @Override
            public void run() {
                System.out.println("第一种继承Thread的匿名写法01,匿名类,匿名子类还是重写run方法");
            }
        }.start();

写法二:实现Runnable接口的匿名写法

        /*
         * <p>
         *  实现Runnable接口的匿名子类
         *
         *  new Thread(new Runnable(){}).start();
         *  这表示调用Thread对象接受的Runnable对象的run
         *  表示:new Runnable(){}表示一个实现了Runnable接口的匿名子类的实例对象
         * </p>
         */
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("第一种继承Thread的匿名写法02,匿名类,匿名子类还是重写run方法");
            }
        }).start();
}
方式三: lambda表达式写法(写法不同,实现的方式是相同的)
        /*
         * <p>
         *  lambda表达式,是通过实现Runnable的方式
         * </p>
         */
        ///通过 Lambda 表达式创建 Runnable 接口的引用
        Runnable r = () -> System.out.println("通过 Lambda 表达式创建 Runnable 接口的引用");
        new Thread(r).start();
        //  ||
        //  ||
        //  \/
        /*
         * <p>
         *  当不指明函数式接口时,编译器会自动解释这种转化
         *  在上面的代码中,编译器会自动推断:根据线程类的构造函数签名 public Thread(Runnable r) { },
         *  将该 Lambda 表达式赋给 Runnable 接口
         * </p>
         */
        new Thread(() -> System.out.println("Lambda01,第二种继承Runnable匿名写法01的Lambda写法")).start();

        /*
         * <p>
         *  Java 中的 Lambda 表达式通常使用 (argument) -> {body},body内如果是单句可以省略
         *  new Thread(()->{}).start();
         * </p>
         */
        new Thread(()->{System.out.println("Lambda表02{}:Java 中的 Lambda 
        表达式通常使用 (argument) -> {body},body内如果是单句可以省略");}).start();
    }

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

我们来看看下面的测试,这个时候你会困惑,到底哪种方式才是启动线程呢

调用run()和start()时,你并不会感觉到有什么不妥,因为run()方法的确像我们想的那样被调用,编译也不会报错

但是,事实上,run()方法并非由新建的新线程所执行的,而是被新创线程的当前线程所执行。

只有调用了start()方法,才会表现出多线程的特性,异步方式,run()执行时,仍然是当前线程,同步方式.想要由创建的新线程,而不是由当前线程执行run()方法,必须使用新线程(newThread)的start()方法.

/**
 * <p>
 *   分析run和start的区别
 * </p>
 *
 * @author:180285
 * @date: 2018/8/24 9:43
 */
public class MyThread004 implements Runnable{

    @Override
    public void run() {
        String threadName = Thread.currentThread().getName();
        System.out.println("当前线程名 : " + threadName +"  分析run和start的区别......");
    }
    public static void main(String[] args) {
        MyThread004 myThread004 = new MyThread004();
        myThread004.run(); //当前线程名 : main  分析run和start的区别......

        Thread thread01 = new Thread(new MyThread004(),"新开辟的线程0");
        thread01.run();  //当前线程名 : main  分析run和start的区别......
        thread01.start();//当前线程名 : 新开辟的线程0  分析run和start的区别......
    }
}

另一种分析

和上面是完全不同的,下面是一个继承了MyThread004的匿名子类,且run方法直接继承的super.run();另一个MyThread004的匿名类重写了run()方法

        Thread thread02 = new Thread(new MyThread004(){});
        thread02.run();// main  分析run和start的区别......
        thread02.start();//Thread-0  分析run和start的区别......

        Thread thread03 = new Thread(new MyThread004(){
            @Override
            public void run() {
                String threadName = Thread.currentThread().getName();
                System.out.println("当前线程名 : " + threadName 
                    + "  一个继承了MyThread004的匿名子类,且重写run方法");
            }
        });
        thread03.run();//当前线程名 : main  一个继承了MyThread004的匿名子类,且重写run方法
        thread03.start();//当前线程名 : Thread-1  一个继承了MyThread004的匿名子类,且重写run方法
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值