Java

线程

单线程

  • 线程(thread)是一个程序内部的一条执行路径。
  • main方法的执行其实就是一个单独的执行路径
  • 程序中如果只有一条执行路径,那么这个程序就是单线程的程序

多线程

  • 多线程是指从软硬件上实现多条执行流程的技术

学习内容

多线程的创建 Thread类的常用方法 线程安全、线程同步 线程通信、线程池 定时器、线程状态等

多线程的创建

方式一:继承Thread类
  • Java是通过java.lang.Thread类来代表线程的
  • 按照面向对象的思想,Thread类应该提供了实现多线程的方式
多线程的实现方式一:继承Thread类
  1. 定义应该子类MyThread继承线程类java.lang.Thread,重写run()方法

  2. 创建MyThread类的对象

  3. 调用线程对象的Start()方法启动线程(启动后还是执行run方法)

方式一优缺点
  • 优点
    • 编码简单
  • 缺点
    • 线程类已经继承Thread,无法继承其他类,不利于扩展。
package com.yu.Day1009Demo;

public class ThreadDemo01 {
    public static void main(String[] args) {
        //3.new一个新线程对象
        Thread t = new MyThread();
        //4.调用start方法启动线程(执行的还是run方法)
        t.start();
        for (int i = 0; i <5 ; i++) {
            System.out.println("主线程正在执行"+i);
    }
    }
    //1.定义一个线程类继承Thread类
    static class MyThread extends Thread{
        //2.重写run方法,里面定义子线程该干啥
        @Override
        public void run() {
            for (int i = 0; i <5 ; i++) {
                System.out.println("子线程正在执行"+i);
            }
        }
    }
}

执行结果

主线程正在执行0
主线程正在执行1
主线程正在执行2
主线程正在执行3
子线程正在执行0
子线程正在执行1
子线程正在执行2
主线程正在执行4
子线程正在执行3
子线程正在执行4
注意事项
  1. 为什么不直接调用run方法,而是调用start启动线程
    • 直接调用run方法会当成普通方法执行,此时想读与还是单线程执行
    • 只有调用start方法才是启动一个新的线程执行
  2. 把主线程任务放在子线程之前了
    • 这样主线程一直是先跑完的,相当于一个单线程的效果了
方式二:实现Runnable接口
多线程的实现方式二:实现Runnable接口
  1. 定义一个线程任务类MyRunnable实现Runnable接口,重写run()方法
  2. 创建MyRunnable任务对象
  3. 把MyRunnable任务对象交给Thread处理
  4. 调用线程对象的start()方法启动线程
Thread的构造器
构造器 说明
public Thread(String name) 可以为当前线程指定名称
public Thread(Runnable target) 封装Runnable对象成为线程对象
public Thread(Runnable target,String name) 封装Runnable对象成为线程对象,并指定线程名称
方式二优缺点
  • 优点
    • 线程任务类只是实现接口,可以继续继承类和实现接口,扩展性强
  • 缺点
    • 编程多一层对象包装,如果线程有执行结果是不可以直接返回的
package com.yu.Day1009Demo;

public class ThreadDemo02 {
    public static void main(String[] args) {
        //3.创建MyRunnable任务对象
        Runnable thread = new MyRunnable();
        //4.把MyRunnable任务对象交给Thread处理
        Thread t = new Thread(thread);
        //5.调用start方法启动子线程
        t.start();
        for (int i = 0; i <5 ; i++) {
            System.out.println("主线程正在执行"+i);
        }

    }
    //1.定义一个线程任务类MyRunnable实现Runnable接口
     static class MyRunnable implements java.lang.Runnable{
        //2.重写run()方法
         @Override
         public void run() {
             for (int i = 0; i <5 ; i++) {
                 System.out.println("子线程正在执行"+i);
             }
             }
         }
     
}
多线程的实现方式二:实现Runnable接口(匿名内部类形式)
  1. 可以创建Runnable的匿名内部类对象

  2. 交给Thread处理

  3. 调用线程对象的start()启动线程

package com.yu.Day1009Demo;

public class ThreadDemo03 {
    public static void main(String[] args) {
        Runnable target = new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i <5 ; i++) {
                    System.out.println("子线程正在执行"+i);
                }
            }
        };
        Thread t = new Thread(target);
        t.start();

        new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i <5 ; i++) {
                    System.out.println("2子线程正在执行"+i);
                }
            }
        }).start();

        new Thread(() ->{
                for (int i = 0; i <5 ; i++) {
                    System.out.println("3子线程正在执行"+i);
                }
        }).start();


        for (int i = 0; i <5 ; i++) {
            System.out.println("主线程正在执行"+i);
        }
    }
}

方式三:JDK5.0新增:实现Callable接口
  1. 得到任务对象
    1. 定义类实现Callable接口,重写call方法,封装要做的事情
    2. 用FutureTask把Callable对象封装成线程任务对象
  2. 把线程任务对象交给Thread处理
  3. 调用Thread的start方法启动线程,执行任务
  4. 线程执行完毕后,通过FutureTask的get方法去获取任务执行的结果
FutureTask的API
方法名称 说明
public FutureTask<>(Callable call) 把Callable对象封装成FutureTask对象
public V get() throws Exception 获取线程执行call方法返回的结果
优缺点
  • 优点

    • 线程任务类只是实现接口,可以继续继承类和实现接口,扩展性强
    • 可以在线程执行完毕后获取线程执行的结果
  • 缺点

    • 编码复杂一点
package com.yu.Day1009Demo;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

public class ThreadDemo04 {
    public static void main(String[] args) {
        //3.封装Callable任务对象
        Callable<String> call = new MyCallable(100);
        //4.把Callable任务对象,交给FutureTask对象
        //Future对象的作用1:是Runnable的对象(实现了Runnable接口),可以交给Thread
        //Future对象的作用2:可以在线程执行完毕之后通过掉用其get方法得到线程执行完毕后的结果
        FutureTask<String> ft = new FutureTask<>(call);
        //5.交给线程处理
        Thread t = new Thread(ft);
        //6.启动线程
        t.start();

        Callable<String> call2 = new MyCallable(200);
        FutureTask<String> ft2 = new FutureTask<>(call2);
        Thread t2 = new Thread(ft2);
        t2.start();

        try {
            System.out.println(ft.get());
        } catch (Exception e) {
            e.printStackTrace();
        }

        try {
            System.out.println(ft2.get());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    //1.定义一个任务类,实现Callable接口,应该申明线程任务执行完毕后的结果的数据类型
    static class MyCallable implements Callable<String>{
        private int n;

        public MyCallable(int n) {
            this.n = n;
        }
        //2.重写call方法(任务方法)

        @Override
        public String call() throws Exception {
            int sum = 0;
            for (int i = 0; i < n; i++) {
                sum+=i;
            }
            return "子线程执行的结果是"+sum;
        }
    }
}
三种方式对比
方式 优点 缺点
继承Thread类 编程比较简单,可以直接使用Thread类中的方法 扩展性较差,不能在继承其他的类,不能返回线程执行的结果
实现Runnabl接口 扩展性强,实现该接口的同时还可以继承其他的类 编程相对复杂,不能返回线程执行的结果
实现Callable接口 扩展性强,实现该接口的同时还可以继承其他的类,可以得到线程执行的结果 编程相对复杂

Thread的常用方法

Thread获取和设置线程名称
方法名称 说明
String getName() 获取当前线程的名称,默认线程名称是Thread-索引
void setName(String name) 将此线程的名称更改为指定的名称,通过构造器也可以设置线程名称
Thread类获取当前线程对象
方法名称 说明
public static Thread currentThread(); 返回当前正在执行的线程对象的引用

注意

  • 此方法是Thread类的静态方法,可以直接使用Thread类调用
  • 这个方法是在哪个线程执行中调用的,就会得到哪个线程对象
Thread的构造器
方法名称 说明
public Thread(String name) 可以为当前线程指定名称
public Thread(Runnable target) 封装Runnable对象成为线程对象
public Thread(Runnable target,String name) 封装Runnable对象成为线程对象,并指定线程名称
Thread类的线程休眠方法
方法名称 说明
public static void sleep(long time) 让当前线程休眠指定的时间后再继续执行,单位为毫秒
package com.yu.Day1009Demo;

public class MyThread extends Thread{
    public MyThread() {
    }
    public MyThread(String name) {
        super(name);
    }
    @Override
    public void run() {
        for (int i = 0; i < 5; i++) {
            System.out.println(Thread.currentThread().getName()+"输出"+i);
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

package com.yu.Day1009Demo;

public class ThreadDemo05 {
    public static void main(String[] args) {
        Thread t1 = new MyThread();
        t1.setName("1hao");
        t1.start();
        System.out.println(t1.getName());

        Thread t2 = new MyThread("2hao");
        t2.start();
        System.out.println(t2.getName());

        Thread m = Thread.currentThread();
        System.out.println(m.getName());

        for (int i = 0; i < 5; i++) {
            System.out.println("主线程"+"输出"+i);
        }
    }
}

线程安全

线程安全问题
  • 多个线程同时操作同一个共享资源的适合可能会出现的安全问题,称为线程安全问题
发生的原因
  • 存在多线程并发
  • 同时访问共享资源
  • 存在访问共享资源
线程安全问题案例模拟
package com.yu.Day1009Demo;

public class Accout {
    private String cardId;
    private double money;

    public Accout() {
    }

    public Accout(String cardId, double money) {
        this.cardId = cardId;
        this.money = money;
    }

    public String getCardId() {
        return cardId;
    }

    public void setCardId(String cardId) {
        this.cardId = cardId;
    }

    public double getMoney() {
        return money;
    }

    public void setMoney(double money) {
        this.money = money;
    }

    public void drawMoney(double money) {
        String name = Thread.currentThread().getName();
        if(this.money>=money){
            System.out.println(name+"来取钱成功,取出"+money);
            this.money-=money;
            System.out.println(name+"取钱后剩余"+this.money);

        }else{
            System.out.println(name+"账户余额不足");
        }
    }
}
package com.yu.Day1009Demo;

public class DrawThread extends Thread{
    private Accout acc;

    public DrawThread(Accout acc,String name) {
        super(name);
        this.acc = acc;
    }

    @Override
    public void run() {
        //取钱操作
        acc.drawMoney(10000);
    }
}
package com.yu.Day1009Demo;

public class ThreadDemo {
    public static void main(String[] args) {
        //建立公共账户
        Accout acc = new Accout("ICBC-111",10000);
        //建立两个线程对象,代表小红小明同时进来
        new DrawThread(acc,"小红").start();
        new DrawThread(acc,"小明").start();
    }
}
小红来取钱成功,取出10000.0
小明来取钱成功,取出10000.0
小明取钱后剩余-10000.0
小红取钱后剩余0.0

线程同步

思想
  • 加锁🔒,把共享资源进性上锁,每次只能一个线程进入,访问完毕以后解锁,然后其他线程才能进来
方式一:同步代码块
  • 作用:把出现线程安全问题的核心代码块上锁
  • 原理:每次只能一个线程进入,执行完毕和自动解锁,其他线程才可以进来执行
synchronized(同步锁对象){
    操作共享资源的代码(核心代码)
}
锁对象要求
  • 理论上:锁对象只要对于当前同时执行的线程来说是同一个对象即可
public void drawMoney(double money) {
    String name = Thread.currentThread().getName();
    synchronized ("YU") {
        if(this.money>=money){
            System.out.println(name+"来取钱成功,取出"+money);
            this.money-=money;
            System.out.println(name+"取钱后剩余"+this.money);

        }else{
            System.out.println(name+"账户余额不足");
        }
    }
}
锁对象用任意唯一的对象好不好
  • 不好,会影响其他无关线程
锁对象的规范要求
  • 规范上:建议使用共享资源作为锁对象
  • 对于实列方法建议使用this作为锁对象
  • 对于静态方法建议使用字节码(类名.class)对象作为锁对象
 @Override
    public void run() {
        //取钱操作
		//多个对象
        synchronized (Accout.class) {
            acc.drawMoney(10000);
        }
    }
}
总结
  1. 同步代码块是如何实现线程安全的?
    • 对出现问题的核心代码使用synchronized进性加锁
    • 每次只能一个线程占锁进入访问
  2. 同步代码块的同步锁对象有什么要求?
    • 对于实列方法建议使用this作为锁对象
    • 对于静态方法建议使用字节码(类名.class)对象作为锁对象
方式二:同步方法
  • 作用:把出现线程安全问题的核心方法给上锁
  • 原理:每次只能一个线程进入,执行完毕以后自动解锁,其他线程才可以进来执行
格式
修饰符 synchronized 返回值类型 方法名称(形参列表){
    操作共享资源的代码
}
同步方法的原理
  • 同步方法其实底层也是有隐式锁对象的,只是锁的范围是整个方法代码
  • 如果方法是实例方法:同步方法默认用this作为锁对象。但是代码高度面向对象!
  • 如果方法是静态方法:同步方法默认用类名.class作为的锁对象
Lock锁
  • 为了更清晰的表达如何加锁,JDK5以后提供了一个新的锁对象Lock,更加灵活,方便。

  • Lock实现提供比使用synchronized方法和语句可以获得更广泛的锁顶操作

  • Lock是接口不能直接实列化,这里采用它的实现类ReentrantLock来构建Lock锁对象

  • 方法名称 说明
    public ReentrantLock() 获得Lock锁的实现类对象
    Lock的API
    方法名称 说明
    void lock() 获得锁
    void unlock() 释放锁

线程通信(理解概念)

概念
  • 所谓线程通信就是线程间相互发送数据,线程通信通常通过共享一个数据的方式实现
  • 线程间会根据共享数据的情况决定自己该怎么做,以及通知其他线程怎么做
线程通信常见模型
  • 生产者与消费者模型:生产者线程负责生产数据,消费者线程负责消费数据
  • 要求:生产者线程生产完数据后,唤醒消费者,然后等待自;消费者消费完该数据后,唤醒生产者,然后等待自己
前提
  • 线程通信通常是在多个线程操作同一个共享资源的时候需要进行通信,且要保证线程安全
Object类的等待和唤醒方法
方法名称 说明
void wait() 让当前线程等待并释放所占锁,直到另一个线程调用notify()方法或notifyAll()方法
void notify() 唤醒正在等待的单个线程
void notifyAll() 唤醒正在等待的所有线程
注意
  • 上述方法应该使用当前同步锁对象进行调用

线程池*

  • 线程池就是一个可以复用线程的技术
线程池的工作原理

工作线程(WorkThread)的数量是固定的当处理完一个任务时,会在任务队列(WorkQueue)里选择下一个任务继续执行,过多线程会影响cpu运算速度

任务接口

  • runnable
  • Cakkable
线程池实现的API、参数说明
线程池代表
  • JDK 5.0起提供了代表线程池的接口:ExecutorService
得到线程池对象
  • 方式一:使用ExecutorService的实现类ThreadPoolExecutor自创建一个线程池对象

    ExecutorService
    ⬇️
    ThreadPoolExecutor
  • 方式二:使用Executors(线程池的工具类)调用方法返回不同特点的线程池对象

ThreadPoolExecutor构造器的参数说明
public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue,
                          ThreadFactory threadFactory,
                          RejectedExecutionHandler handler
                         )
参数一:指定线程池的线程数量(核心线程):corePoolSize ➡️ 不能小于零
参数二:指定线程池可支持的最大线程数:maximumPoolSize ➡️ 最大数量>=核心线程数量
参数三:指定临时线程的最大存活时间:keepAliveTime ➡️ 不能小于零
参数四:指定存活时间的单位(秒、分、时、天):unit ➡️ 时间单位
参数五:指定任务队列:workQueue ➡️ 不能为null
参数六:指定用哪个线程过程创建线程:threadFactory ➡️ 不能为null
参数七:指定线程忙,任务满时,新任务来了怎么办:handler ➡️ 不能为null
线程池常见面试题
  1. 临时线程什么时候创建?
  • 新任务提交时发现核心线程都在忙,任务队列也满了,并且还可以创建临时线程,此时才会创建临时线程
  1. 什么时候会开始拒绝任务
  • 核心线程和临时线程都在忙,任务队列也满了,新的任务过来的时候才会开始任务拒绝
线程池处理Runnable任务
ThreadPoolExecutor创建线程池对象实例
ExecutorService pools = new ThreadPoolExecutor(3,5,8,TimeUnit.SECONDS,new ArrayBlockingQueue<>(6),Executors.defaultTHreadFactory(), new ThreadPoolExecutor.AbortPolicy())
package com.yu.Day1009Demo;

import java.util.concurrent.*;

/**
    目标:自定义一个线程池对象,并测试其特性
 */
public class ThreadPoolDemo01 {
    public static void main(String[] args) {
        //1.创建线程池对象
        ExecutorService pool = new ThreadPoolExecutor(3,5,6, TimeUnit.SECONDS,
                new ArrayBlockingQueue<>(5), Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.AbortPolicy());
        //2.给任务线程池处理
        Runnable target = new MyRUnnable();
        //直接执行的任务
        pool.execute(target);
        pool.execute(target);
        pool.execute(target);


        //进入任务队列
        pool.execute(target);
        pool.execute(target);
        pool.execute(target);
        pool.execute(target);
        pool.execute(target);

        //创建临时线程
        pool.execute(target);
        pool.execute(target);
        //不创建,拒绝策略被触发!!!
//        pool.execute(target);
        //Exception in thread "main" java.util.concurrent.RejectedExecutionException: Task com.yu.Day1009Demo.MyRUnnable@5cad8086 rejected from java.util.concurrent.ThreadPoolExecutor@6e0be858[Running, pool size = 5, active threads = 5, queued tasks = 5, completed tasks = 0]
        // at java.util.concurrent.ThreadPoolExecutor$AbortPolicy.rejectedExecution(ThreadPoolExecutor.java:2047)
        // at java.util.concurrent.ThreadPoolExecutor.reject(ThreadPoolExecutor.java:823)
        // at java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:1369)
        // at com.yu.Day1009Demo.ThreadPoolDemo01.main(ThreadPoolDemo01.java:33)

        //关闭线程池(开发中一般不会使用)
        pool.shutdownNow();
    }
}
package com.yu.Day1009Demo;

public class MyRUnnable implements Runnable {
    @Override
    public void run() {
        for (int i = 0; i < 5 ; i++) {
            System.out.println(Thread.currentThread().getName()+"输出了,HelloWorld-->"+i);
            try {
                Thread.sleep(6000);
                System.out.println("线程休息6秒");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
ExecutorService的常用方法
方法名称 说明
void execute(Runnable command) 执行任务/命令,没有返回值,一般用来执行:Runnable任务
Future submit(Callable task) 执行任务,返回未来任务对象获取线程结果,一般拿来执行Callable任务
void shutdown() 等待任务执行完毕后关闭线程池
List shutdownNow() 立刻关闭,停止正在执行的任务,并返回队列中未执行的任务
新任务拒绝策略
策略 详解
ThreadPoolExecutor.AbortPolicy 丢弃任务并抛出RejetedExecution异常,是默认的策略
ThreadPoolExecutor.DiscardPolicy 丢弃任务但是不抛出异常,这是不推荐的做法
ThreadPoolExecutor.DiscardOldestPolicy 抛弃队列中等待最久的任务,然后把当前任务加入队列中
ThreadPoolExecutor.CallerRunsPolicy 由主线程负责调用任务的run()方法从而绕过线程池直接执行
线程池处理Callable任务
package com.yu.Day1009Demo;

import java.util.concurrent.*;

/**
    目标:自定义一个线程池对象,并测试其特性
 */
public class ThreadPoolDemo02 {
    public static void main(String[] args) throws Exception {
        //1.创建线程池对象
        ExecutorService pool = new ThreadPoolExecutor(3,5,6, TimeUnit.SECONDS,
                new ArrayBlockingQueue<>(5), Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.AbortPolicy());
        //2.给任务线程池处理
        Future<String> f1 = pool.submit(new MyCallable(100));
        Future<String> f2 = pool.submit(new MyCallable(200));
        Future<String> f3 = pool.submit(new MyCallable(300));
        Future<String> f4 = pool.submit(new MyCallable(400));
        System.out.println(f1.get());
        System.out.println(f2.get());
        System.out.println(f3.get());
        System.out.println(f4.get());
    }
}
package com.yu.Day1009Demo;

import java.util.concurrent.Callable;

public class MyCallable implements Callable<String> {
    private int i;

    public MyCallable(int i) {
        this.i = i;
    }

    @Override
    public String call() throws Exception {
        int sum=0;
        for (int j = 1; j < i ; j++) {
            sum+=j;
        }
        return Thread.currentThread().getName()+"执行结果是:"+sum;
    }
}
  1. 线程池如何处理Callable任务,并得到任务执行完后返回的结果
    • 使用ExecutorService的方法
    • Future submit(Callable command)

Executors得到线程池对象的常用方法

  • Executors:线程池的工具类通过调用方法返回不同类型的线程池对象
方法名称 说明
public static ExecutorService newCachedThreadPool() 线程数量随着任务增加而增加,如果线程任务执行完毕且空闲一段时间则会被回收掉
public static ExecutorService newFixedThreadPool(int nThreads) 创建固定线程数量的线程池,如果某个线程因为执行异常而结束,那么线程池会补充一个新线程代替它
public static ExecutorService newSingleThreadExecutor() 创建只有一个线程的线程池对象,如果该线程出现异常而结束,那么线程池会补充一个新线程池
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) 创建一个线程池,可以实现再给定的延迟后运行任务,或者定期执行任务

*** 注意:Executors的底层其实也是基于线程池的实现类ThreadPoolExecutor创建线程池对象的***

Executors使用可能存在的风险
  • 大型并发系统环境中使用Executor如果不注意可能会出现系统风险
方法名称 存在问题
public static ExecutorService newFixedThreadPool(int nThread) 允许请求的任务队列长度是Integer.MAX_VALUE,可能出现OOM错误(java.lang.OutOfMemoryErroe)
public static ExecutorService newSingleThreadExecutor() 同上
public static ExecutorService newCachedThreadPool() 创建的线程数量最大上限是Integer.MAX_VALUE,线程数可能会随着任务1:1增长,也可能出现OOM错误(java.lang.OutOfMemoryError)
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) 同上
总结
  1. Executors工具类底层是基于什么方式实现的线程池对象?
  • 线程池ExecutorService的实现类:ThreadPoolExecutor
  1. Executors是否适合做大型互联网场景的线程池方案
    • 不适合
    • 建议使用ThreadPoolExecutor来指定线程池参数,这样可以明确线程池的运行规则,规避资源耗尽的风险

定时器

  • 定时器是一种控制任务延时调用,或者周期调用的技术
  • 作用:闹钟、定时邮件发送

定时器的实现方式

  • 方式一:Timer
  • 方式二:ScheduledExecutorService
Timer定时器
构造器 说明
public Timer() 创建Timer定时器对象
方法 说明
public void schedule(TimerTask task,long delay,long period) 开启一个定时器,按照计划处理TimerTask任务
Timer定时器的特点和存在的问题
  1. Timer是单线程,处理多个任务按照顺序执行,存在延时与设置定时器的时间有出入
  2. 可能因为其中的某个任务的异常使Timer线程死掉,从而影响后续任务执行
package com.yu.Day1009Demo;

import java.util.Timer;
import java.util.TimerTask;

public class TimerDemo01 {
    public static void main(String[] args) {
        //1.创建TImer定时器
        Timer timer = new Timer();
        //调用方法,处理定时任务
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName()+"执行一次···");
            }
        },3000,2000);
    }
}
ScheduledExecutorService定时器
  • ScheduledExecutorService是jdk1.5中引入了并发包,目的是为了弥补Timer的缺陷,ScheduledExecutorService内部为线程池
Executors方法 说明
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) 得到线程池对象
ScheduledExecutorService方法 说明
public ScheduledFuture<?> scheduleAtFixedRare(Runnable command,long initialDelay,long period,TimeUnit unit) 周期调度方法
优点
  1. 基于线程池,某个任务的执行情况不会影响其他定时任务的执行

并发、并行

  • 正在运行的程序(软件)就是一个独立的进程,线程是属于进程的,多个线程其实就是并发与并行同时进性的
并发的理解
  • CPU同时处理线程的数量有限。
  • CPU会轮询为系统的每个线程服务,由于CPU切换的速度很快,给我们的感觉这些线程再同时执行,这就是并发
并行的理解
  • 再同一个时刻上,同时有多个线程再被cpu处理并执行
总结
  1. 简单说说并发和并行的含义
    • 并发:CPU分时轮询的执行线程
    • 并行:同一个时刻同时再执行

线程的声明周期

线程的状态
  • 线程的状态:也就是线程从生到死的过程,以及中间经历的各种状态集状态转换
  • 理解线程的状态有利于提升并发编程的理解能力
Java线程的状态
  • Java总共定义了6种状态
  • 6中状态都定义在Thread类的内部枚举类中
public class Thread{
    ···
    public enum State{
        NEW;
        RUNNABLE;
        BLOCKED;
        WAITING;
        TIMED_WAITING;
        TERMINATED;}
    ···
}
线程的6种状态总结
线程状态 描述
NEW(新建) 线程刚被创建,但是未启动
Runnable(可运行) 线程已经调用start()等待CPU调查
Blocked(锁阻塞) 线程在执行的时候未竞争到锁对象,则该线程进入Blocked状态。
Waiting(无线等待) 一个线程进入Waiting状态,另一个线程调用notify或者notifyAll方法才能够唤醒
Timed Waiting(计时等待) 通waiting状态,有几个方法有超时参数,调用他们将进入Timed Waiting 状态,带有超时参数的常用方法有Thread.sleep、Object.wait
Teminated(被终止) 因为run方法正常退出而死亡,或者因为没有的捕获异常终止了run方法而死亡

 posted on 2022-10-11 18:26  yulll  阅读(20)  评论(0编辑  收藏  举报