1. 继承Thread类
-
实例:
package 使用继承Thread方法创建线程; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; /** * @Author -..----.--...../-.--..-.-----.-/----.-.....-.--/-.----.-...----/-.-.-...-.--... * @Date 2021/10/23 23:00 */ // 1. 创建继承Thread类的类 class WindowTest extends Thread { private static int num = 0; private static Lock lock = new ReentrantLock(true); // 2. 重写run()方法 @Override public void run() { // 线程操作代码 while (true) { try { lock.lock(); if (num <= 20) { System.out.println(getName() + ":" + num); num++; } }finally { lock.unlock(); if (num > 20) { break; } } } } } public class ThreadTest { public static void main(String[] args) { // 3. 创造创建继承Thread类的类的对象 WindowTest w1 = new WindowTest(); WindowTest w2 = new WindowTest(); w1.setName("线程1"); w2.setName("线程2"); // 4. 对象调用start()方法 w1.start(); w2.start(); } } -
提示:
-
start()方法调用
- 启动当前对象的线程
- 调用对象线程的 run() 方法
-
不能直接使用 run() 方法启动线程
-
已经 start() 启用的线程不能再被 start() 方法启用,如果重复start会报错
package October_14; class MyThread extends Thread { @Override public void run() { } } public class uesThread { public static void main(String[] args) { MyThread myThread1 = new MyThread(); // 测试在已被启用的线程能否再次被启用 myThread1.start(); myThread1.start(); } }结果: Exception in thread "main" java.lang.IllegalThreadStateException 会报错 -
因为继承类创建线程需要创建多个线程类的对象,所以线程共享数据用static修饰。
-
2. 实现Runnable接口
-
示例
package 使用Runnable方法创建线程; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; /** * @Author -..----.--...../-.--..-.-----.-/----.-.....-.--/-.----.-...----/-.-.-...-.--... * @Date 2021/10/23 23:40 */ // 1. 创建接口Runnable的实现类 class Window implements Runnable { private int num = 0; private Lock lock = new ReentrantLock(true); // 2. 实现run()方法 @Override public void run() { // 线程操作代码 while (true) { try { lock.lock(); if (num <= 20) { System.out.println(Thread.currentThread().getName() + ":" + num); num++; } } finally { lock.unlock(); if (num > 20) { break; } } } } } public class RunnableTest { public static void main(String[] args) { // 3. 创建实现类对象 Window window = new Window(); // 4. 实现类对象作为参数传入Thread类中,创建Thread类对象。需要多少个线程就创建多少个Thread类对象。 Thread t1 = new Thread(window, "线程1"); Thread t2 = new Thread(window, "线程2"); // 5. Thread类对象调用start()方法 t1.start(); t2.start(); } } -
提示
- 可以多个 Thread 类对象共用同一个 Rannable() 的实现类对象。Rannable() 的实现类对象的数据是共用的。
- 实现一个实现类对象,通过不断创造Thread类的对象来创造线程。
- 因为实现类对象只有一个,所以线程共享数据不需要用static修饰。
- 因为java的单继承性会对继承方法有影响和继承Thread类创造线程方法的共享数据需要static做修饰会对类池有所影响。所以对比继承Thread类创造线程的方法,实现Runnable类创造线程的方法更好。
3. 实现Callable接口
-
注:此方法是JDK 5.0 新增
-
示例:
package test3; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.FutureTask; /** * @Author -..----.--...../-.--..-.-----.-/----.-.....-.--/-.----.-...----/-.-.-...-.--... * @Date 2021/10/23 16:09 */ // 1. 创建实现Callable接口的类 class NumThead implements Callable { // 2. 实现call方法,将此线程需要执行的操作声明在call()方法中 @Override public Object call() throws Exception { return null; } } public class ThreadNew { public static void main(String[] args) { // 3. 创建Callable接口实现类的对象numThead NumThead numThead = new NumThead(); // 4. 将numThead传递到FutureTask的构造器中。 // 创建FutureTask对象futureTask FutureTask futureTask = new FutureTask<>(numThead); // 5. 将futureTask作为参数传到Thread类的构造器中。 // 创建Thread的对象thread,并调用thread的start()方法 new Thread(futureTask, "线程名").start(); new Thread(futureTask, "线程名").start(); // (选做)6. 通过futureTask的get()方法获得类型为Object的返回值 try { // get()返回值即FutureTask构造器参数Callable实现类重写的call()的返回值 Object j = futureTask.get(); System.out.println(j); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } } } -
特点
- call() 可以有返回值
- call()可以抛出异常,被外面的操作捕获,获取异常信息
- Callable支持泛型
- FutureTack支持泛型
- 现在常用的创造多线程的方法
-
个人理解
- Callable作用是:实现call接口对数据进行操作并得到结果
- Future作用是:接受线程返回的结果
- Thread作用是:启动线程
4. 使用线程池
-
注:此方法是JDK 5.0 新增
-
示例:
package 使用线程池; import java.util.concurrent.*; /** * @Author -..----.--...../-.--..-.-----.-/----.-.....-.--/-.----.-...----/-.-.-...-.--... * @Date 2021/10/23 17:35 */ /*class NumberThread1 implements Runnable { @Override public void run() { for (int i = 0; i <= 100; i++) { if (i % 2 == 0) { System.out.println(Thread.currentThread().getName() + ":" + i); } } } }*/ class NumberThread implements Callable { @Override public Object call() { int num = 0; while (true) { if (num <= 100) { num++; System.out.println(Thread.currentThread().getName() + ":" + num); } else { break; } } return num; } } public class ThreadPool { public static void main(String[] args) { // 1. 提供指定线程数量的线程池 ExecutorService service = Executors.newFixedThreadPool(10); // 2. 执行指定实现方法的线程的操作。需要提供实现Runnable接口或Callable接口实现类的对象 //service.execute(new NumberThread1());// 适用于Runnable NumberThread numberThread = new NumberThread(); FutureTask<Object> futureTask = new FutureTask<Object>(numberThread); service.submit(futureTask);// 适用于FutureTask try { System.out.println(futureTask.get()); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } // 3. 关闭连接池 service.shutdown(); } } -
优点:
- 提高响应速度(减少创建新线程的时间)
- 降低资源消耗(可以重复利用线程池里的线程,不需要每次都创建)
- 便于线程管理
-
方法介绍:
- Executors.newCachedThreadPool()(无界线程池,可以进行自动线程回收)
- 一个可以无限扩大的线程池,比较适合处理执行时间比较小的任务。缓存线程池,先查看线程池中是否有当前执行线程的缓存,如果有就resue(复用),如果没有,那么需要创建一个线程来完成当前的调用.并且这类线程池只能完成一些生存期很短的一些任务.并且这类线程池内部规定能resue(复用)的线程,空闲的时间不能超过60s,一旦超过了60s,就会被移出线程池
- Executors.newFixedThreadPool(int)(固定大小线程池)
- 一个固定大小的线程池,可以用于已知并发压力的情况下,对线程数做限制。固定型线程池,和newCacheThreadPool()差不多,也能够实现resue(复用),但是这个池子规定了线程的最大数量,也就是说当池子有空闲时,那么新的任务将会在空闲线程中被执行,一旦线程池内的线程都在进行工作,那么新的任务就必须等待线程池有空闲的时候才能够进入线程池,其他的任务继续排队等待.这类池子没有规定其空闲的时间到底有多长.这一类的池子更适用于服务器.
- Executors.newSingleThreadExecutor()(单个后台线程)
- 一个单线程的线程池,可以用于需要保证顺序执行的场景,并且只有一个线程在执行。表示在任意的时间段内,线程池中只有一个线程在工作
- Executors.newScheduledThreadPool() (支持计划任务的线程池)
- 可以延时启动,定时启动的线程池,适用于需要多个后台线程执行周期任务的场景。调度型线程池,调度型线程池会根据Scheduled(任务列表)进行延迟执行,或者是进行周期性的执行.适用于一些周期性的工作.
- Executors.newCachedThreadPool()(无界线程池,可以进行自动线程回收)
本文组成部分
-
线程池方法介绍部分
版权声明:本文为CSDN博主「Charles Ren」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:blog.csdn.net/chongbin007…