很多学习Java基础的小伙伴肯定听说过多线程。那么我们有几种方式来创建线程呢?在jdk1.5或者jdk5之前有两种方式,一种是继承Thread类,另一种是实现Runnable接口。在jdk1.5后又为我们提供两种方式,一种是实现Callable接口,另一种就是使用线程池。下面我们来简单的说一下四种方式是如何创建线程的。
一、继承Thread类。
继承Thread类需要我们重写run()方法,且将核心代码写到run方法中。这里我们在创建的线程中打印1到100以内的偶数为例。代码如下:
package com.aoshen.java2; //首先我们需要继承Thread类 class NumTest extends Thread { // 重写Thread类的run方法 @Override public void run() { // 将核心代码写到run方法里面 for (int i = 1; i <= 100; i++) { if (i % 2 == 0) { System.out.println(i); } } } } public class ThreadTest { public static void main(String[] args) { NumTest numTest=new NumTest(); // numTest.setName("线程名"); 这里是给线程起一个名字 // 启动线程并调用run方法 numTest.start(); // numTest.start();这里是错误的,对于继承的方式来讲,如果我们对已经start过的线程再调用start就会出错 // 正确的方式是如下方式,也就是需要我们重新创建一个对象来调用 // NumTest numTest1=new NumTest(); // numTest1.start(); } }
二、实现Runnable接口,同样是打印1到100以内的偶数为例:
package com.aoshen.java2; //实现Runnable接口 class NumTest1 implements Runnable { // 重写run方法,并将核心代码写到run方法中 @Override public void run() { for (int i = 1; i <= 100; i++) { if (i % 2 == 0) { System.out.println(i); } } } } public class RunnableTest { public static void main(String[] args) { // 创建对象 NumTest1 numTest = new NumTest1(); // 创建Thread并将上步的对象作为参数传入构造器 Thread thread = new Thread(numTest); // thread.setName("线程名"); // 启动线程 thread.start(); // 如果想再次执行的话,这种实现的方式只需要我们按照如下方式 // 再创建一个Thread对象,将实现类的想传入构造器,并调用 // Thread对象的start即可 // Thread thread1=new Thread(numTest); // thread1.start(); } }
三、实现Callable接口
通过上面的两个例子我们可以看出,不管是继承Thread类还是实现Runnable接口,其核心就是重写run方法,但是run方法的返回值是void也就是说没有返回值。而且run方法是不能抛出异常的,如果我们的业务复杂一下的话,需要捕获异常的话是捕获不到的。那么实现Callable接口的方式可以在我们重新call方法的时候抛出异常,也可以有返回值。比如,我们需要打印1到100以内的偶数,并需要返回偶数的和为例。
package com.aoshen.java2; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.FutureTask; //实现Callable接口,Callable接口是有泛型的,这个 //泛型就是我们我们调用call方法所需要返回的值的类型 class NumTest2 implements Callable{ @Override public Integer call() throws Exception { int sum = 0; for (int i = 1; i <= 100; i++) { // 遍历1到100以内的所有偶数,并将所有偶数的和返回 if (i % 2 == 0) { System.out.println(i); sum += i; } } return sum; } } public class CallableTest { public static void main(String[] args) { // 创建NumTest2对象 NumTest2 numTest2 = new NumTest2(); // 创建FutureTask对象 并将上步的对象作为参数传入 FutureTask futureTask = new FutureTask<>(numTest2); // 创建Thread类,并将上步对象传入 Thread thread = new Thread(futureTask); //启动线程 thread.start(); try { // 获取线程执行后返回的返回值,这一步不是必须的,如果你不需要返回值,则这一步可以不写 Integer integer = futureTask.get(); System.out.println("总和为:" + integer); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } } }
四、使用线程池
使用线程池的好处有以下几点
1.提高效率,不用重复的创建和销毁线程
2.提高利用率
3.可以对线程池进行一些设置
package com.aoshen.java2; import java.util.concurrent.*; //实现遍历1到100以内的偶数 class RunnableDemo implements Runnable { @Override public void run() { for (int i = 1; i <= 100; i++) { if (i % 2 == 0) { System.out.println(i); } } } } //实现遍历 1到100以内的奇数,并将奇数的和返回 class CallableDemo implements Callable{ @Override public Integer call() throws Exception { int sum = 0; for (int i = 1; i <= 100; i++) { // 遍历1到100以内的所有偶数,并将所有偶数的和返回 if (i % 2 != 0) { System.out.println(i); sum += i; } } return sum; } } public class ThreadPollTest { public static void main(String[] args) { // 创建固定线程数量的线程池 ExecutorService service = Executors.newFixedThreadPool(5); RunnableDemo runnableDemo = new RunnableDemo(); // 如果是实现Runnable接口的方式则调用execute方式来执行 service.execute(runnableDemo); CallableDemo callableDemo = new CallableDemo(); // 如果是实现Callable接口的方式则调用submit方式来执行 Future future = service.submit(callableDemo); try { Integer integer = future.get(); System.out.println("和为:" + integer); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } // 最后要记得关闭线程池哦 service.shutdown(); } }
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)