线程的实现方法

tech2025-04-25  15

线程的实现方法

通过集成java.lang.Thread线程类来创建一个线程通过实现java.lang.Runnable接口来创建一个线程带返回值的线程接口CallableJava多线程之Callable接口的实现join()加入线程,会让主线程等待sleep()线程休眠yeild()让出CPU执行权限从下面的代码例子得到结论 我自己对这个概念当时记得,长时间不用,然后又忘了,在这里做笔记给自己回顾使用

通过集成java.lang.Thread线程类来创建一个线程

package com.jay.Thread; /** * @version 0.0.1 * @program: spring-poi-demo * @description: 通过集成java.lang.Thread线程类来创建一个线程 * @author: huangzq * @create: 2020-09-04 08:21 */ public class TestThread { public static void main(String[] args) { /** * 控制台输出结果: * 主线程ID是: 1 * 名称线程2的线程ID是:1 * 名称线程1的线程ID是:11 * 结论: * 1、主线程和线程2的线程ID相同,说明直接调用run()方法不会创建新的线程,而是在主线程中直接调用run()方法,普通的方法调用 * 2、线程1先调用start()方法,而后线程2调用run()方法,最终却线程2先于线程1输出,说明新建的线程并不会影响主线程的执行顺序 */ System.out.println("主线程ID是: " + Thread.currentThread().getId()); Thread t1 = new MyThread("线程1"); t1.start(); Thread t2 = new MyThread("线程2"); /*直接调用run()方法*/ t2.run(); } /** * 自定义线程 */ static class MyThread extends Thread { /*线程名称*/ private String name; public MyThread(String name) { this.name = name; } @Override public void run() { System.out.println("名称:" + name + "的线程ID是:" + Thread.currentThread().getId()); } } }

通过实现java.lang.Runnable接口来创建一个线程

package com.jay.Thread; /** * @version 0.0.1 * @program: spring-poi-demo * @description: 通过实现java.lang.Runnable接口来创建一个线程 * @author: huangzq * @create: 2020-09-04 08:28 */ public class TestRunnable { /** * 其实不管是通过继承Thread类,还是实现Runnable接口的方式,都可以创建一个线程,其结果都是一样的。 * 区别在于: * * 实现Runnable的方式需要将实现Runnable接口的类作为参数传递给Thread,然后通过Thread类调用Start()方法来创建线程, * 直接继承Thread类的话代码更简洁,也更容易理解,但是由于JAVA被设计为只支持单继承,所以如果要继承其他类的同时需要实现线程那就只能实现Runnable接口了, * 这里更推荐实现Runnable接口,实现Runnable的方式更为灵活一些。 * * @param args */ public static void main(String[] args) { System.out.println("主线程的ID是: " + Thread.currentThread().getId()); MyRunnable r1 = new MyRunnable("线程1"); Thread t1 = new Thread(r1); t1.start(); MyRunnable r2 = new MyRunnable("线程2"); Thread t2 = new Thread(r2); /*直接调用run()方法,并不会创建新线程*/ t2.run(); } static class MyRunnable implements Runnable{ private String name; public MyRunnable(String name) { this.name = name; } @Override public void run() { System.out.println("名字:" + name + "的线程ID是: " + Thread.currentThread().getId()); } } }

带返回值的线程接口Callable

package com.jay.Thread; import java.util.concurrent.*; /** * @version 0.0.1 * @program: spring-poi-demo * @description: 带返回值的线程接口Callable * @author: huangzq * @create: 2020-09-04 08:57 */ public class TestCallable { /** * 线程1返回了东西... * 线程2返回了东西... * 线程3返回了东西... * @param args */ public static void main(String[] args) { //创建一个线程池 ExecutorService threadPool = Executors.newFixedThreadPool(3); //创建三个有返回值的任务 MyCallable c1 = new MyCallable("线程1"); MyCallable c2 = new MyCallable("线程2"); MyCallable c3 = new MyCallable("线程3"); Future f1 = threadPool.submit(c1); Future f2 = threadPool.submit(c2); Future f3 = threadPool.submit(c3); try { System.out.println(f1.get().toString()); System.out.println(f2.get().toString()); System.out.println(f3.get().toString()); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); }finally { threadPool.shutdown(); } } /** * 实现带返回值的接口 */ static class MyCallable implements Callable { private String name; public MyCallable(String name) { this.name = name; } @Override public Object call() throws Exception { return name + "返回了东西..."; } } }

Java多线程之Callable接口的实现

package com.jay.Thread; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.FutureTask; /** * @version 0.0.1 * @program: spring-poi-demo * @description: Java多线程之Callable接口的实现 * @author: huangzq * @create: 2020-09-04 09:06 */ /* * 一、创建执行线程的方式三:实现 Callable 接口。 相较于实现 Runnable 接口的方式,方法可以有返回值,并且可以抛出异常。 * * 二、执行 Callable 方式,需要 FutureTask 实现类的支持,用于接收运算结果。 FutureTask 是 Future 接口的实现类 */ public class TestCallableTwo { /** * 例子可以看到: Callable 和 Runnable接口的区别 * * (1)Callable规定的方法是call(),而Runnable规定的方法是run(). * (2)Callable的任务执行后可返回值,而Runnable的任务是不能返回值的。 * (3)call()方法可抛出异常,而run()方法是不能抛出异常的。 * (4)运行Callable任务可拿到一个Future对象, Future表示异步计算的结果。 * 它提供了检查计算是否完成的方法,以等待计算的完成,并检索计算的结果。 * 通过Future对象可了解任务执行情况,可取消任务的执行,还可获取任务执行的结果。 * Callable是类似于Runnable的接口,实现Callable接口的类和实现Runnable的类都是可被其它线程执行的任务。 * @param args */ public static void main(String[] args) { ThreadDemo td = new ThreadDemo(); //1.执行 Callable 方式,需要 FutureTask 实现类的支持,用于接收运算结果。 FutureTask<Integer> result = new FutureTask<>(td); new Thread(result).start(); //2.接收线程运算后的结果 try { //FutureTask 可用于 闭锁 类似于CountDownLatch的作用,在所有的线程没有执行完成之后这里是不会执行的 Integer sum = result.get(); System.out.println(sum); System.out.println("------------------------------------"); } catch (InterruptedException | ExecutionException e) { e.printStackTrace(); } } static class ThreadDemo implements Callable<Integer> { @Override public Integer call() throws Exception { int sum = 0; for (int i = 0; i <= 100000; i++) { sum += i; } return sum; } } }

join()加入线程,会让主线程等待

package com.jay.Thread; /** * @version 0.0.1 * @program: spring-poi-demo * @description: join()加入线程 * @author: huangzq * @create: 2020-09-04 08:37 */ public class TestJoin { /** * 执行结果: * main主线程: main ====> 0 * main主线程: main ====> 1 * 当前线程: 线程1 ===> 0 * main主线程: main ====> 2 * 当前线程: 线程1 ===> 1 * 当前线程: 线程1 ===> 2 * 当前线程: 线程1 ===> 3 * 当前线程: 线程1 ===> 4 * main主线程: main ====> 3 * main主线程: main ====> 4 * 当前线程: 线程2 ===> 0 * 当前线程: 线程2 ===> 1 * 当前线程: 线程2 ===> 2 * 当前线程: 线程2 ===> 3 * 当前线程: 线程2 ===> 4 * main主线程: main ====> 5 * main主线程: main ====> 6 * main主线程: main ====> 7 * main主线程: main ====> 8 * main主线程: main ====> 9 * * 结论: * 1、使用了join()方法之后,主线程会等待子线程结束之后才会结束,join在代码中可以注释来测试看结果 */ public static void main(String[] args) throws InterruptedException { Thread t1 = new MyJoinThread("线程1"); t1.start(); t1.join(); for (int i = 0; i < 10; i++){ if(i == 5){ Thread t2 = new MyJoinThread("线程2"); t2.start(); t2.join(); } System.out.println("main主线程: " + Thread.currentThread().getName() + " ====> " + i); } } static class MyJoinThread extends Thread{ public MyJoinThread(String name) { super(name); } @Override public void run() { for (int i = 0; i < 5; i++){ System.out.println("当前线程: " + Thread.currentThread().getName() + " ===> " + i); } } } }

sleep()线程休眠

package com.jay.Thread; /** * @version 0.0.1 * @program: spring-poi-demo * @description: sleep()线程休眠 * @author: huangzq * @create: 2020-09-04 08:32 */ public class TestSleep { private int i = 10; private Object object = new Object(); /** * 线程: Thread-0开始执行,i的值为:11 * 线程: Thread-0准备进入休眠状态... * 线程: Thread-0休眠结束... * 线程: Thread-0继续执行,i的值为===========>:12 * 线程: Thread-1开始执行,i的值为:13 * 线程: Thread-1准备进入休眠状态... * 线程: Thread-1休眠结束... * 线程: Thread-1继续执行,i的值为===========>:14 * 结论: * 1、当Thread-0进入休眠状态,Thread-1并没有马上执行,而是等待Thread-0休眠结束释放了对象锁才继续执行 * 2、当调用sleep()方法时,必须捕获异常或者向上抛出异常,当线程休眠结束并不会马上执行,而是进入就绪状态,等待CPU的再次调度,调用Sleep()方法相当于是进入了阻塞状态 * 3、补充:这个时候可考察sleep和wait的区别,他们所属对象不同,sleep这个时候不会释放锁,而wait会释放,不会阻塞,synchronized我在上篇的文章中详细介绍了 */ public static void main(String[] args) { TestSleep testSleep = new TestSleep(); Thread t1 = testSleep.new MyTestThread(); t1.start(); Thread t2 = testSleep.new MyTestThread(); t2.start(); } class MyTestThread extends Thread{ @Override public void run() { synchronized (object){ i++; System.out.println("线程: " + Thread.currentThread().getName() + "开始执行,i的值为:" + i); System.out.println("线程: " + Thread.currentThread().getName() + "准备进入休眠状态..."); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("线程: " + Thread.currentThread().getName() + "休眠结束..."); i++; System.out.println("线程: " + Thread.currentThread().getName() + "继续执行,i的值为===========>:" + i); } } } }

yeild()让出CPU执行权限

package com.jay.Thread; /** * @version 0.0.1 * @program: spring-poi-demo * @description: yeild()让出CPU执行权限 * @author: huangzq * @create: 2020-09-04 08:48 */ public class TestYield { /** * 执行结果: * 名字: 线程1执行 * 名字: 线程2执行 * 线程2结果num: 1065788928计算耗时: 8毫秒 * 线程1结果count: 1784293664计算耗时: 138毫秒 * 结论: * 1、调用yield()方法是为了让当前线程让出CPU执行权限,从而可以让CPU去执行其他线程,它和sleep()方法类似同样是不会释放对象锁, * 但是yield()不会控制具体的交出CPU权限的时间,同时也只能让具有相同优先级的线程获得CPU执行时间的机会 * 2、调用yield()方法并不会让当前线程进入阻塞状态,而只是进入就绪状态,只需要等待重新获取CPU的时间片,而Sleep()则会进入阻塞状态 */ public static void main(String[] args) { MyYieldThread t = new MyYieldThread("线程1"); t.start(); MyYieldThread2 t2 = new MyYieldThread2("线程2"); t2.start(); System.out.println("主线程名称:"+Thread.currentThread().getName()); } /** * 线程1的 */ static class MyYieldThread extends Thread{ private String name; public MyYieldThread(String name) { this.name = name; } @Override public void run() { System.out.println("名字: " + name + "执行"); long start = System.currentTimeMillis(); int count = 0; for (int i = 0; i < 1000000; i++){ count = count + (i + 1); //执行到这里就让了cup给线程2执行了 Thread.yield(); } long end = System.currentTimeMillis(); System.out.println(name + "结果count: " + count + ",计算耗时: " + (end - start) + "毫秒"); } } /** * 线程2的 */ static class MyYieldThread2 extends Thread{ private String name; public MyYieldThread2(String name) { this.name = name; } @Override public void run() { System.out.println("名字: " + name + "执行"); long start = System.currentTimeMillis(); int num = 0; for (int i = 0; i < 10000000; i++){ num += i * 8; } long end = System.currentTimeMillis(); System.out.println(name + "结果num: " + num + ",计算耗时: " + (end - start) + "毫秒"); } } }

从下面的代码例子得到结论

1、Callable接口类似于Runnable,从名字就可以看出来了,但是Runnable不会返回结果,并且无法抛出返回结果的异常,而Callable功能更强大一些,被线程执行后,可以返回值,这个返回值可以被Future拿到,也就是说,Future可以拿到异步执行任务的返回值。

2、通常在使用Callable的时候,也会涉及到Future,一般配合一起使用,一个产生结果,一个拿到结果。

3、假设有一个很耗时的返回值需要计算,并且这个返回值不是立刻需要的话,那么就可以使用这个组合,用另一个线程去计算返回值,而当前线程在使用这个返回值之前可以做其它的操作,等到需要这个返回值时,再通过Future得到最终计算结果

参考:https://www.jianshu.com/p/94c1c34053d0

最新回复(0)