使用线程
实现 Runnable 接⼝;
实现 Callable 接⼝;
继承 Thread 类。
实现 Runnable 和 Callable 接⼝的类只能当做⼀个可以在线程中运⾏的任务,不是真正意义上的线程, 因此最后还需要通过 Thread 来调⽤。可以理解为任务是通过线程驱动从⽽执⾏的。
实现 Runnable 接⼝
需要实现接⼝中的 run() ⽅法。
public class Main { public static void main(String[] args) { Runnable rn = new runn(); Thread thre = new Thread(rn); thre.start(); for (int i = 0; i < 10; i++) { System.out.println("zhu " + i); } } } class runn implements Runnable { public void run() { for (int i = 0; i < 10; i++) { System.out.println("zi " + i); } } }
zi 0
zi 1
zhu 0
zi 2
zhu 1
zi 3
zhu 2
zi 4
zi 5
zi 6
zhu 3
zi 7
zhu 4
zi 8
zhu 5
zi 9
zhu 6
zhu 7
zhu 8
zhu 9
使⽤ Runnable 实例再创建⼀个 Thread 实例,然后调⽤ Thread 实例的 start() ⽅法来启动线程
实现 Callable 接⼝
与 Runnable 相⽐,Callable 可以有返回值,返回值通过 FutureTask 进⾏封装。
Runnable
接口 不会返回结果或抛出检查异常,但是 Callable
接口 可以
工具类 Executors
可以实现将 Runnable
对象转换成 Callable
对象
import java.util.*; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.FutureTask; public class Main { public static void main(String[] args) throws ExecutionException, InterruptedException { Callable<String> mc = new MyCallable(); FutureTask<String> ft = new FutureTask<>(mc); Thread thread = new Thread(ft); thread.start(); try { String s1 = ft.get(); // 等待保证一定线程执行完,才拿结果 System.out.println("结果: " + s1); } catch (Exception e) { e.printStackTrace(); } } } class MyCallable implements Callable<String> { public String call() throws Exception { String s = ""; for (int i = 0; i < 3; i++) { s += Integer.toString(i); } return s; } }
结果: 012
继承 Thread 类
同样也是需要实现 run() ⽅法,因为 Thread 类也实现了 Runable 接⼝。 当调⽤ start() ⽅法启动⼀个线程时,虚拟机会将该线程放⼊就绪队列中等待被调度,当⼀个线程被调度 时会执⾏该线程的 run() ⽅法。
public class Main { public static void main(String[] args) { Thread rn = new th(); rn.start(); for (int i = 0; i < 10; i++) { System.out.println("zhu " + i); } // System.out.println(fu.get()); } } class th extends Thread { public void run() { for (int i = 0; i < 10; i++) { System.out.println("zi " + i); } } }
zi 0
zi 1
zhu 0
zi 2
zhu 1
zi 3
zhu 2
zhu 3
zi 4
zhu 4
zi 5
zhu 5
zhu 6
zi 6
zhu 7
zi 7
zhu 8
zi 8
zhu 9
zi 9
实现接⼝ VS 继承 Thread
实现接⼝会更好⼀些,因为: Java 不⽀持多重继承,因此继承了 Thread 类就⽆法继承其它类,但是可以实现多个接⼝; 类可能只要求可执⾏就⾏,继承整个 Thread 类开销过⼤
实现接口可以 多线程共享同一个实例
线程池
https://www.zhihu.com/question/485071288
线程池其实是一种池化的技术的实现,池化技术的核心思想其实就是实现资源的一个复用,避免资源的重复创建和销毁带来的性能开销。在线程池中,线程池可以管理一堆线程,让线程执行完任务之后不会进行销毁,而是继续去处理其它线程已经提交的任务。
为什么要用线程池
- 降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。
- 提高响应速度。当任务到达时,任务可以不需要等到线程创建就能立即执行。
- 提高线程的可管理性。线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配,调优和监控
创建线程池
方式二:通过 Executor 框架的工具类 Executors 来实现
我们可以创建三种类型的 ThreadPoolExecutor:
《阿里巴巴 Java 开发手册》中强制线程池不允许使用 Executors 去创建,而是通过 ThreadPoolExecutor 的方式,这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险
线程池的构造其实很简单,就是传入一堆参数,然后进行简单的赋值操作。
https://blog.csdn.net/qq_38408785/article/details/91383959?spm=1001.2101.3001.6650.1&utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7ECTRLIST%7ERate-1-91383959-blog-122498816.pc_relevant_default&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7ECTRLIST%7ERate-1-91383959-blog-122498816.pc_relevant_default&utm_relevant_index=2
常见的4种线程池
1、newCachedThreadPool:
/**
* 一个可缓存线程池,如果线程池长度超过需要处理需要,可灵活回收空闲线程,若无可回收,则新建线程
* 适合短时间的任务
* 缺点:要问题是线程数最大数是Integer.MAX_VALUE,可能会创建数量非常多的线程,甚至OOM。
*/
2、newFixedThreadPool:
/**
* 定长线程池,可以控制线程最大并发数,超出的任务会在队列中等待
* 缺点:要问题是堆积的请求处理队列可能会耗费非常大的内存,甚至OOM。
*/
3、newScheduledThreadPool:
/**
* 定长的线程池,支持定时及周期性任务执行
* 缺点:问题是线程数最大数是Integer.MAX_VALUE,可能会创建数量非常多的线程,甚至OOM。
*/
4、newSingleThreadExecutor
/**
* 单线程的线程池,保证所有任务先进先出来执行
* 缺点:要问题是线程数最大数是Integer.MAX_VALUE,可能会创建数量非常多的线程,甚至OOM。
*/
简述线程池的状态
- Running:能接受新提交的任务,也可以处理阻塞队列的任务。
- Shutdown:不再接受新提交的任务,但可以处理存量任务,线程池处于running时调用shutdown方法,会进入该状态。
- Stop:不接受新任务,不处理存量任务,调用shutdownnow进入该状态。
- Tidying:所有任务已经终止了,worker_count(有效线程数)为0。
- Terminated:线程池彻底终止。在tidying模式下调用terminated方法会进入该状态。
线程池有哪些参数 7
corePoolSize 核心线程数
maximumPoolSize 最大线程数
keepAliveTime 空闲线程存活时间
unit 空闲线程存活时间单位
workQueue 工作队列
新任务被提交后,会先进入到此工作队列中,任务调度时再从队列中取出任务
threadFactory 线程工厂 创建一个新线程时使用的工厂,可以用来设定线程名、是否为daemon线程等等
handler 拒绝策略
当工作队列中的任务已到达最大限制,并且线程池中的线程数量也达到最大限制,这时如果有新任务提交进来,该如何处理呢。这里的拒绝策略,就是解决这个问题的
执行 execute()方法和 submit()方法的区别
execute执行流程
上图不准确
当前线程数目不小于核心线程数目 ,尝试放入阻塞队列
如果小于最大线程数,那么也会创建非核心线程来执行提交的任务,如图
从这里可以发现,就算队列中有任务,新创建的线程还是优先处理这个提交的任务,而不是从队列中获取已有的任务执行,从这可以看出,先提交的任务不一定先执行
jdk中提供了4中拒绝策略
①CallerRunsPolicy
该策略下,在调用者线程中直接执行被拒绝任务的run方法,除非线程池已经shutdown,则直接抛弃任务。
②AbortPolicy
该策略下,直接丢弃任务,并抛出RejectedExecutionException异常。
③DiscardPolicy
该策略下,直接丢弃任务,什么都不做。
④DiscardOldestPolicy
该策略下,抛弃进入队列最早的那个任务,然后尝试把这次拒绝的任务放入队列
线程池执行流程
饱和策略 就是拒绝策略
https://blog.csdn.net/jisuanji12306/article/details/86363390?spm=1001.2101.3001.6650.1&utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7ECTRLIST%7ERate-1-86363390-blog-114284804.pc_relevant_multi_platform_featuressortv2dupreplace&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7ECTRLIST%7ERate-1-86363390-blog-114284804.pc_relevant_multi_platform_featuressortv2dupreplace&utm_relevant_index=2
题目:有两个线程A、B,A线程向一个集合里面依次添加元素"abc"字符串,一共添加十次,当添加到第五次的时候,希望B线程能够收到A线程的通知,然后B线程执行相关的业务操作
import java.util.*; import java.util.concurrent.locks.LockSupport; public class Main { static volatile boolean notice = false; public static void main(String[] args) { List<String> list = new ArrayList<>(); Thread threadA = new Thread(() -> { for (int i = 1; i <= 10; i++) { list.add("aef"); System.out.println("线程A向list中添加一个元素,此时list中的元素个数为:" + list.size()); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } if (list.size() == 5) { notice = true; System.out.println("a " + notice); } } }); Thread threadB = new Thread(() -> { while (true) { // System.out.println("b " + notice); if (notice) { System.out.println("线程B收到通知,开始执行自己的业务..."); break; } } }); threadB.start(); threadA.start(); } }
import java.util.*; import java.util.concurrent.CountDownLatch; import java.util.concurrent.locks.LockSupport; public class Main { public static void main(String[] args) { CountDownLatch countDownLatch = new CountDownLatch(1); List<String> list = new ArrayList<>(); // 实现线程A Thread threadA = new Thread(() -> { for (int i = 1; i <= 10; i++) { list.add("abc"); System.out.println("线程A向list中添加一个元素,此时list中的元素个数为:" + list.size()); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } if (list.size() == 5) { countDownLatch.countDown(); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } }); Thread threadB = new Thread(() -> { try { countDownLatch.await(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("线程B收到通知,开始执行自己的业务..."); }); // 需要先启动线程B threadB.start(); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } // 再启动线程A threadA.start(); } }
import java.util.*; import java.util.concurrent.locks.LockSupport; public class Main { public static void main(String[] args) { List<String> list = new ArrayList<>(); // 实现线程B Thread threadB = new Thread(() -> { if (list.size() != 5) { LockSupport.park(); } System.out.println("线程B收到通知,开始执行自己的业务..."); }); // 实现线程A Thread threadA = new Thread(() -> { for (int i = 1; i <= 10; i++) { list.add("abc"); System.out.println("线程A向list中添加一个元素,此时list中的元素个数为:" + list.size()); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } if (list.size() == 5) LockSupport.unpark(threadB); } }); threadA.start(); threadB.start(); } }
Java并发编程-两个线程交替打印0-100的奇偶数
import java.util.*; import java.util.concurrent.Callable; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutionException; import java.util.concurrent.FutureTask; public class jiaoti { private static volatile int count = 0; private static final Object lock = new Object(); private static final int num = 101; static class TurningRunner implements Runnable { @Override public void run() { while (count < num) { synchronized (lock) { System.out.println(Thread.currentThread().getName() + ":" + count); count++; lock.notify(); // 唤醒另一个线程 if (count <= num) { try { lock.wait(); // 释放锁,进入阻塞状态,等待被唤醒 } catch (InterruptedException e) { System.out.println(e.getMessage()); } } } } } } public static void main(String[] args) throws InterruptedException { Thread th0 = new Thread(new TurningRunner(), "偶数"); Thread th1 = new Thread(new TurningRunner(), "奇数"); th0.start(); // 短暂休眠,确保线程执行顺序 Thread.sleep(50); th1.start(); while (true) { if (count >= num) { th0.interrupt(); th1.interrupt(); break; } } } }
public class jiaoti { private static int count = 0; private static final Object lock = new Object(); private static final int num = 101; static class TurningRunner implements Runnable { @Override public void run() { while (count < num) { synchronized (lock) { System.out.println(Thread.currentThread().getName() + ":" + count); count++; lock.notify(); // 唤醒另一个线程 if (count < num) { try { lock.wait(); // 释放锁,进入阻塞状态,等待被唤醒 } catch (InterruptedException e) { System.out.println(e.getMessage()); } } } } } } public static void main(String[] args) throws InterruptedException { Thread th0 = new Thread(new TurningRunner(), "偶数"); Thread th1 = new Thread(new TurningRunner(), "奇数"); th0.start(); // 短暂休眠,确保线程执行顺序 Thread.sleep(50); th1.start(); } }
给定两个数组,实现两个线程,交替循环打印
import java.util.*; import java.util.concurrent.Callable; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutionException; import java.util.concurrent.FutureTask; public class jiaoti { public static void main(String[] args) { char[] ai = "1234567".toCharArray(); char[] ac = "ABCDEFG".toCharArray(); Object obj = new Object(); Thread t1 = new Thread(() -> { synchronized (obj) { for (char c : ai) { System.out.print(c); try { obj.notify(); // 唤醒其它线程 obj.wait(); // 阻塞 t1 } catch (InterruptedException e) { e.printStackTrace(); } } obj.notify(); } }); Thread t2 = new Thread(() -> { synchronized (obj) { for (char c : ac) { System.out.print(c); try { obj.notify(); // 唤醒其它线程 obj.wait(); // 阻塞 t2 } catch (InterruptedException e) { e.printStackTrace(); } } obj.notify(); } }); t1.start(); t2.start(); } }