Java

    技术2022-07-11  105

    有句话说前头:

    Java使用Thread类表示线程,所有线程都会是Thread类或者其子类的实例。

    你所能想到的创建方式

    先来说说最常用的实现多线程的两种方式:Thread和Runnable

    Runnable是一个接口,包含一个run()方法 public interface Runnable{ public abstract void run(); }

    通过实现Runnable的方式来创建线程,实现多线程。比如:

    class MyThread implements Runnable { private int i; //run方法:线程执行体 public void run() { for (; i < 20; i++) { //当线程类实现Runnable接口时 //如果想要获得当前线程,只能用Thread.currentThread()方法 System.out.println(Thread.currentThread().getName()+ " " + i); } } public static void main(String[]args){ //启动线程 for(int i=0;i<20;i++) { System.out.println(Thread.currentThread().getName() + " " + i); if (i == 2) { MyThread mt = new MyThread(); //通过new Thread(target,name)方法创建新线程 new Thread(mt, "新线程1").start(); new Thread(mt, "新线程2").start(); } } } }

    运行结果:

    main 0 main 1 main 2 main 3 main 4 main 5 main 6 main 7 main 8 main 9 main 10 main 11 main 12 main 13 main 14 main 15 main 16 main 17 main 18 main 19 新线程1 0 新线程1 1 新线程1 2 新线程1 3 新线程1 4 新线程1 5 新线程1 6 新线程2 6 新线程2 8 新线程2 9 新线程2 10 新线程2 11 新线程2 12 新线程2 13 新线程2 14 新线程2 15 新线程2 16 新线程2 17 新线程2 18 新线程1 7 新线程2 19

    从Java8开始,Runnable接口使用了@FunctionalInterface修饰,也就是说,Runnable是函数式接口。

    Thread是一个类,本身就实现了Runnable接口,声明如下: public class Thread implements Runnable{}

    实例如下:

    class MyThread extends Thread { private int i; //run方法:线程执行体 public void run() { for (; i < 20; i++) { //当线程类实现Runnable接口时 //如果想要获得当前线程,只能用Thread.currentThread()方法 System.out.println(Thread.currentThread().getName()+ " " + i); } } public static void main(String[]args){ //启动线程 for(int i=0;i<20;i++) { System.out.println(Thread.currentThread().getName() + " " + i); if (i == 2) { MyThread mt = new MyThread(); //通过new Thread(target,name)方法创建新线程 new Thread(mt, "新线程1").start(); new Thread(mt, "新线程2").start(); } } } }

    执行结果如下:

    main 0 main 1 main 2 main 3 main 4 main 5 main 6 main 7 main 8 main 9 main 10 main 11 main 12 main 13 main 14 main 15 main 16 main 17 main 18 main 19 新线程1 0 新线程2 0 新线程2 1 新线程2 2 新线程2 4 新线程2 5 新线程2 6 新线程2 7 新线程2 8 新线程2 9 新线程2 10 新线程2 11 新线程2 12 新线程2 13 新线程2 14 新线程2 15 新线程2 16 新线程2 17 新线程2 18 新线程2 19 新线程1 20

    通过上边的运行结果我们可以发现,线程1和线程2共享线程资源,一共创建了20个线程(共享MyThread)

    使用Callable和Future创建线程

    从Java5之后提供了一种callable接口来创建线程,Callable接口提供了一个call()方法可以作为线程的执行体,和run()方法相比call()方法更为强大,如:

    可以有返回值可以声明抛出异常

    因此可以提供一个Callable对象作为Thread的target,而该线程的线程执行体就是该对象的call方法,但是Callable不是Runnable的子接口,所以Callable对象不能直接作为Thread的target,而需要借助Future接口来代表Callable接口里call()方法的返回值,Future接口提供了一个FutureTask实现类,此类不仅实现了Future接口还实现了Runnable接口。 实例如下:

    class MyThread { public static void main(String[] args) { //创建Callable对象 MyThread mt = new MyThread(); //先使用Lambda表达式创建Callable<Integer>对象 //使用FutureTask包装callable对象 FutureTask<Integer> task = new FutureTask<>((Callable<Integer>) () -> { int i = 0; for (; i < 20; i++) { //当线程类实现Runnable接口时 //如果想要获得当前线程,只能用Thread.currentThread()方法 System.out.println(Thread.currentThread().getName() + " " + i); } //call方法返回值 return i; }); //启动线程 for (int i = 0; i < 20; i++) { System.out.println(Thread.currentThread().getName() + " " + i); if (i == 2) { //实质上还是使用callable对象创建并启动线程的 new Thread(task, "有返回值的线程").start(); } } try { //获取线程返回值 System.out.println("子线程返回值: " + task.get()); } catch (Exception e) { e.printStackTrace(); } } }

    执行结果:

    main 0 main 1 main 2 main 3 main 4 main 5 main 6 main 7 main 8 main 9 main 10 main 11 main 12 main 13 main 14 main 15 main 16 main 17 main 18 main 19 有返回值的线程 0 有返回值的线程 1 有返回值的线程 2 有返回值的线程 3 有返回值的线程 4 有返回值的线程 5 有返回值的线程 6 有返回值的线程 7 有返回值的线程 8 有返回值的线程 9 有返回值的线程 10 有返回值的线程 11 有返回值的线程 12 有返回值的线程 13 有返回值的线程 14 有返回值的线程 15 有返回值的线程 16 有返回值的线程 17 有返回值的线程 18 有返回值的线程 19 子线程返回值: 20

    孰优孰劣???

    很难讲哪种方式就一定好,需要针对于具体的场景来说

    采用实现Runnable、Callable接口的方式创建多线程:     只是实现了Runnable接口或者Callable接口,还可以继承其他类     多个线程可以共享一个target对象,非常适合多个相同的线程处理同一份资源的情况,从而将CPU、代码和数据分开,形成清晰的模型     但是不足的就是编程复杂,如果要访问当前线程需要使用Thread.currentThread()方法

    使用继承Thread类的方式创建多线程:     编程简单,如果需要访问当前线程只需要用this指针就可以     但是不足的就是因为继承了Thread类,就不能再继承其他父类了

    简单总结一下:一般我们推荐的是使用实现Runnable接口或者Callable接口的方式来创建多线程

    Processed: 0.017, SQL: 9