JAVA多线程实现的四种方法

一、方法一(继承Thread类)

定义类继承Thread,重写run方法,把新线程要做的事写在run方法中,创建线程对象,开启新线程, 内部会自动执行run方法。

public class Demo2 {
   public static void main(String[] args) {
      MyThread mt = new MyThread();     //4,创建Thread类的子类对象
      mt.start();                      //5,开启线程
      
      for(int i = 0; i < 1000; i++) {
         System.out.println("bb");
      }
   }
}

class MyThread extends Thread {             //1,继承Thread
   public void run() {                   //2,重写run方法
      for(int i = 0; i < 1000; i++) {       //3,将要执行的代码写在run方法中
         System.out.println("aaaaaaaaaaaa");
      }
   }
}
public static void main(String[] args) {
   new Thread() {                            //1,继承Thread类
      public void run() {                         //2,重写run方法
         for(int i = 0; i < 1000; i++) {             //3,将要执行的代码写在run方法中
            System.out.println("aaaaaaaaaaaaaa");
         }
      }
   }.start();                               //4,开启线程
}

二、方法二(实现Runnable接口)

定义类实现Runnable接口,实现run方法,把新线程要做的事写在run方法中,创建自定义的Runnable的子类对象,创建Thread对象, 传入Runnable,调用start()开启新线程,内部会自动调用Runnable的run()方法。

Thread(Runnable target)
          分配新的 Thread 对象。
Thread(Runnable target, String name)
          分配新的 Thread 对象。
public class Demo3_Thread {
   public static void main(String[] args) {
      MyRunnable mr = new MyRunnable();  //4,创建Runnable的子类对象
      //Runnable target = mr;    mr = 0x0011
      Thread t = new Thread(mr);       //5,将其当作参数传递给Thread的构造函数
      t.start();                   //6,开启线程
      
      for(int i = 0; i < 1000; i++) {
         System.out.println("bb");
      }
   }
}

class MyRunnable implements Runnable {    //1,定义一个类实现Runnable
   @Override
   public void run() {                   //2,重写run方法
      for(int i = 0; i < 1000; i++) {       //3,将要执行的代码写在run方法中
         System.out.println("aaaaaaaaaaaa");
      }
   } 
}
public static void main(String[] args) {
   new Thread(new Runnable() {                //1,将Runnable的子类对象传递给Thread的构造方法
      public void run() {                         //2,重写run方法
         for(int i = 0; i < 1000; i++) {             //3,将要执行的代码写在run方法中
            System.out.println("bb");
         }
      }
   }).start();                                  //4,开启线程
}

三、两种方式的区别

  • 继承Thread

       由于子类重写了Thread类的run(),当调用start()时,直接找子类的run()方法

       好处:可以直接使用Thread类中的方法,代码简单

       弊端:如果已经有了父类,就不能用这种方法

  • 实现Runnable接口

       构造函数中传入了Runnable的引用,成员变量记住了它,start()调用run()方法时内部判断成员变量Runnable的引用是否为空,不为空编译时看的是Runnable的run(),运行时执行的是子类的run()方法

       好处:即使自己定义的线程类有了父类也没关系,因为有了父类也可以实现接口,而且接口是可以多实现的

       弊端:不能直接使用Thread中的方法需要先获取到线程对象后,才能得到Thread的方法,代码复杂

四、方法三(实现Callable接口)

Callable 接口类似于 Runnable,两者都是为那些其实例可能被另一个线程执行的类设计的。但是 Runnable 不会返回结果,并且无法抛出经过检查的异常。

public class Demo3_Thread {
   public static void main(String[] args) throws Exception{
      MyCallable mc = new MyCallable(100);

      FutureTask<Integer> f = new FutureTask<>(mc);
      new Thread(f).start();

      System.out.println(f.get());
   }

}

class MyCallable implements Callable<Integer> {

   private int number;

   public MyCallable(int number) {
      this.number = number;
   }

   @Override
   public Integer call() throws Exception {
      int sum = 0;
      for (int x = 1; x <= number; x++) {
         sum += x;
      }
      return sum;
   }
}

五、方法四(线程池)

1.概述

程序启动一个新线程成本是比较高的,因为它涉及到要与操作系统进行交互。而使用线程池可以很好的提高性能,尤其是当程序中要创建大量生存期很短的线程时,更应该考虑使用线程池。线程池里的每一个线程代码结束后,并不会死亡,而是再次回到线程池中成为空闲状态,等待下一个对象来使用。在JDK5之前,我们必须手动实现自己的线程池,从JDK5开始,Java内置支持线程池。

2.Executors工厂类

static ExecutorService newFixedThreadPool(int nThreads)
          创建一个可重用固定线程数的线程池,以共享的无界队列方式来运行这些线程。
static ExecutorService newSingleThreadExecutor()
          创建一个使用单个 worker 线程的 Executor,以无界队列方式来运行该线程。

这些方法的返回值是ExecutorService对象,该对象表示一个线程池,可以执行Runnable对象或者Callable对象代表的线程。它提供了如下方法:

<T> Future<T>
submit(Callable<T> task)
          提交一个返回值的任务用于执行,返回一个表示任务的未决结果的 Future。
 Future<?> submit(Runnable task)
          提交一个 Runnable 任务用于执行,并返回一个表示该任务的 Future。

Future 表示异步计算的结果。它提供了检查计算是否完成的方法,以等待计算的完成,并获取计算的结果。

 V get()
          如有必要,等待计算完成,然后获取其结果。

3.使用步骤

  • 创建线程池对象
  • 创建Runnable实例
  • 提交Runnable实例
  • 关闭线程池

4.代码

①提交Runnable

ExecutorService pool = Executors.newFixedThreadPool(2);

// 可以执行Runnable对象或者Callable对象代表的线程
pool.submit(new MyRunnable());
pool.submit(new MyRunnable());

//结束线程池
pool.shutdown();

②提交Callable

// 创建线程池对象
ExecutorService pool = Executors.newFixedThreadPool(2);

// 可以执行Runnable对象或者Callable对象代表的线程
Future<Integer> f1 = pool.submit(new MyCallable(100));
Future<Integer> f2 = pool.submit(new MyCallable(200));

// V get()
Integer i1 = f1.get();
Integer i2 = f2.get();

System.out.println(i1);
System.out.println(i2);

// 结束
pool.shutdown();

public class MyCallable implements Callable<Integer> {
	private int number;
		
	public MyCallable(int number) {
		this.number = number;
	}
		
	@Override
	public Integer call() throws Exception {
		int sum = 0;
		for (int x = 1; x <= number; x++) {
			sum += x;
		}
		return sum;
	}		
}

好处:

  • 可以有返回值
  • 可以抛出异常

弊端:

  • 代码比较复杂,所以一般不用

猜你喜欢

转载自blog.csdn.net/qq_40298054/article/details/87870297