(个人笔记系列) 多线程知识
前言:
本系列是我自己的个人笔记总结,是根据我自己纸质版笔记的内容和学习课件归纳,可能做得不好,但希望能够帮到那些和我一样0基础刚入坑的小白们,同时也欢迎各位前辈予以指正和批评指导,谢谢。
多线程知识总结
1. 多线程技术概述
1.1 线程与进程
进程
- 是指一个内存中运行的应用程序,每个进程都有一个独立的内存空间
线程
-
是进程中的一个执行路径,共享一个内存空间,线程之间可以自由切换,并发执行. 一个进程最少 有一个线程
-
线程实际上是在进程基础之上的进一步划分,一个进程启动之后,里面的若干执行路径又可以划分 成若干个线程
1.2 线程调度
分时调度
- 所有线程轮流使用 CPU 的使用权,平均分配每个线程占用 CPU 的时间。
抢占式调度
-
优先让优先级高的线程使用 CPU,如果线程的优先级相同,那么会随机选择一个(线程随机性), Java使用的为抢占式调度。
-
CPU使用抢占式调度模式在多个线程间进行着高速的切换。对于CPU的一个核新而言,某个时刻, 只能执行一个线程,而 CPU的在多个线程间切换速度相对我们的感觉要快,看上去就是 在同一时 刻运行。 其实,多线程程序并不能提高程序的运行速度,但能够提高程序运行效率,让CPU的 使 用率更高。
1.3 同步与异步
同步
- 排队执行 , 效率低但是安全.
异步
- 同时执行 , 效率高但是数据不安全。
1.4 并发与并行
并发
- 指两个或多个事件在同一个时间段内发生。
并行
- 指两个或多个事件在同一时刻发生(同时发生)。
2. 实现多线程的常用方式
2.1 继承Thread
- 继承 java.lang.Thread
- 重写 run 方法
main方法
public static void main(String[] args) { MyThread m = new MyThread(); m.start(); for (int i = 0; i < 10; i++) { System.out.println("汗滴禾下土"+i); } }
继承Thread,重写run方法
public class MyThread extends Thread { //run方法就是线程要执行的任务方法 public void run() { //这里的代码就是一条新的执行路径 //这个执行路径是触发方式,不是调用run方法,而是通过thread对象的start方法来启动任务 for (int i = 0; i < 10; i++) { System.out.println("锄禾日当午"+i); } } }
效果图
程序执行的概念图
匿名调用的方式
new Thread(){ @Override public void run() { for (int i = 0; i < 10; i++) { System.out.println("12345"+i); } } }.start();
2.2 实现Runnable
- 继承 java.lang.Runnable
- 实现 run 方法
main方法
//实现runnable //1 创建一个任务对象 MyRunnable r = new MyRunnable(); //2.创建一个线程并给他一个任务 Thread t = new Thread(r); //3.启动线程 t.start(); for (int i = 0; i < 10; i++) { System.out.println("汗滴禾下土"+i); }
实现run方法
ublic void run() { //线程的任务 for (int i = 0; i < 10; i++) { System.out.println("锄禾日当午"+i); } }
效果图
实现Runnable与继承Thread相比的优势
实现Runnable与继承Thread相比有如下优势:
- 通过创建任务,然后给线程分配任务的方式实现多线程,更适合多个线程同时执行任务的情况
- 可以避免单继承所带来的局限性
- 任务与线程是分离的,提高了程序的健壮性
- 后期学习的线程池技术,接受Runnable类型的任务,不接受Thread类型的线程
3. Thread类
3.1 Thread类中的常用的方法
编号 | 方法 | 用途 |
---|---|---|
1 | start( ) | 启动当前线程;调用当前线程的run() |
2 | run( ) | 通常需要重写Thread类中的此方法,将创建的线程要执行的操作声明在此方法中 |
3 | current Thread() | 静态方法,返回执行当前代码的线程 |
4 | getName( ) | 获取当前线程的名字 |
5 | setName( ) | 设置当前线程的名字 |
6 | yield( ) | 释放当前cpu的执行权 |
7 | join( ) | 在线程a中调用线程b的join(),此时线程a就进入阻塞状态,直到线程b完全执行完以后,线程a才结束阻塞状态。 |
8 | stop( ) | 已过时。当执行此方法时,强制结束当前线程。 |
9 | sleep(long millitime) | 让当前线程“睡眠”指定的millitime毫秒。在指定的millitime毫秒时间内,当前线程是阻塞状态。 |
10 | isAlive( ) | isAlive():判断当前线程是否存活 |
3.2 线程的优先级
① 优先级常量
-
MAX_PRIORITY:10
-
MIN _PRIORITY:1
-
NORM_PRIORITY:5 -->默认优先级
② 如何获取和设置当前线程的优先级
- getPriority( ):获取线程的优先级
- setPriority(int p):设置线程的优先级
说明:高优先级的线程要抢占低优先级线程cpu的执行权。但是只是从概率上讲,高优先级的线程高概率的情况下被执行。并不意味着只当高优先级的线程执行完以后,低优先级的线程才执行。
3.3 线程通信
以下三个方法定义在Object类中的:
- wait()
- notify()
- notifyAll()
4.设置和获取线程名称
- currentThread( ):获取当前进行的线程
- setName( ):设置线程名称
- getName( ):获取线程名称
案例
public static void main(String[] args) { //如何获取线程的名称 System.out.println(Thread.currentThread().getName()); //两种设置线程名称的方式 Thread t = new Thread(new MyRunnable()); t.setName("wwww"); t.start(); new Thread(new MyRunnable(),"锄禾日当午").start(); //不设置的有默认的名字 new Thread(new MyRunnable()).start(); } static class MyRunnable implements Runnable{ @Override public void run() { System.out.println(Thread.currentThread().getName()); } }
5. 线程的休眠
方法:sleep( )
使用案例:每隔1秒执行一次(休眠间隔1秒=1000毫秒)
//线程的休眠 for (int i = 0; i < 10; i++) { System.out.println(i); Thread.sleep(1000); //1000毫秒 }
6. 线程阻塞
比如等待用户输入时,如果用户不输入则一直停留在用户输入处,直到用户输入数据才开始往下进行,这种情况称为线程阻塞,又称耗时操作。
7. 线程的中断
一个线程是一个独立的执行路径,他是否应该结束,应该由其自身决定
可以在需要关闭处打上标记,系统识别到标记时会报异常,进入try-catch,可以在try-catch决定程序的后续处理操作
**标记调用方法:**interrupt( );
案例
public static void main(String[] args) { //线程中断 //y一个线程是一个独立的执行路径,它是否结束应该由其自身决定 Thread t1 = new Thread(new MyRunnable()); t1.start(); for (int i = 0; i < 5; i++) { System.out.println(Thread.currentThread().getName()+":"+i); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } //给线程t1添加中断标记 t1.interrupt(); } static class MyRunnable implements Runnable{ @Override public void run() { for (int i = 0; i < 10; i++) { System.out.println(Thread.currentThread().getName()+":"+i); try { Thread.sleep(1000); } catch (InterruptedException e) { //e.printStackTrace(); System.out.println("发现了中断标记,线程自杀"); return; } } } }
8. 守护线程
线程:分为守护线程和用户线程
- 用户线程:当一个进程不包含任何的存活的用户线程时,进行结束
- 守护线程:守护用户线程的,当最后一个用户线程结束时,所有守护线程自动死亡。
**守护方法调用:**setDaemon( );
注意:守护线程要设置在线程启动之前
**案例:**当用户线程结束时,守护线程也跟着结束
public static void main(String[] args) { //线程分为守护线程和用户线程 //用户线程:当一个进程不包含任何的存活的用户线程时,进行结束 //守护线程:守护用户线程的,当最后一个用户线程结束时,所有守护线程自动死亡。 Thread t1 = new Thread(new MyRunnable()); //设置守护线程 t1.setDaemon(true); t1.start(); for (int i = 0; i < 5; i++) { System.out.println(Thread.currentThread().getName()+":"+i); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } static class MyRunnable implements Runnable{ @Override public void run() { for (int i = 0; i < 10; i++) { System.out.println(Thread.currentThread().getName()+":"+i); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } }
9. 线程安全问题
9.1 多线程不安全问题
多个线程同时操作一个变量时,存在不安全问题
案例:3个线程同时操作conut,导致正常逻辑余票为0时应该停止售票,而输出的count数值变为-2
(问题:3个线程抢占进入循环,在第一个进去还没执行到count自减操作前,第二个和第三个线程也可能已经入循环,而在一个线程执行完count自减操作,即count已为0,后续两个线程依旧会进行count自减操作。)
public static void main(String[] args) { //线程不安全 Runnable run = new Ticket(); new Thread(run).start(); new Thread(run).start(); new Thread(run).start(); } static class Ticket implements Runnable{ //总票数 private int count = 10; @Override public void run() { while (count>0){ //卖票 System.out.println("正在准备卖票"); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } count--; System.out.println("卖票结束,余票:"+count); } } }
9.2 线程安全问题解决方法1—同步代码块(隐式锁)
被同步代码块括住的内容会进行排队操作
格式:synchronized(锁对象){
}
注意:多线程应该用一把锁
案例
public static void main(String[] args) { Object o = new Object(); //线程不安全 //解决方案1 同步代码块 //格式:synchronized(锁对象){ // // // } Runnable run = new Ticket(); new Thread(run).start(); new Thread(run).start(); new Thread(run).start(); } static class Ticket implements Runnable{ //总票数 private int count = 10; private Object o = new Object(); @Override public void run() { //Object o = new Object(); //这里不是同一把锁,所以锁不住 while (true) { synchronized (o) { if (count > 0) { //卖票 System.out.println("正在准备卖票"); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } count--; System.out.println(Thread.currentThread().getName()+"卖票结束,余票:" + count); }else { break; } } } } }
9.3 线程安全问题解决方法2—同步方法(隐式锁)
格式:将synchronized修饰到方法中,在public后加修饰符synchronized
案例
public static void main(String[] args) { Object o = new Object(); //线程不安全 //解决方案2 同步方法 Runnable run = new Ticket(); new Thread(run).start(); new Thread(run).start(); new Thread(run).start(); } static class Ticket implements Runnable{ //总票数 private int count = 10; @Override public void run() { while (true) { boolean flag = sale(); if(!flag){ break; } } } public synchronized boolean sale(){ if (count > 0) { //卖票 System.out.println("正在准备卖票"); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } count--; System.out.println(Thread.currentThread().getName()+"卖票结束,余票:" + count); return true; } return false; } }
9.4 线程安全问题解决方法3—显式锁Lock
格式:Lock l = new ReentrantLock( );
方法:上锁lock( )
解锁unlock( )
public static void main(String[] args) { Object o = new Object(); //线程不安全 //解决方案1 显示锁 Lock 子类 ReentrantLock Runnable run = new Ticket(); new Thread(run).start(); new Thread(run).start(); new Thread(run).start(); } static class Ticket implements Runnable{ //总票数 private int count = 10; //参数为true表示公平锁 默认是false 不是公平锁 private Lock l = new ReentrantLock(true); @Override public void run() { while (true) { l.lock(); if (count > 0) { //卖票 System.out.println("正在准备卖票"); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } count--; System.out.println(Thread.currentThread().getName()+"卖票结束,余票:" + count); }else { break; } l.unlock(); } } }
9.5 显式锁Lock与隐式锁synchronized的区别
① 出身不同
synchronized:Java中的关键字,是由JVM来维护的。是JVM层面的锁。
Lock:是JDK5以后才出现的具体的类。使用lock是调用对应的API。是API层面的锁。
② 使用方式不同
所谓的显示和隐式就是在使用的时候,使用者要不要手动写代码去获取锁和释放锁的操作。
在使用synchronized关键字的时候,使用者根本不用写其他的代码,然后程序就能够获取锁和释放锁了。synchronized是由系统维护的,如果非逻辑问题的话,是不会出现死锁的。
在使用lock的时候,我们使用者需要手动的获取和释放锁。如果没有释放锁,就有可能导致出现死锁的现象。手动获取锁方法:lock.lock()。释放锁:unlock方法。需要配合tyr/finaly语句块来完成。
③ 等待是否可中断
synchronized是不可中断的。除非抛出异常或者正常运行完成
Lock可以中断的。中断方式:
1:调用设置超时方法tryLock(long timeout ,timeUnit unit)
2:调用lockInterruptibly()放到代码块中,然后调用interrupt()方法可以中断
④ 加锁的时候是否可以公平
synchronized;非公平锁
lock:两者都可以的。默认是非公平锁。在其构造方法的时候可以传入Boolean值。
true:公平锁
false:非公平锁
⑤ 锁绑定多个条件来condition
synchronized:没有。要么随机唤醒一个线程;要么是唤醒所有等待的线程。
Lock:用来实现分组唤醒需要唤醒的线程,可以精确的唤醒,而不是像sync那样,不能精确唤醒线程。
⑥ 从性能比较
synchronized是托管给JVM执行的,而Lock是java写的控制锁的代码。
⑦ 从使用锁的方式比较
synchronized原始采用的是CPU悲观锁机制,即线程获得的是独占锁。独占锁以为着其他线程只能依靠阻塞来等待线程释放锁。
而Lock用的是乐观锁方式。所谓乐观锁就是,每次不加锁而是假设没有冲突而去完成某项操作,如果因为冲突失败就重试,直到成功为止。
9.6 公平锁与非公平锁
上述3种方法都是非公平锁,只有Lock可以执行非公平锁
方法:在定义时追加true
//参数为true表示公平锁 默认是false 不是公平锁 private Lock l = new ReentrantLock(true);
9.7 线程死锁
方法两个有锁的方法互相调用时,会出现线程死锁的情况(个人理解:两个有锁方法在进行线程相互调用时,可能出现互相线程阻塞的情况,从而形成线程死锁)
死锁案例
public static void main(String[] args) { //线程死锁 Culprit c = new Culprit(); Police p = new Police(); new MyThread(c,p).start(); c.say(p); } static class MyThread extends Thread{ private Culprit c; private Police p; MyThread(Culprit c,Police p){ this.c = c; this.p = p; } @Override public void run() { p.say(c); } } static class Culprit{ public synchronized void say(Police p){ System.out.println("罪犯:你放了我,我放了人质"); p.fun(); } public synchronized void fun(){ System.out.println("罪犯被放了,罪犯也放了人质"); } } static class Police{ public synchronized void say(Culprit c){ System.out.println("警察:你放了人质,我放了你"); c.fun(); } public synchronized void fun(){ System.out.println("警察救了人质,但是罪犯跑了"); } }
10. 多线程通信问题
问题:有时候需要两个线程进行时,如果一个线程运行需要另一个线程提供条件,且另一个线程运行也需要一个线程提供条件(协同进行,相互)比如消费者和生产者,则可能发生多线程通信错误。
解决方法:使用睡眠和唤醒操作进行交替式运行(在特定位置使用条件打标记)
this.notifyAll(); // 唤醒其他线程 try { this.wait(); // 该线程进入睡眠状态 } catch (InterruptedException e) { e.printStackTrace(); }
案例:厨师做菜和服务员端菜
public static void main(String[] args) { //多线程通信 生产者与消费者问题 Food f = new Food(); new Cook(f).start(); new Waiter(f).start(); } //厨师 static class Cook extends Thread{ private Food f; public Cook(Food f) { this.f = f; } @Override public void run() { for (int i = 0; i < 100; i++) { if(i%2==0){ f.setNameAndTaste("老干妈小米粥","香辣味"); }else { f.setNameAndTaste("煎饼果子","甜辣味"); } } } } //服务员 static class Waiter extends Thread{ private Food f; public Waiter(Food f) { this.f = f; } @Override public void run() { for (int i = 0; i < 100; i++) { try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } f.get(); } } } //食物 static class Food{ private String name; private String taste; //true表示可以生产 boolean flag = true; public synchronized void setNameAndTaste(String name,String taste){ if(flag){ this.name = name; try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } this.taste = taste; flag = false; this.notifyAll(); try { this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } } public synchronized void get(){ if(!flag){ System.out.println("服务员端走的菜的名称是:"+name+",味道是:"+taste); flag = true; this.notifyAll(); try { this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } } }
11. 线程的六种状态
线程状态。线程可以处于以下状态之一:
NEW
尚未启动的线程处于此状态。RUNNABLE
在Java虚拟机中执行的线程处于此状态。BLOCKED
被阻塞等待监视器锁定的线程处于此状态。WAITING
无限期等待另一个线程执行特定操作的线程处于此状态。TIMED_WAITING
正在等待另一个线程执行最多指定等待时间的操作的线程处于此状态。TERMINATED
已退出的线程处于此状态。
注意:线程在给定时间点只能处于一种状态。 这些状态是虚拟机状态,不反映任何操作系统线程状态。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-S6adOmVl-1598706031607)(C:\Users\44298\AppData\Roaming\Typora\typora-user-images\image-20200829194359564.png)]
12. 带返回值的线程Callable
12.1 Runnable 与 Callable
接口定义 //Callable接口 public interface Callable<V> { V call() throws Exception; } //Runnable接口 public interface Runnable { public abstract void run(); }
12.2 Callable使用步骤
-
编写类实现Callable接口 , 实现call方法
class XXX implements Callable<T> { @Override public <T> call() throws Exception { return T; } }
-
创建FutureTask对象 , 并传入第一步编写的Callable类对象
FutureTask<Integer> future = new FutureTask<>(callable);
-
通过Thread,启动线程
new Thread(future).start();
12.3 Runnable 与 Callable的相同点与不同点
- 相同点:
-
都是接口
- 都可以编写多线程程序
- 都采用Thread.start()启动线程
- 不同点:
-
Runnable没有返回值;Callable可以返回执行结果
- Callable接口的call()允许抛出异常;Runnable的run()不能抛出
12.4 Callable获取返回值
Callalble接口支持返回执行结果,需要调用FutureTask.get()得到,此方法会阻塞主进程的继续往下执 行,如果不调用不会阻塞。
13. 线程池
13.1 线程池 Executors
如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了,这样频繁创建线程 就会大大降低 系统的效率,因为频繁创建线程和销毁线程需要时间. 线程池就是一个容纳多个线程的容 器,池中的线程可以反复使用,省去了频繁创建线程对象的操作,节省了大量的时间和资源。
线程池流程:
- 创建线程
- 创建任务
- 执行任务
- 关闭线程
13.2 线程池的好处
- 降低资源消耗。
- 提高响应速度。
- 提高线程的可管理性。
13.3 Java中的四种线程池 . ExecutorService
① 缓存线程池(长度无限制)
定义格式
ExecutorService service = Executors.newCachedThreadPool();
执行流程:
- 判断线程池是否存在空闲线程
- 存在则使用
- 不存在,则创建线程 并放入线程池, 然后使用
/** * 缓存线程池. * (长度无限制) * 执行流程: * 1. 判断线程池是否存在空闲线程 * 2. 存在则使用 * 3. 不存在,则创建线程 并放入线程池, 然后使用 */ ExecutorService service = Executors.newCachedThreadPool();//向线程池中 加入 新的任务 service.execute(new Runnable() { @Override public void run() { System.out.println("线程的名称:"+Thread.currentThread().getName()); } }); service.execute(new Runnable() { @Override public void run() { System.out.println("线程的名称:"+Thread.currentThread().getName()); } }); service.execute(new Runnable() { @Override public void run() { System.out.println("线程的名称:"+Thread.currentThread().getName()); } });
案例:
public static void main(String[] args) { //缓存线程池 //无限制长度 //任务加入后的执行流程 //1判断线程池是否存在空闲线程 2存在则使用 3不存在则创建线程并使用 //向线程池中加入新的任务 ExecutorService service = Executors.newCachedThreadPool();//指挥线程池执行新的任务 service.execute(new Runnable() { @Override public void run() { System.out.println(Thread.currentThread().getName()+"锄禾日当午"); } }); service.execute(new Runnable() { @Override public void run() { System.out.println(Thread.currentThread().getName()+"锄禾日当午"); } });service.execute(new Runnable() { @Override public void run() { System.out.println(Thread.currentThread().getName()+"锄禾日当午"); } }); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } }
② 定长线程池(长度是指定数值)
定义格式
ExecutorService service = Executors.newFixedThreadPool(定义长度);
执行流程:
-
判断线程池是否存在空闲线程
-
存在则使用
-
不存在空闲线程,且线程池未满的情况下,则创建线程 并放入线程池, 然后使用
-
不存在空闲线程,且线程池已满的情况下,则等待线程池存在空闲线程
/** * 定长线程池. * (长度是指定的数值) * 执行流程: * 1. 判断线程池是否存在空闲线程 * 2. 存在则使用 * 3. 不存在空闲线程,且线程池未满的情况下,则创建线程 并放入线程池, 然后使用 * 4. 不存在空闲线程,且线程池已满的情况下,则等待线程池存在空闲线程 */ ExecutorService service = Executors.newFixedThreadPool(2); service.execute(new Runnable() { @Override public void run() { System.out.println("线程的名称:"+Thread.currentThread().getName()); } }); service.execute(new Runnable() { @Override public void run() { System.out.println("线程的名称:"+Thread.currentThread().getName()); } });
案例:
/*定长线程池 长度是指定的线程池 加入任务后的执行流程 1 判断线程池是否存在空闲线程 2 存在则使用 3 不存在空闲线程 且线程池未满的情况下 则创建线程 并放入线程池中 然后使用 4 不存在空闲线程 且线程池已满的情况下 则等待线程池的空闲线程 **/ public static void main(String[] args) { ExecutorService service = Executors.newFixedThreadPool(2); service.execute(new Runnable() { @Override public void run() { System.out.println(Thread.currentThread().getName()+"锄禾日当午"); try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } } }); service.execute(new Runnable() { @Override public void run() { System.out.println(Thread.currentThread().getName()+"锄禾日当午"); try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } } }); service.execute(new Runnable() { @Override public void run() { System.out.println(Thread.currentThread().getName()+"锄禾日当午"); } }); }
③ 单线程线程池
定义格式
ExecutorService service = Executors.newSingleThreadExecutor();
执行流程:
-
判断线程池 的那个线程 是否空闲
-
空闲则使用
-
不空闲,则等待 池中的单个线程空闲后 使用
效果与定长线程池 创建时传入数值1 效果一致. /** * 单线程线程池. * 执行流程: * 1. 判断线程池 的那个线程 是否空闲 * 2. 空闲则使用 * 3. 不空闲,则等待 池中的单个线程空闲后 使用 */ ExecutorService service = Executors.newSingleThreadExecutor();service.execute(new Runnable() { @Override public void run() { System.out.println("线程的名称:"+Thread.currentThread().getName()); } }); service.execute(new Runnable() { @Override public void run() { System.out.println("线程的名称:"+Thread.currentThread().getName()); } });
案例:
/*单线程线程池 执行流程 1 判断线程池的那个线程是否空闲 2 空闲则使用 3 不空闲则等待它空闲后再使用 **/ public static void main(String[] args) { ExecutorService service = Executors.newSingleThreadExecutor();service.execute(new Runnable() { @Override public void run() { System.out.println(Thread.currentThread().getName()+"锄禾日当午"); } }); service.execute(new Runnable() { @Override public void run() { System.out.println(Thread.currentThread().getName()+"锄禾日当午"); } }); service.execute(new Runnable() { @Override public void run() { System.out.println(Thread.currentThread().getName()+"锄禾日当午"); } }); }
④ 周期定长线程池
定义格式
ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(2);
执行流程:
- 判断线程池是否存在空闲线程
- 存在则使用
- 不存在空闲线程,且线程池未满的情况下,则创建线程 并放入线程池, 然后使用
- 不存在空闲线程,且线程池已满的情况下,则等待线程池存在空闲线程
周期性任务执行时:
- 定时执行, 当某个时机触发时, 自动执行某任务 .
public static void main(String[] args) { /** * 周期任务 定长线程池. * 执行流程: * 1. 判断线程池是否存在空闲线程 * 2. 存在则使用 * 3. 不存在空闲线程,且线程池未满的情况下,则创建线程 并放入线程池, 然后使用 * 4. 不存在空闲线程,且线程池已满的情况下,则等待线程池存在空闲线程 * * 周期性任务执行时: * 定时执行, 当某个时机触发时, 自动执行某任务 . */ ScheduledExecutorService service = Executors.newScheduledThreadPool(2); /** * 定时执行 * 参数1. runnable类型的任务 * 参数2. 时长数字 * 参数3. 时长数字的单位 */ /*service.schedule(new Runnable() { @Override public void run() { System.out.println("俩人相视一笑~ 嘿嘿嘿"); } },5,TimeUnit.SECONDS); */ /** * 周期执行 * 参数1. runnable类型的任务 * 参数2. 时长数字(延迟执行的时长) * 参数3. 周期时长(每次执行的间隔时间) * 参数4. 时长数字的单位 */ service.scheduleAtFixedRate(new Runnable() { @Override public void run() { System.out.println("俩人相视一笑~ 嘿嘿嘿"); } },5,2,TimeUnit.SECONDS); }
案例:
/*周期任务 定长线程池 执行流程 1 判断线程池是否存在空闲线程 2 存在则使用 3 不存在空闲线程 且线程池未满的情况下 则创建线程 并放入线程池中 然后使用 4 不存在空闲线程 且线程池已满的情况下 则等待线程池的空闲线程 周期性任务执行时 定时执行 当某个任务触发时 自动执行某任务 **/ public static void main(String[] args) { ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(2);//定时执行一次 //参数1:定时执行的任务 //参数2:时长数字 //参数3:2的时间单位 Timeunit的常量指定 /* scheduledExecutorService.schedule(new Runnable() { @Override public void run() { System.out.println(Thread.currentThread().getName()+"锄禾日当午"); } },5, TimeUnit.SECONDS); //5秒钟后执行*/ /* 周期性执行任务 参数1:任务 参数2:延迟时长数字(第一次在执行上面时间以后) 参数3:周期时长数字(没隔多久执行一次) 参数4:时长数字的单位 * **/ scheduledExecutorService.scheduleAtFixedRate(new Runnable() { @Override public void run() { System.out.println(Thread.currentThread().getName()+"锄禾日当午"); } },5,1,TimeUnit.SECONDS); } }
14. Lambda表达式
函数式编程思想
面向对象:创建对象调用方法 解决问题
案例:
/* lambda表达式 函数式编程思想 **/ public static void main(String[] args) { //冗余的Runnable编写方式 /* Thread t = new Thread(new Runnable() { @Override public void run() { System.out.println("锄禾日当午"); } }); t.start();*/ Thread t = new Thread(() -> System.out.println("锄禾日当午")); t.start(); }
- 个人学习笔记之Redis系列~Redis进阶(一):PHP集群中使用redis实现SESSION共享
- Java基础知识强化之多线程笔记04:并行和并发 区别
- Java基础知识强化之多线程笔记07:同步、异步、阻塞式、非阻塞式 的联系与区别
- 深度学习笔记 Day3 python基础知识系列(仅本人自用)
- spring--常用小知识(个人笔记)
- 多线程系列之 Java多线程的个人理解(一)
- 个人知识管理(PKM)全民推广系列
- Deep Learning(深度学习)学习笔记整理系列之LeNet-5卷积参数个人理解
- Java学习笔记【多线程基本知识】
- 深度学习笔记 Day2 python基础知识系列
- 自学Java系列 笔记4 多线程 1
- java个人学习笔记18(多线程之间通信+等待唤醒机制)
- 专业个人知识管理,笔记软件或在线系统搜集
- 【我的ASP.NET学习笔记】个人博客系统-Request.Params["cid"]--知识
- linux下php安装扩展组件(个人工作笔记系列之php)
- Java基础知识强化之网络编程笔记05:UDP之多线程实现聊天室案例
- 多线程学习系列:(二)线程基本知识
- Java多线程编程总结笔记——一多线程基础知识
- iOS开发之多线程-- NSOperation使用简介(个人笔记)
- C# 基础知识系列- 12 任务和多线程