最新消息:XAMPP默认安装之后是很不安全的,我们只需要点击左方菜单的 "安全"选项,按照向导操作即可完成安全设置。

java并发编程之线程的创建

XAMPP案例 admin 294浏览 0评论

前言

Java语言内置了多线程支持:一个Java程序实际上是一个JVM进程,JVM进程用一个主线程来执行main()方法,在main()方法内部,我们又可以启动多个线程。此外,JVM还有负责垃圾回收的其他工作线程等。

和单线程相比,多线程编程的特点在于:多线程经常需要读写共享数据,并且需要同步。例如,播放电影时,就必须由一个线程播放视频,另一个线程播放音频,两个线程需要协调运行,否则画面和声音就不同步。因此,多线程编程的复杂度高,调试更困难。

本节我们来学习Java中创建多线程的几种方式。

方式一 继承Thread

继承Thread,重写里面的run()方法,也就是我们要实现的代码逻辑:

public class MyThread extends Thread {
    @Override
    public void run() {
            System.out.println("hello world");
    }

    public static void main(String[] args) {
        MyThread myThread1 = new MyThread();
        myThread1.start();

    }
}

启动线程时调用Thread类的start()方法即可。

方式二 实现Runable接口

实现Runable接口,并重写里面的run()方法,即需要实现的代码逻辑

public class RunableThread implements Runnable{
    @Override
    public void run() {
        System.out.println("hello world");
    }

    public static void main(String[] args) {
        Thread newThread=new Thread(new RunableThread());
        newThread.start();
    }
}

同样的,将实现了run()方法的实例传到Thread类中就可以实现多线程,调用start()方法启动线程。

方式三 使用lambda

lambda表达式来简化创建线程的过程:

public static void main(String[] args) {
        new Thread(()->{
            System.out.println("hello world!!");
        }).start();
    }

或者使用匿名内部类的方式:

/**

 *描述:匿名内部类创建线程

 */

new Thread(new Runnable() {
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName());
    }
}).start();
}
}

不管事lambda表达式还是匿名内部类,它们仅仅是在语法层面上实现了线程,并不能把它归结于实现多线程的方式

方式四 使用线程池

使用线程池来创建线程举例:

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class RunableThread implements Runnable{
    @Override
    public void run() {
        System.out.println("hello world");
    }

    public static void main(String[] args) {
        ExecutorService executorService = Executors.newFixedThreadPool(3);

        for (int i = 0; i < 3; i++) {
            executorService.submit(new RunableThread());
        }

        executorService.shutdown();
    }
}

使用线程池创建线程时也得传入实现了Runable接口的类,重写里面的run()方法来实现逻辑。

启动线程时使用submit方法将线程添加到线程池中。

我们来解线程池的源码,

dqz00067

可以看到线程池本质上还是通过new Thread()方式来创建线程的,只不过是创建线程的时候指定了一些默认值,比如线程名字、是否是守护线程、线程优先级等等。

方式五 有返回值的 Callable

import java.util.concurrent.*;

public class Task implements Callable<String> {
    @Override
    public String call() throws Exception {
        return "hello!**";
    }

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        ExecutorService executorService = Executors.newFixedThreadPool(3);
        //定义一个任务
        Callable<String> task= new Task();
        //提交任务并获得一个future对象(一个Future类型的实例代表一个未来能获取结果的对象)
        Future<String> future = executorService.submit(task);
        String result = future.get();
        // 从Future获取异步执行返回的结果:
        System.out.println(result);
    }
}

Runnable 创建线程是无返回值的,而 Callable 和与之相关的 Future、FutureTask,它们可以把线程执行的结果作为返回值返回。

无论是 Callable 还是 FutureTask,它们首先和 Runnable 一样,都是一个任务,是需要被执行的,而不是说它们本身就是线程。它们可以放到线程池中执行,submit()方法将任务放到线程池中,并由线程池创建线程,最终都是靠线程来执行的,而子线程的创建最终都脱离不了最初的两种基本方式,也就是实现 Runnable 接口继承 Thread 类

实现 Runnable 接口比继承 Thread 类实现线程要好

实现 Runnable 接口继承 Thread 类实现线程,两者本质上是一种方式,都是构造一个Thread类,这是创建线程的唯一方式。

推荐使用实现Runnable接口的方式:

原因有三:

1.代码架构方面 Runnable接口中只有一个run()方法,定义了需要执行的内容。从代码架构考虑,Runnable负责执行内容,Thread类负责线程启动和属性设置等内容,权责分明。

2.性能方面 使用继承Thread类方式,每次执行任务都需要新建一个独立的线程,执行完任务后销毁线程,如果还想执行这个任务,就必须再新建一个继承Thread的类,如果此时执行的内容比较少,比如只是在 run() 方法里简单打印一行文字,那么它所带来的开销并不大,相比于整个线程从开始创建到执行完毕被销毁,这一系列的操作比 run() 方法打印文字本身带来的开销要大得多,相当于捡了芝麻丢了西瓜,得不偿失。如果我们使用实现 Runnable 接口的方式,就可以把任务直接传入线程池,使用一些固定的线程来完成任务,不需要每次新建销毁线程,大大降低了性能开销。

3.可扩展性方面 Java 语言不支持双继承,如果我们的类一旦继承了 Thread 类,那么它后续就没有办法再继承其他的类,这样一来,如果未来这个类需要继承其他类实现一些功能上的拓展,它就没有办法做到了,相当于限制了代码未来的可拓展性。

总结

•Java中创建多线程的方式有Runnable 接口和继承 Thread 类等好几种,但本质上只有一种,即构造Thread类

•实现 Runnable 接口比继承 Thread 类实现线程好

转载请注明:XAMPP中文组官网 » java并发编程之线程的创建

您必须 登录 才能发表评论!