线程基础学习:浅谈线程等待、通知和线程池,

线程的等待和通知

等待和通知是Object类下的方法,但只有锁对象才能调用,如:

wait():让当前线程进入等待状态,直到被通知才能执行;

wait(long):让当前线程进入等待状态,同时给他设置一个等待的时间,直到被通知或者时间结束;

notify():随机通知一个正在等待的线程,让其开始执行;

notifyAll():通知所有正在等待的线程

注意:必须是锁对象,否则会抛出IllegalMonitorStateException

/**
*
*通过锁对象将线程等待,5秒后通知该线程执行
*/
public class WaitDemo {
    public synchronized void print() throws InterruptedException {
        for (int i = 0; i < 100; i++) {
            System.out.println(Thread.currentThread().getName() + "--->" +i);
            if(i == 50){
                //让当前线程等待
                this.wait();
            }
        }
    }
    public synchronized void notifyTest(){
        //让等待的线程执行
        this.notifyAll();
    }
    public static void main(String[] args) {
        WaitDemo waitDemo = new WaitDemo();
        new Thread(()->{
            try {
                waitDemo.print();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }).start();
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        waitDemo.notifyTest();
    }
}

wait和sleep的区别

通过之前的学习我们都知道让线程进入阻塞还可以让其sleep,那么wait和他的区别是什么?

1.调用对象不同

wait()由锁对象来调用

sleep()由线程来调用

2.锁的使用不同

执行wait后,会自动释放锁

执行sleep后不会自动释放

3.唤醒机制不同

wait可以被通知方法唤醒

sleep只能等待时间结束,自动唤醒

线程池

我们可以从它是什么?有什么作用?来学习。

什么是线程?作用是什么?

我们都知道线程是一种宝贵的系统资源,执行完任务后会死亡,如果有大量的任务需要处理,那么就需要频繁的创建和销毁线程(线程的生命周期),造成系统性能降低。而线程池则会保存一定量的线程,不用频繁的创建和销毁,当线程池执行完任务后它会回到线程池等待下一个任务,不仅节约资源,还提升了性能;它的 作用就是将线程回收利用。

如何使用线程池?

线程池的API

Executor接口

ExecutorService接口

AbstractExecutorService抽象类

ThreadPoolExecutor线程池类

Executors工具类

(顶层接口是Executor,可以调用executor(Runnable)来启动一个线程执行任务)

ExecutorServcie

它是继承自Executor,可以添加线程池管理方法,如:shutdown()、shutdownNow()

Executors工具类

主要用于创建线程池的工具类,主要方法有:

方法名说明
newCachedThreadPool()返回线程数量无限的线程池
newFixedThreadPool(int )返回固定长度的线程池,可以控制并发量
newSingleThreadExecutor()返回单一线程的线程池
newScheduledThreadPool(int)返回可调度的线程

我用几行代码展示一下用法:

public class ThreadPoolDemo {
    public static void useCachedThreadPool(){
        //创建长度不限的线程池
        ExecutorService executorService = Executors.newCachedThreadPool();
        for (int i = 0; i < 10; i++) {
            int n = i;
            //使用线程池启动线程
            executorService.execute(()->{
                System.out.println(Thread.currentThread().getName()+"执行了任务"+n);
            });
            try {
                Thread.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        executorService.shutdown();
    }

    public static void useFixedThreadPool(){
        //创建长度固定的线程池
        ExecutorService executorService = Executors.newFixedThreadPool(10);
        for (int i = 0; i < 100; i++) {
            int n = i;
            //使用线程池启动线程
            executorService.execute(()->{
                System.out.println(Thread.currentThread().getName()+"执行了任务"+n);
            });
            try {
                Thread.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        executorService.shutdown();
    }
    public static void useSingleThreadPool(){
        //创建单一长度的线程池
        ExecutorService executorService = Executors.newSingleThreadExecutor();
        for (int i = 0; i < 10; i++) {
            int n = i;
            //使用线程池启动线程
            executorService.execute(()->{
                System.out.println(Thread.currentThread().getName()+"执行了任务"+n);
            });
            try {
                Thread.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        executorService.shutdown();
    }

    public static void useScheduledThreadPool(){
        //创建可调度的线程池
        ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(10);
        System.out.println("----------");
        scheduledExecutorService.scheduleAtFixedRate(()->{
            System.out.println(Thread.currentThread().getName()+"---->"+ LocalDateTime.now());
        }, 1,3, TimeUnit.SECONDS);
    }

    public static void main(String[] args) {
//        useCachedThreadPool();
//        useFixedThreadPool();
//        useSingleThreadPool();
        useScheduledThreadPool();

    }
}

自定义线程池的配置

线程池的实现类:ThreadPoolExecutor

public ThreadPoolExecutor(int corePoolSize,

int maximumPoolSize,

long KeepAliveTime,

TimeUnit unit,

BlockingQueue workQueue)

参数解释:

corePoolSize:核心线程数;

maximumPoolSize:最大线程数;

keepALiveTime:临时的线程能够存活的时间

TimeUnit :时间单位;

BlockingQueue :用于保存任务的阻塞队列

优化配置

1.核心线程数:应该和CPU内核数量相关(大于或等于),可以通过:Runtime.getRuntime().availableProcessors()来获取。

2.最大线程数可以和核心线程数相等,应避免频繁的创建和销毁线程

3.如果存在非核心线程,设置大一点,应避免频繁的创建和销毁线程

4.阻塞队列使用LinkedBlockingQueue的性能比较高

//cpu内核数
	public static final int CPU_NUM = Runtime.getRuntime().availableProcessors();
	
	/**
	 * 自定义配置线程池
	 */
	public static void testThreadPool(){
		//配置线程池
		ExecutorService pool = new ThreadPoolExecutor(CPU_NUM,CPU_NUM * 4 + 20,
				0,TimeUnit.SECONDS,new LinkedBlockingQueue<Runnable>());
		for(int i = 0;i < 100;i++){
			final int n = i;
			//使用线程池启动线程
			pool.execute(()->{
				System.out.println(Thread.currentThread().getName()+"执行了! i --> "+n);
			});
		}
		//关闭线程池
		pool.shutdown();
	}

线程池的实现原理

所有的线程保存在HashSet中

 生产者和消费者模式

在线程世界里,也有消费者和生产者,生产数据的线程是生产者,消费数据的线程是消费者;这是一种设计模式。

 

实现过程:

通过添加缓冲区,设置上限;生产者生产数据,向缓冲区存放,如果满了,生产者进入等待,直到缓冲区有空的位置通知生产者生产;消费者从缓冲区取数据进行消费,如果空了,消费者进入等待,直到缓冲区有数据再通知消费者消费。

解决问题:降低耦合性,提高了并发性能,解决忙线不均

package com.cn.wait;

import java.util.ArrayList;
import java.util.List;

public class BaoziShop {
    /**
     * 包子
     */
    class Baozi{
        private int id;

        public Baozi(int id) {
            this.id = id;
        }

        @Override
        public String toString() {
            return "香喷喷的包子" + id;
        }
    }
    private int MAX = 100;
    private List<Baozi> baozis = new ArrayList<>();
    public synchronized void makeBaozi(){
        //判断缓冲区是否满了
        if (baozis.size()==100){
            try {
                System.out.println("缓冲区满了,还请等待");
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }else {
            this.notifyAll();
        }
        //创建包子
        Baozi baozi = new Baozi(baozis.size()+1);
        System.out.println(Thread.currentThread().getName()+"做了"+baozi);
        //保存到缓冲区
        baozis.add(baozi);
    }
    public synchronized void takeBaozi(){
        if(baozis.size()==0){
            try {
                System.out.println("缓冲区空了,还请等待");
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }else {
            this.notifyAll();
        }
        if (baozis.size()>0){
            Baozi baozi = baozis.remove(0);
            System.out.println(Thread.currentThread().getName()+"吃了"+baozi);
        }
    }

    public static void main(String[] args) {
        BaoziShop baoziShop = new BaoziShop();
        Thread product = new Thread(() -> {
            for (int i = 0; i < 100; i++) {
                try {
                    baoziShop.makeBaozi();
                    Thread.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });

        for (int i = 0; i < 10; i++) {
            Thread consumer = new Thread(()->{
                for (int j = 0; j < 10; j++) {
                    baoziShop.takeBaozi();
                }
            });
            consumer.start();
        }
        product.start();
    }
}

上面的生产者消费者模式除了使用锁的等待和通知方式外,还可以使用阻塞队列,自动完成等待和通知功能。

阻塞队列

应用了生产者和消费者模式的集合,根据数据满或空,自动对线程执行等待和通知

BlockingQueue 接口:

put 添加数据,达到上限会自动让线程等待

take 取并删除数据,数据空了会自动让线程等待

实现类:

ArrayBlockingQueue 类 数据结构为数组

LinkedBlockingQueue类 链表结构

还是以包子铺为例

/**
 * 包子铺
 */
public class BaoziShop2 {
    /**
     * 包子
     */
    static class Baozi{
        private int id;

        public Baozi(int id) {
            this.id = id;
        }

        @Override
        public String toString() {
            return "香喷喷的包子" + id;
        }
    }

    public static void main(String[] args) {
        //阻塞队列
        BlockingQueue<Baozi> baozis = new ArrayBlockingQueue<>(100);
        //生产者线程
        new Thread(()->{
            for (int i = 0; i < 200; i++) {
                //创建包子,添加到阻塞队列,满了就自动阻塞线程
                Baozi baozi = new Baozi(baozis.size() + 1);
                try {
                    baozis.put(baozi);
                    System.out.println(Thread.currentThread().getName()+"生产了"+baozi);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();

        //消费者
        for (int i = 0; i < 5; i++) {
            new Thread(()->{
                //取包子,空了会自动阻塞
                for (int j = 0; j < 40; j++) {
                    try {
                        Baozi baozi = baozis.take();
                        System.out.println(Thread.currentThread().getName()+"消费了"+baozi);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }).start();
        }
    }
}

结束

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值