两年了,才知道如何实现多线程 ,哎

18bf411d97cbec8c1716955323b50576.jpg

文:两年了,才知道如何实现多线程 ,哎
注:中国传统文化,先仔细看,若有用,再点赞, 给自己一点思考的时间
注:微信搜索:CodeCow,关注这个非常 SAO 的程序员

一、为啥会有这篇文章

时光飞逝,回到2017年,年末;小编刚自学完编程以及三大框架,可谓 信心满满,剑指offer。

殊不知,在第一场面试中,被一位小杨(年龄不大)面试官上了一课;这些年过去了,可谓记忆犹新。

在这里插入图片描述
  • 小杨:来,你说说多线程
  • 阿牛:线程是进程中的一个执行单元。。。
  • 小杨:那多线程有几种实现方式
  • 阿牛:2种(毫不犹豫,这谁还不知道!!)
  • 小杨:那这两种有啥区别
  • 阿牛:嗯。。。(瞬间懵B)

就这样,我在忐忑中,接受着小杨的各种拷问,最终以“回去等通知吧”而结束;

面试完,虽隐约能猜到结果,但却也渴望上天,天平的倾斜;

阿牛苦等几天,犹隔三秋,结果以 GG 而结束。

二、浅聊,如何实现多线程

小伙伴们都知道,一个程序在 没有跳转语句 的前提下,都是 由上至下 依次执行。

那现在想要设计一个程序,“边撸代码”且“ 边看大片(正经人) ”,怎么设计?

604396c8aaff82cbe5b69e5b38c7828c.jpg

首先,要解决上述问题,我们得 聊聊多线程,那肯定的先了解什么是 线程进程

在「java编程思想」书的 并发模块 中,有这么两句话,用来描述进程和线程

进程:指一个内存中运行的应用程序,每个进程都有一个独立的内存空间,进程其实就是程序的一次执行过程,是系统运行程序的基本单位

线程:指进程中的一个单一的顺序控制流

:线程其实就是进程中的一个执行单元,负责当前进程中程序的执行

这两句话虽然有点抽象,但却说出了线程和进程的本质。

来来来,我们随便看看,小编系统中的“ 进程

在这里插入图片描述

接下来,我们再看看,小编电脑管家中的“ 线程

在这里插入图片描述

在了解完线程和进程之后,实现多线程可谓“so easy

接下来简单讲几种实现多线程的方式,别再 “两年后了

三、继承Thread类,了解下

又到 “装B” 的时候了,来来来,带大家看一下 Thread类的源码

1596391849(1) - 副本.jpg

接下来,看下Thread类的 “start()方法

1596392023(1) - 副本.jpg

从源码可以看出,Thread类本质上是实现了Runnable接口的一个实例。

因此,启动线程的唯一方式就是通过Thread类的start()方法,start()方法是个 native方法,它会启动一个新的线程,并执行run()方法。

来,接下来,整点 SAO操作

首先,创建 TestThread类(正经人,别多想)

/**
 * Create By CodeCow on 2020/8/3.
 */
public class TestThread {

    public static void main(String[] args) {
        MyThread mt = new MyThread("新线程————看大片");
        //开启新线程
        mt.start();
        //在主方法中执行for循环
        for (int i = 0; i < 5; i++) {
            System.out.println("main线程————撸代码,没意思。。" + i);
        }
    }

    //继承Thread类
    public static class MyThread extends Thread {
        //定义指定线程名称的构造方法
        public MyThread(String name) {
            //调用父类的String参数的构造方法,指定线程的名称(原理:利用继承特点,将线程名称传递)
            super(name);
        }

        //重写run方法,定义线程要执行的代码
        @Override
        public void run() {
            for (int j = 0; j < 5; j++) {
                //getName()方法 来自父亲(就是Thread类中,获取当前线程名称方法)
                System.out.println(getName() + " :好刺激哟,不行了,快、快。。" + j);
            }
        }
    }
}

接下来,咋们 运行下 TestThread,看看是啥结果

Connected to the target VM, address: '127.0.0.1:56321', transport: 'socket'
main线程————撸代码,没意思。。0
main线程————撸代码,没意思。。1
新线程————看大片 :好刺激哟,不行了,快、快。。0
main线程————撸代码,没意思。。2
新线程————看大片 :好刺激哟,不行了,快、快。。1
main线程————撸代码,没意思。。3
新线程————看大片 :好刺激哟,不行了,快、快。。2
新线程————看大片 :好刺激哟,不行了,快、快。。3
新线程————看大片 :好刺激哟,不行了,快、快。。4
main线程————撸代码,没意思。。4
Disconnected from the target VM, address: '127.0.0.1:56321', transport: 'socket'

不难发现“ 撸代码线程”和“ 看大片线程 ”各执行了五次

因此,用这种方式启动线程,直接用 自己的类继承Thread类,并重写他的run()方法 就可以启动线程,并执行自己定义的run()方法了,就可以了。这操作,不6吗!
[图片上传失败...(image-7dfae8-1596706852557)]

四、实现Runnable接口,了解下

这次,小编就不“装B”,直接带大家看一下 Runnable类的源码

1596393304(1) - 副本.jpg

从源码不难发现,Runnable接口从“Java1.0”就已经有了,它内部只有一个抽象方法run()。

因此:要启动线程就要实现Runnable接口并重写它的run()方法。

  • 注意:由于Java不支持多继承,如果自己的类已经继承了其他类,要启动线程就要实现Runnable接口并重写它的run()方法

来来来,实操整一波

首先,创建 TestRunnable类,并实现Runnable接口

/**
 * Create By CodeCow on 2020/8/3.
 */
public class TestRunnable implements Runnable{

    //重写run()方法, 写自己需要的代码
    @Override
    public void run() {
        for (int i = 0; i < 5; i++) {
            //currentThread() 返回对当前正在执行的线程对象的引用
            System.out.println(Thread.currentThread().getName()+" :好刺激哟,不行了,快、快。。" + i);
        }
    }

    public static void main(String[] args) {
        //创建自定义类对象 线程任务对象
        TestRunnable mr = new TestRunnable();
        //创建线程对象
        Thread t = new Thread(mr, "新线程————看大片(正经人别多想)");
        t.start();
        for (int i = 0; i < 5; i++) {
            System.out.println("main线程————撸代码,没意思。。" + i);
        }
    }
}

接下来,咋们 运行下TestRunnable,看看又会是啥结果呢

Connected to the target VM, address: '127.0.0.1:56275', transport: 'socket'
main线程————撸代码,没意思。。0
新线程————看大片(正经人别多想) :好刺激哟,不行了,快、快。。0
main线程————撸代码,没意思。。1
新线程————看大片(正经人别多想) :好刺激哟,不行了,快、快。。1
main线程————撸代码,没意思。。2
新线程————看大片(正经人别多想) :好刺激哟,不行了,快、快。。2
main线程————撸代码,没意思。。3
新线程————看大片(正经人别多想) :好刺激哟,不行了,快、快。。3
main线程————撸代码,没意思。。4
新线程————看大片(正经人别多想) :好刺激哟,不行了,快、快。。4
Disconnected from the target VM, address: '127.0.0.1:56275', transport: 'socket'

不难发现 “ 撸代码线程” 和 “看大片线程” 同样也各执行了五次。稳了,稳了

在这里插入图片描述

稳是稳了,但很多人肯定会认为,这么写不 Low 吗;

Low,Low到死,毕竟JDK都快出15了,连 8 的 “Lambda” 还不会用吗!!!

来来来,看看使用 Lambda表达式 + 匿名内部类 的 SAO操作

/**
 * Create By CodeCow on 2020/8/3.
 */
public class TestRunnableByLambda {

    public static void main(String[] args) {
        new Thread(() -> {
            for (int i = 0; i < 5; i++) {
                System.out.println("新线程————看大片(别多想) :好刺激哟,不行了,快、快。。" + i);
            }
        }).start();
        for (int i = 0; i < 5; i++) {
            System.out.println("main线程————撸代码,没意思。。" + i);
        }
    }
}

其结果不言而喻,也是 “撸代码线程” 和 “看大片线程” 各自执行了五次
[图片上传失败...(image-c9d25f-1596706852557)]
然并卵,同样在实际开发中,并非像上篇文章《答应我,别再if/else校验请求参数了可以吗》那么简单。

也就是说,并非 3 + 2 - 5 * 0 这么简单

假如有需求:需要让异步执行的线程在执行完成后返回一个值给当前的线程,当前的线程需要依赖这个值做一些其他的业务操作!

此时,怎么办!别慌,Callable登场

五、Callable 了解下

同样,也带大家喽一眼 Callable类的源码

1596396836(1) - 副本.jpg

可以看出,Callable接口是 Java1.5 就开始出现了,并且 只有一个带返回值的call()方法。

那么,问题来了,怎么写带返回值的线程按?

淡定,看我表演

首先,创建 TestCallable类,并实现Callable接口

/**
 * Create By CodeCow on 2020/8/3.
 */
@Slf4j
public class TestCallable implements Callable<String> {

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        ExecutorService executorService = Executors.newCachedThreadPool();
        TestCallable testCallable = new TestCallable();
        Future<String> future = executorService.submit(testCallable);
        log.info("future: " + future.get()); //get会阻塞
        executorService.shutdown();
    }

    @Override
    public String call() throws Exception {
        return "带含有返回值的线程";
    }
}

接下来,我们 运行下TestCallable,看下结果

Connected to the target VM, address: '127.0.0.1:57004', transport: 'socket'
03:45:08.723 [main] INFO com.codecow.mini.test.TestCallable - future: 带含有返回值的线程
Disconnected from the target VM, address: '127.0.0.1:57004', transport: 'socket'

不难看出,看出啥了。。。。

后记

好啦,今就先聊到这里吧,本文仅仅是 抛砖引玉 而已,浅聊了实现“多线程”的几种方式。

其次,假如我在两年前我能看到类似的文章,并且“多读书、多看报,少吹牛B,多睡觉”,也不至于与 “小杨” 擦肩而过。哎!两年了。

★★ 好文推荐 ★★

小声BB

  • 中国传统文化,先仔细看,若有用,再点赞, 给自己一点思考的时间
  • 更多幽默、风趣好文,尽在“ CodeCow ” , WX搜索 CodeCow,可以看看

《 Java如海,学习作舟,泛舟于海,方知Java之宽阔 》

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