文章目录
一、线程与进程
1.进程
- 是指一个内存中运行的应用程序,每个进程都有一个独立的内存空间。
2.线程
- 是进程中的一个执行路径,共享一个内存空间,线程之间可以自由切换,并发执行。一个进程最少有一个线程。
- 线程实际上是在进程基础之上的进一步划分,一个进程启动之后,里面的若干执行路径又可以划分成若干个线程。
一个实例:
3.线程调度
分时调度
- 所有线程轮流使用 CPU 的使用权,平均分配每个线程占用 CPU 的时间。
抢占式调度
- 优先让优先级高的线程使用 CPU,如果线程的优先级相同,那么会随机选择一个(线程随机性),Java使用的为抢占式调度。
- CPU使用抢占式调度模式在多个线程间进行着高速的切换。对于CPU的一个核新而言,某个时刻,只能执行一个线程,而 CPU的在多个线程间切换速度相对我们的感觉要快,看上去就是 在同一时刻运行。 其实,多线程程序并不能提高程序的运行速度,但能够提高程序运行效率,让CPU的使用率更高。
二、同步与异步&并发与并行
1.同步与异步
同步:排队执行 , 效率低但是安全。
异步:同时执行 , 效率高但是数据不安全。
2.并发与并行
并发:指两个或多个事件在同一个时间段内发生。
并行:指两个或多个事件在同一时刻发生(同时发生)。
代码如下(示例):
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
import warnings
warnings.filterwarnings('ignore')
import ssl
ssl._create_default_https_context = ssl._create_unverified_context
代码如下(示例):
data = pd.read_csv(
'https://labfile.oss.aliyuncs.com/courses/1283/adult.data.csv')
print(data.head())
该处使用的url网络请求的数据。
三、继承Thread
package thread;
public class Demo1 {
public static void main(String[] args) {
MyThread m = new MyThread();
m.start();
for (int i = 0; i < 10; i++) {
System.out.println("汗滴禾下土"+i);
}
}
}
package thread;
public class MyThread extends Thread {
//run方法就是线程要执行的任务方法
@Override
public void run() {
//这里的代码就是一条新的执行路径
//这个执行路径是触发方式,不是调用run方法,而是通过thread对象的start方法来启动任务
for (int i = 0; i < 10; i++) {
System.out.println("锄禾日当午"+i);
}
}
}
四、实现Runnable
实现Runnable
package thread;
/*
实现Runnable与继承Thread相比有如下优势
1.通过创建任务,然后给线程分配任务的方式实现多线程,更适合多个线程同时执行任务的情况
2,可以避免单继承所带来的局限性
3,任务与线程是分离的,提高了程序的健壮性
4,后期学习的线程池技术,接受Runnable类型的任务,不接受Thread类型的线程
**/
public class Demo1 {
public static void main(String[] args) {
//实现runnable
//1 创建一个任务对象
MyRunnable r = new MyRunnable();
//创建一个线程并给他一个任务
Thread t = new Thread(r);
//启动线程
t.start();
for (int i = 0; i < 10; i++) {
System.out.println("汗滴禾下土"+i);
}
}
}
package thread;
public class MyRunnable implements Runnable{
@Override
public void run() {
//线程的任务
for (int i = 0; i < 10; i++) {
System.out.println("锄禾日当午"+i);
}
}
}
用匿名内部类实现Runnable
package thread;
public class Demo2 {
public static void main(String[] args) {
new Thread(){
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println("12345"+i);
}
}
}.start();
for (int i = 0; i < 10; i++) {
System.out.println("汗滴禾下土"+i);
}
}
}
五、Thread类
常用Thread类
构造方法
构造器 | 概述 |
---|---|
Thread() | 分配新的 Thread对象。 |
Thread(Runnable target) | 分配新的 Thread对象。 |
Thread(Runnable target, String name) | 分配新的 Thread对象。 |
Thread(String name) | 分配新的 Thread对象。 |
字段
变量和类型 | 字段 | 描述 |
---|---|---|
static int | MAX_PRIORITY | 线程可以拥有的最大优先级。 |
static int | MIN_PRIORITY | 线程可以拥有的最低优先级。 |
static int | NORM_PRIORITY | 分配给线程的默认优先级。 |
常用方法摘要
变量和类型 | 方法 | 描述 |
---|---|---|
long | getId() | 返回此Thread的标识符。 |
String | getName() | 返回此线程的名称。 |
int | getPriority() | 返回此线程的优先级。 |
void | setPriority(int newPriority) | 更改此线程的优先级。 |
void | start() | 导致此线程开始执行; Java虚拟机调用此线程的run方法。 |
void | stop() | 已过时。 这种方法本质上是不安全的。 |
static void | sleep(long millis) | 导致当前正在执行的线程休眠(暂时停止执行)指定的毫秒数,具体取决于系统计时器和调度程序的精度和准确性。 |
static void | sleep(long millis, int nanos) | 导致当前正在执行的线程休眠(暂时停止执行)指定的毫秒数加上指定的纳秒数,具体取决于系统定时器和调度程序的精度和准确性。 |
void | setDaemon(boolean on) | 将此线程标记为 daemon线程或用户线程。 |
六、设置和获取线程名称
package thread;
public class Demo3 {
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());
}
}
}
七、线程休眠sleep
package thread;
public class Demo4 {
public static void main(String[] args) throws InterruptedException {
//线程的休眠
for (int i = 0; i < 10; i++) {
System.out.println(i);
Thread.sleep(1000); //1000毫秒
}
}
}
八、线程阻塞
什么是线程阻塞?
在某一时刻某一个线程在运行一段代码的时候,这时候另一个线程也需要运行,但是在运行过程中的那个线程执行完成之前,另一个线程是无法获取到CPU执行权的(调用sleep方法是进入到睡眠暂停状态,但是CPU执行权并没有交出去,而调用wait方法则是将CPU执行权交给另一个线程),这个时候就会造成线程阻塞。
为什么会出现线程阻塞?
1.睡眠状态:当一个线程执行代码的时候调用了sleep方法后,线程处于睡眠状态,需要设置一个睡眠时间,此时有其他线程需要执行时就会造成线程阻塞,而且sleep方法被调用之后,线程不会释放锁对象,也就是说锁还在该线程手里,CPU执行权还在自己手里,等睡眠时间一过,该线程就会进入就绪状态,典型的“占着茅坑不拉屎”;
2.等待状态:当一个线程正在运行时,调用了wait方法,此时该线程需要交出CPU执行权,也就是将锁释放出去,交给另一个线程,该线程进入等待状态,但与睡眠状态不一样的是,进入等待状态的线程不需要设置睡眠时间,但是需要执行notify方法或者notifyall方法来对其唤醒,自己是不会主动醒来的,等被唤醒之后,该线程也会进入就绪状态,但是进入仅需状态的该线程手里是没有执行权的,也就是没有锁,而睡眠状态的线程一旦苏醒,进入就绪状态时是自己还拿着锁的。等待状态的线程苏醒后,就是典型的“物是人非,大权旁落“;
3.礼让状态:当一个线程正在运行时,调用了yield方法之后,该线程会将执行权礼让给同等级的线程或者比它高一级的线程优先执行,此时该线程有可能只执行了一部分而此时把执行权礼让给了其他线程,这个时候也会进入阻塞状态,但是该线程会随时可能又被分配到执行权,这就很”中国化的线程“了,比较讲究谦让;
4.自闭状态:当一个线程正在运行时,调用了一个join方法,此时该线程会进入阻塞状态,另一个线程会运行,直到运行结束后,原线程才会进入就绪状态。这个比较像是”走后门“,本来该先把你的事情解决完了再解决后边的人的事情,但是这时候有走后门的人,那就会停止给你解决,而优先把走后门的人事情解决了;
5.suspend() 和 resume() :这两个方法是配套使用的,suspend() 是让线程进入阻塞状态,它的解药就是resume(),没有resume()它自己是不会恢复的,由于这种比较容易出现死锁现象,所以jdk1.5之后就已经被废除了,这对就是相爱相杀的一对。
九、线程的中断
package thread;
public class Demo5 {
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;
}
}
}
}
}
十、守护线程
改造一下线程中断的代码:
package thread;
public class Demo6 {
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();
}
}
}
}
}
十一、线程安全问题
package thread;
public class Demo7 {
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);
}
}
}
}
当代码运行完毕,余票数会出现-2的情况,导致线程不安全的问题。
1.线程安全1-同步代码块
package thread;
//线程同步synchronized
public class Demo8 {
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;
}
}
}
}
}
}
2.线程安全2-同步方法
package thread;
//线程同步synchronized
public class Demo9 {
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;
}
}
}
3.线程安全3-显式锁Lock
package thread;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
//同步代码块和同步方法都属于隐式锁
//线程同步lock
public class Demo10 {
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();
}
}
}
}
显式锁和隐式锁
在多线程的程序中,JAVA为了具体的控制每个线程的,所以有了锁机制,锁又被分为显式锁和隐式锁。
隐式锁(Synchronized)
隐式锁中又分为同步代码块和同步方法,但是都是基于Synchronized关键字来实现的,因为他只需要是使用管关键字就可以,不用显示加锁和解锁的过程,所以称之为隐式锁,具体的实现可以参考下面的代码:
同步代码块:
public void method(){
synchronized(this){
//要锁住的代码
//一次只能有一个线程来指向这段代码
}
}
1.上面的代码是同步代码块,就是在代码块前面加上Synchroized关键字,并在后面的小括号内传入锁对象,这里的this是指定了当前的这个对象作为锁,注意任何对象都是可以作为锁来使用的。
2.这个是排队执行,一个线程进入代码块时,就会把这个锁对象的状态变成上锁状态,其他的线程发现是上锁状态时,就会在外面等待,直到这个线程将锁住的代码块执行完毕之后,将锁对象的状态变为解锁,其他的线程才会一起抢,谁抢到就重复上面的操作,知道满足某个条件,程序结束。
同步方法:
public synchronized void method(){
//方法体
}
1.同步方法就是将Synchroized关键字方放在方法的返回值前面,这里的锁就是也就是this,谁调用它,谁就是他的锁。
2.原理跟同步代码块一样,但是这里的锁因为是this,如果在一个线程对象里有多个方法,那么第一个线程执行时给这个this上了锁,那么其他方法都是不能执行的,只有等一个方法执行完了之后,改变锁的状态,其他的才可以执行。
显式锁(Lock)
显式锁的概念是相对于隐式锁的,因为显式锁在使用的时候程序员可以控制上锁和解锁,所以称之为显式。显式锁不能直接使用,所以要使用它的实现类来进行操作。
public class Demo03 implements Runnable {
Lock l = new ReentrantLock();
@Override
public void run() {
l.lock();
try{
//要锁住的代码
}catch(Exception e){
//异常的处理逻辑
}finally{
l.unlock();
}
}
}
我们首先要在线程类里面定义一个锁对象,然后通过他的lock()方法来上锁,锁住的东西就是直到调用它unlock()方法开锁之前的所有代码,显式锁如果上锁,一定要记得在最后开锁,建议写在finally代码块里面,还要注意只有上了锁才能开锁,不上锁开锁系统会报异常。
显式锁和隐式锁的区别
1.Synchronized是内部实现的,不需要人为控制,jvm会在内部自动改变锁的状态,而Lock是需要上锁和解锁的,而且只上锁不解锁或者只解锁不上锁都是不可以的。
2.Synchronized是针对代码块来上锁的,就是如果代码运行到同步代码块或者同步方法外面,锁就会自动解开。
3.而Lock是针对于一个对象,不论你有多少的代码,只要上锁之后没有用unlock解锁,这个东西都是被锁住的,而且还会有异常,程序员还要对异常进行处理,这个虽然麻烦,但是他也提供了一个便利,就是开锁和解锁不必在同一个代码块里面,就增加了可操作性。
4.因为隐式锁是jvm在检测和处理,所以在一般情况下还是隐式锁比较好用。
十二、公平锁与非公平锁
简单地说,公平锁就是保障了多线程下各线程获取锁的顺序,先到的线程优先获取锁,而非公平锁则无法提供这个保障。
在显式锁里面fair参数为true为公平锁。
十三、线程死锁
想象一个警察和罪犯对峙的场景:警察说“你先放了人质,我放了你”,但是罪犯想的是“你先放了我,我才放了人质”,双方僵持不下,这就造成死锁。
package thread;
public class Demo11 {
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("警察救了人质,但是罪犯跑了");
}
}
}
方法摘要:
变量和类型 | 方法 | 描述 |
---|---|---|
void | notify() | 唤醒正在此对象监视器上等待的单个线程。 |
void | notifyAll() | 唤醒等待此对象监视器的所有线程。 |
void | wait() | 导致当前线程等待它被唤醒,通常是 通知或 中断 。 |
void | wait(long timeoutMillis) | 导致当前线程等待它被唤醒,通常是 通知或 中断 ,或者直到经过一定量的实时。 |
void | wait(long timeoutMillis, int nanos) | 导致当前线程等待它被唤醒,通常是 通知或 中断 ,或者直到经过一定量的实时。 |
十四、生产者与消费者
package thread;
public class Demo12 {
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();
}
}
}
}
}
十五、线程的六种状态
线程可以处于以下状态之一:
- NEW
尚未启动的线程处于此状态。 - RUNNABLE
在Java虚拟机中执行的线程处于此状态。 - BLOCKED
被阻塞等待监视器锁定的线程处于此状态。 - WAITING
无限期等待另一个线程执行特定操作的线程处于此状态。 - TIMED_WAITING
正在等待另一个线程执行最多指定等待时间的操作的线程处于此状态。 - TERMINATED
已退出的线程处于此状态。
从创建开始,在运行的过程中,纵使在运行(Runnable)的过程中会经历各种状态(Blocked、Waiting、TimedWaiting),但是终将会走向结束(Terminated)。
十六、带返回值的线程Callable
Runnable 与 Callable:
Callable使用步骤:
Runnable 与 Callable的相同点:
- 都是接口
- 都可以编写多线程程序
- 都采用Thread.start()启动线程
Runnable 与 Callable的不同点:
-
Runnable没有返回值;Callable可以返回执行结果
-
Callable接口的call()允许抛出异常;Runnable的run()不能抛出
Callable获取返回值:
Callalble接口支持返回执行结果,需要调用FutureTask.get()得到,此方法会阻塞主进程的继续往下执行,如果不调用不会阻塞。
十七、线程池概述
如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了,这样频繁创建线程 就会大大降低 系统的效率,因为频繁创建线程和销毁线程需要时间. 线程池就是一个容纳多个线程的容 器,池中的线程可以反复使用,省去了频繁创建线程对象的操作,节省了大量的时间和资源。
线程池的好处
- 降低资源消耗。
- 提高响应速度。
- 提高线程的可管理性。
1.缓存线程池
package thread;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Demo13 {
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();
}
}
}
2.定长线程池
package thread;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Demo14 {
/*定长线程池
长度是指定的线程池
加入任务后的执行流程
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()+"锄禾日当午");
}
});
}
}
3.单线程线程池
package thread;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Demo15 {
/*单线程线程池
执行流程
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()+"锄禾日当午");
}
});
}
}
4.周期定长线程池
package thread;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class Demo16 {
/*周期任务 定长线程池
执行流程
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);
}
}
十八、Lambda表达式
package thread;
public class Demo17 {
/*
lambda表达式
函数式编程思想
**/
public static void main(String[] args) {
//冗余的Runnable编写方式
/* Thread t = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("锄禾日当午");
}
});
t.start();*/
//用lambda表达式编写
Thread t = new Thread(() -> System.out.println("锄禾日当午"));
t.start();
}
}