Java多线程总结(二)

前言:

上一篇讲了什么是线程,线程的状态以及如何终止线程,但是却没有讲到如何创建线程,今天就讲解下如何创建线程。因为觉得创建线程比较重要,所以单独拿出来讲啦。

正文:

创建线程的三种方式:

方式一:继承Thread类

public class Demo1 extends Thread {
    public Demo1(String name){
        super(name);
    }

    @Override
    public void run(){
        print();
    }

    private void print() {
        for (int i=0;i<10;i++){
            System.out.println(this.getName()+" : "+i);
        }
    }

    public static void main(String[] args) {
        Demo1 demo1 = new Demo1("张三");//创建线程一
        Demo1 demo2= new Demo1("李四");//创建线程二
        demo1.start();
        demo2.start();
    }


}

运行结果:

张三 : 0
李四 : 0
张三 : 1
李四 : 1
张三 : 2
张三 : 3
张三 : 4
张三 : 5
张三 : 6
张三 : 7
张三 : 8
张三 : 9
李四 : 2
李四 : 3
李四 : 4
李四 : 5
李四 : 6
李四 : 7
李四 : 8
李四 : 9

可以看出来张三和李四出现资源的争夺,也就是张三和李四执行没有固定的执行顺序,这就证明多线程的效果出来啦。

线程的使用细节:

线程的启动使用父类的start()方法

如果线程对象直接调用run(),那么JVM不会当作线程来运行,会认为是普通的方法调用。

线程的启动只能是一次,否则抛出异常

可以直接创建Thread类的对象并启动该线程,但是如果没有重写run(),什么也不执行。

匿名内部类的线程实现方式

方式二:实现Runnable接口创建线程

1:定义了实现Runnable接口

2:重写Runnable接口中的run方法,就是将线程运行的代码放入在run方法中

3:通过Thread类建立线程对象

4:将Runnable接口的子类对象作为实际参数,传递给Thread类构造方法

5:调用Thread类的start方法开启线程,并调用Runable接口子类run方法

为什么要将Runnable接口的子类对象传递给Thread的构造函数,因为自定义的run方法所属对象是Runnable接口的子类对象,所以要让线程去执行指定对象的run方法 

public class Demo2 {
    public static void main(String[] args) {
        MyRun my = new MyRun();
        Thread t1 = new Thread(my);
        t1.start();
        for(int i=0;i<10;i++){
            System.out.println("main:" + i);
        }
    }

}
class MyRun implements Runnable{
    @Override
    public void run() {
       for (int i=0;i<10;i++){
           System.err.println("MyRun:"+i);
       }
    }
}

运行结果:

main:0
MyRun:0
main:1
MyRun:1
MyRun:2
MyRun:3
main:2
MyRun:4
main:3
MyRun:5
main:4
MyRun:6
main:5
MyRun:7
main:6
MyRun:8
main:7
MyRun:9
main:8
main:9

方式三:使用Callable和Future创建线程

1.创建Callable接口的实现类,并实现call()方法,然后创建该实现类的实例(从java8开始可以直接使用Lambda表达式创建Callable对象)。

2.使用FutureTask类来包装Callable对象,该FutureTask对象封装了Callable对象的call()方法的返回值

3.使用FutureTask对象作为Thread对象的target创建并启动线程(因为FutureTask实现了Runnable接口)

4.调用FutureTask对象的get()方法来获得子线程执行结束后的返回值

public class CallableAndFuture {
    //Callable  :一个产生结果
    //Future    :一个拿到结果
    public static void main(String[] args) {
        //1.创建Callable的对象返回值
        Callable<Integer> callable = new Callable<Integer>() {
            public Integer call() throws Exception {
                return new Random().nextInt(100);
            }
        };
        //2.FutureTask实现了两个接口,Runnable和Future,所以它既可以作为Runnable被线程执行,又可以作为Future得到Callable的返回值
        FutureTask<Integer> future = new FutureTask<Integer>(callable);
        //3.作为Runnable被线程执行
        new Thread(future).start();
        try {
            Thread.sleep(5000);// 可能做一些事情
            //4.作为Future得到Callable的返回值
            System.out.println(future.get());// 拿到结果
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }
}

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

参考文章:https://www.jianshu.com/p/79293c654991

总结:

多线程学习上的确比较难,有些知识点容易搞混,但是想成为高级java程序员这是一个无法避开的坑,所以难啃也要啃完。明天就是六一儿童节啦,提前祝大家节日快乐。永远保持一个年轻的心,追寻自己喜爱做的事情。

节日肯定少不了音乐,今天就给大家分享几首歌曲。给匆忙的生活,来点短暂的放松。

适合早上听得几首(激情的歌):Sucker 歌手:Jonas Brothers   Make In China 歌手:Higher Brothers   奇迹再现 歌手:毛华锋 

适合下班路上听的几首: 归去来兮   山中客   我们两   Wo Ladki

我是阿达,一名喜欢分享知识的程序员,时不时的也会荒腔走板的聊一聊电影、电视剧、音乐、漫画,这里已经有231位小伙伴在等你们啦,感兴趣的就赶紧来点击关注我把,哪里有不明白或有不同观点的地方欢迎留言。

发布了98 篇原创文章 · 获赞 917 · 访问量 19万+

猜你喜欢

转载自blog.csdn.net/jdk_wangtaida/article/details/90712889
今日推荐