当前位置 : 主页 > 编程语言 > java >

73 java多线程_5 _线程池

来源:互联网 收集:自由互联 发布时间:2022-07-13
文章目录 ​​线程池概念​​ ​​线程池原理​​ ​​创建线程池,线程池API​​ ​​Callable接口​​ ​​Callable的基本使用​​ ​​Callable结合线程池的使用​​ ​​Future接口​​


文章目录

  • ​​线程池概念​​
  • ​​线程池原理​​
  • ​​创建线程池,线程池API​​
  • ​​Callable接口​​
  • ​​Callable的基本使用​​
  • ​​Callable结合线程池的使用​​
  • ​​Future接口​​
  • ​​线程同步和异步​​
  • ​​线程同步​​
  • ​​线程异步​​
  • ​​Lock接口(与synchronized)​​
  • ​​Lock接口的实现类:ReentrantLock​​
  • ​​ReadWriteLock接口:ReentrantReadWriteLock​​

线程池概念

  • 问题:
    • 线程是宝贵的内存资源、单个线程约占1MB空间,过多分配易造成内存溢出。
    • 频繁的创建及销毁线程会增加虚拟机回收频率、资源开销,造成程序性能下降。
  • 线程池:
    • 线程容器,可设定线程分配的数量上限。
    • 将预先创建的线程对象存入池中,并重用线程池中的线程对象。
    • 避免频繁的创建和销毁。

    线程池原理

    将任务提交给线程池,由线程池分配线程、运行任务,并在当前任务结束后复用线程

    73 java多线程_5 _线程池_多线程

    创建线程池,线程池API

    常用的线程池接口和类(所在包java. util. concurrent) :

  • Executor:线程池的顶级接口。
  • ExecutorService:线程池接口,可通过submit (Runnable task) 提交任务代码。
  • Executors工厂类:通过此类可以获得一个线程池。
  • 通过newFixedThreadPool (int nThreads) 获取固定数量的线程池。参数:指定线程池中线程的数量。
  • 通过newCachedThreadPool() 获得动态数量的线程池,如不够则创建新的,没有上限
  • package com.wlw.thread.threaddemo02;

    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    import java.util.concurrent.ScheduledExecutorService;
    import java.util.concurrent.ThreadPoolExecutor;

    /**
    * 线程池的创建
    * (1)Executor:线程池的根接口 -----> execute()执行
    * (2)ExecutorService:包含了管理线程池的一些方法,submit,shutdown
    * 子类 ThreadPoolExecutor
    * 子类 ScheduledThreadPoolExecutor
    * (3)Executors:创建线程池的工具类
    * (3.1)创建固定线程个数线程池
    * (3.2)创建缓存(动态数量)线程池,由任务的多少决定
    * (3.3)创建单线程池
    * (3.4)创建调度线程池,调度:周期、定时执行
    *
    */
    public class ThreadPoolDemo01 {
    public static void main(String[] args) {
    //1.1创建固定线程个数线程池
    //ExecutorService executorService = Executors.newFixedThreadPool(4);
    //1.2创建缓存(动态数量)线程池,由任务的多少决定
    ExecutorService executorService = Executors.newCachedThreadPool();
    //1.3创建单线程池
    //ExecutorService es = Executors.newSingleThreadExecutor();
    //1.4创建调度线程池
    //Executors.newScheduledThreadPool(4);


    //2.创建任务
    Runnable runnable = new Runnable() {
    private int ticket = 100;
    @Override
    public void run() {
    while (true){
    if(ticket <= 0){
    break;
    }
    System.out.println(Thread.currentThread().getName()+"卖出了,第"+ticket+"张票");
    ticket--;
    }
    }
    };

    //3.提交任务(提交多次,分配多个线程)
    for (int i = 0; i < 4; i++) {
    executorService.submit(runnable);
    }

    //4.关闭线程池
    executorService.shutdown();//等提交的任务全部执行完,关闭线程池,但不接受新任务。
    //executorService.shutdownNow();//立即停止,不等待
    }
    }

    Callable接口

    线程创建的三种方式:
    1. 通过继承Thread,重写run方法
    2. 实现Runnable接口
    3. 实现Callable接口
    - jdk5加入,与Runnable接口类似,实现之后代表一个线程任务
    - Callable具有泛型返回值、可以声明异常。

    public interface Callable<V>{
    public V call() throws Exception;
    }

    Callable的基本使用

    注意:需要将Callable对象转换成可执行任务FutureTask(FutureTask 实现了Runnable接口),再传给线程

    package com.wlw.thread.threaddemo02;

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

    /**
    * 演示Callable接口的使用
    * Callable接口与Runnable接口的区别:
    * (1)Callable接口中call方法具有泛型返回值,Runnable接口中的run方法没有返回值
    * (2)Callable接口中call方法有声明异常,Runnable接口中的run方法没有声明异常
    */
    public class CallableDemo {
    public static void main(String[] args) throws Exception {
    //功能需求:使用Callable实现1~100的和
    //1.创建Callable对象
    Callable<Integer> callable = new Callable<Integer>() {
    @Override
    public Integer call() throws Exception {
    System.out.println(Thread.currentThread().getName()+"开始计算:");
    int sum = 0;
    for (int i = 0; i <= 100; i++) {
    sum = sum + i;
    }
    return sum;
    }
    };

    //2.将Callable对象转换成可执行任务(FutureTask 实现了Runnable接口)
    FutureTask<Integer> task = new FutureTask<>(callable);

    //3.创建线程对象,将任务传给线程
    Thread thread = new Thread(task);

    //4.启动线程
    thread.start();

    //5.获取结果(get() 方法要等call() 方法执行完毕,才能返回)
    Integer integer = task.get();
    System.out.println(integer);
    }
    }
    /*
    执行结果:
    Thread-0开始计算:
    5050
    */

    Callable结合线程池的使用

    注意:Callable对象是可以直接传给线程池的(不需要转换成FutureTask)

    线程池.submit (线程池的提交)有返回值,为future : 表示将要执行完任务的结果

    package com.wlw.thread.threaddemo02;

    import java.util.concurrent.*;

    /**
    * Callable结合线程池的使用
    * Callable对象是可以直接传给线程池的(不需要转换成FutureTask)
    * 实现计算1~100的和
    */
    public class ThreadPool_Callable_Demo {
    public static void main(String[] args) throws Exception {
    //1.创建线程池
    ExecutorService es = Executors.newFixedThreadPool(1);

    //2.提交任务 es.submit 的返回值为future : 表示将要执行完任务的结果
    Future<Integer> future = es.submit(new Callable<Integer>() {
    @Override
    public Integer call() throws Exception {
    System.out.println(Thread.currentThread().getName() + "开始计算");
    int sum = 0;
    for (int i = 0; i <= 100; i++) {
    sum = sum + i;
    }
    return sum;
    }
    });

    //3.获取结果
    System.out.println(future.get());

    //4.关闭线程池
    es.shutdown();
    }
    }

    Future接口

  • Future:表示将要完成任务的结果
  • 表示ExecutorServ ice. submit ()所返回的状态结果,就是call()的返回值
  • 方法:V get() 以阻塞形式等待Future中的异步处理结果(call()的返回值)
  • 需求:使用两个线程,并发计算150,51100的和,再进行汇总

    package com.wlw.thread.threaddemo02;

    import java.util.concurrent.Callable;
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    import java.util.concurrent.Future;

    /**
    * Future接口的使用
    * 需求:使用两个线程,并发计算1~50,51~100的和,再进行汇总
    */
    public class FutureDemo {
    public static void main(String[] args) throws Exception{

    //1.创建线程池
    ExecutorService es = Executors.newFixedThreadPool(2);

    //2.提交任务
    Future<Integer> future1 = es.submit(new Callable<Integer>() {
    @Override
    public Integer call() throws Exception {
    System.out.println(Thread.currentThread().getName()+"计算开始");
    int sum = 0;
    for (int i = 0; i <= 50; i++) {
    sum += i;
    }
    System.out.println("1~50 计算完毕");
    return sum;
    }
    });
    Future<Integer> future2 = es.submit(new Callable<Integer>() {
    @Override
    public Integer call() throws Exception {
    System.out.println(Thread.currentThread().getName()+"计算开始");
    int sum = 0;
    for (int i = 51; i <= 100; i++) {
    sum += i;
    }
    System.out.println("51~100 计算完毕");
    return sum;
    }
    });

    //3.获取结果(get() 方法要等call() 方法执行完毕,才能返回)
    System.out.println(future1.get()+future2.get());

    //4.关闭线程池
    es.shutdown();
    }
    }
    /*
    执行结果:
    pool-1-thread-1计算开始
    pool-1-thread-2计算开始
    51~100 计算完毕
    1~50 计算完毕
    5050
    */

    线程同步和异步

    线程同步

    • 形容一次方法调用,同步一旦开始,调用者必须等待该方法返回,才能继续

    73 java多线程_5 _线程池_多线程_02

    线程异步

    • 形容一次方法调用,异步一旦开始,像是一次消息传递,调用者告知之后立刻返回。
    • 二者竞争时间片,并发执行

    73 java多线程_5 _线程池_ide_03

    Lock接口(与synchronized)

  • 实现线程同步的接口
  • JDK5加入,与 synchronized 比较, 显示定义,结构更灵活。
  • 提供更多实用性方法,功能更强大、性能更优越。
  • 常用方法:
    • void lock() //获取锁,如锁被占用,则等待。
    • boolean tryLock() //尝 试获取锁(成功返回true。失败返回false,不阻塞)
    • void unlock() //释放锁

    Lock接口的实现类:ReentrantLock

    重入锁:ReentrantLock:Lock接口的实现类,与synchronized一样具有互斥锁功能(但比synchronized更强大)

    例子1:

    package com.wlw.thread.lock;

    import java.util.concurrent.locks.Lock;
    import java.util.concurrent.locks.ReentrantLock;

    /**
    * ReentrantLock的使用
    * 重入锁
    */
    public class MyList {

    //创建锁
    private Lock lock = new ReentrantLock();
    private String[] str = {"A","B","","",""};
    int count = 2;

    public void add(String value){
    try {
    lock.lock(); //上锁
    str[count] = value;
    try {
    Thread.sleep(1000);//主动休眠1秒
    } catch (InterruptedException e) {
    e.printStackTrace();
    }
    count++;
    System.out.println(Thread.currentThread().getName()+"存入了"+value);

    }finally {
    lock.unlock();//解锁
    }

    }

    public String[] getStr() {
    return str;
    }
    }package com.wlw.thread.lock;

    import java.util.Arrays;

    public class TestList {
    public static void main(String[] args) throws Exception {

    MyList list = new MyList();

    Runnable runnable1 = new Runnable() {
    @Override
    public void run() {
    list.add("Hello");
    }
    };
    Runnable runnable2 = new Runnable() {
    @Override
    public void run() {
    list.add("World");
    }
    };

    //创建线程并启动
    Thread t1 = new Thread(runnable1);
    Thread t2 = new Thread(runnable2);

    t1.start();
    t2.start();

    t1.join();
    t2.join();

    System.out.println(Arrays.toString(list.getStr()));
    }
    }
    /*
    Thread-0存入了Hello
    Thread-1存入了World
    [A, B, Hello, World, ]
    */

    例子2:

    package com.wlw.thread.lock;

    import java.util.concurrent.locks.Lock;
    import java.util.concurrent.locks.ReentrantLock;

    public class Ticket implements Runnable{

    private int ticket = 100;
    private Lock lock = new ReentrantLock();

    @Override
    public void run() {
    while (true){
    lock.lock();
    try {
    if(ticket <= 0){
    break;
    }
    System.out.println(Thread.currentThread().getName()+"卖了第"+ticket+"张票");
    ticket--;
    } finally {
    lock.unlock();
    }

    }
    }
    }package com.wlw.thread.lock;

    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;

    public class TestTicket {
    public static void main(String[] args) {

    //创建实现类对象
    Ticket ticket = new Ticket();
    //创建线程池
    ExecutorService es = Executors.newFixedThreadPool(4);
    //提交任务
    for (int i = 0; i < 4; i++) {
    es.submit(ticket);
    }

    //关闭线程池
    es.shutdown();
    }
    }

    ReadWriteLock接口:ReentrantReadWriteLock

  • 读写锁:ReentrantReadWriteLock:
    • 一种支持一写多读的同步锁,读写分离,可分别分配读锁、写锁。
    • 支持多次分配读锁,使多个读操作可以并发执行。
  • 互斥规则:
    • 写-写:互斥,阻塞。
    • 读-写:互斥,读阻塞写、写阻塞读。
    • 读-读:不互斥、不阻塞。
    • 在读操作远远高于写操作的环境中,可在保障线程安全的情况下,提高运行效率。
    package com.wlw.thread.lock;

    import java.util.concurrent.locks.Lock;
    import java.util.concurrent.locks.ReentrantLock;
    import java.util.concurrent.locks.ReentrantReadWriteLock;
    import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock;
    import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock;
    /**
    * 读写锁的演示
    * ReentrantReadWriteLock
    *
    */
    public class ReadWriteDemo {
    //创建读写锁
    private ReentrantReadWriteLock rrl= new ReentrantReadWriteLock();
    //获取读锁
    private ReadLock readLock = rrl.readLock();
    //获取写锁
    private WriteLock writeLock = rrl.writeLock();

    //重入锁
    private Lock lock = new ReentrantLock();

    private String value;

    public String getValue(){
    readLock.lock();//读锁,来上锁
    //lock.lock();
    try {
    try {
    Thread.sleep(1000);
    } catch (InterruptedException e) {
    e.printStackTrace();
    }
    System.out.println("读取了,"+value);
    return this.value;
    } finally {
    readLock.unlock();
    //lock.unlock();
    }
    }

    public void setValue(String value){
    writeLock.lock();//写锁,上锁
    //lock.lock();
    try {
    try {
    Thread.sleep(1000);
    } catch (InterruptedException e) {
    e.printStackTrace();
    }
    System.out.println("写入了,"+value);
    this.value = value;
    } finally {
    writeLock.unlock();
    //lock.unlock();
    }
    }
    }package com.wlw.thread.lock;

    import java.util.Random;
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;

    public class TestReadWrite {
    public static void main(String[] args) {
    //创建对象
    ReadWriteDemo readWriteDemo = new ReadWriteDemo();
    //创建线程池
    ExecutorService es = Executors.newFixedThreadPool(20);

    Runnable read = new Runnable() {
    @Override
    public void run() {
    readWriteDemo.getValue();
    }
    };
    Runnable write = new Runnable() {
    @Override
    public void run() {
    readWriteDemo.setValue("张三"+new Random().nextInt(100));
    }
    };

    long start = System.currentTimeMillis();

    //提交2次写
    for (int i = 0; i < 2; i++) {
    es.submit(write);
    }

    //提交18次读
    for (int i = 0; i < 18; i++) {
    es.submit(read);
    }

    es.shutdown();//关闭
    while (!es.isTerminated()){//判断所有线程是否都运行完毕,否则空转
    }

    long end = System.currentTimeMillis();
    System.out.println("用时:"+(end - start));
    }
    }
    /*
    读写锁与重入锁 两者用时比较:
    用时:3006 vs 用时:20013
    */


    上一篇:81 java网络编程_1 _计算机网络
    下一篇:没有了
    网友评论