Java多线程笔记【三】

2.4 线程的通信

  • void wait() 令当前线程挂起并放弃CPU、同步资源并等待,使别的线程可访问并修改共享资源,而当前线程排队等候其他线程调用notify()或notifyAll()方法唤醒,唤醒后等待重新获得对监视器的所有权后才能继续执行。
  • void notify() 唤醒正在排队等待同步资源的线程中优先级最高者结束等待
  • void notifyAll() 唤醒正在等待对象监视器的所有线程

注意:

  • 这三个方法只有在synchronized方法或synchronized代码块中才能使用,否则会报java.lang.IllegalMonitorStateException异常。
  • 因为这三个方法必须由锁对象(同步监视器)调用,this可以省略,而任意对象都可以作为synchronized的同步锁,因此这三个方法只能在Object类中声明。

2.4.1 生产者和消费者

  • 消费者等待
    • 消费者:判断桌子上是否有商品,没有就等待
    • 生产者:生产产品,把产品放到桌子上,叫醒等待得消费者来取产品
  • 生产者等待
    • 消费者:判断桌子上是否有产品,如果没有就等待,如果有就取走,取走之后桌子就空了,叫醒等待得生产者继续生成
    • 生产者:判断桌子上是否有产品,如果有就等待,如果没有才生产,把产品放到桌子上。叫醒等待的消费者来取产品
public class Demo {
    public static void main(String[] args) {
        Desk desk = new Desk(false);
        Foodie foodie = new Foodie(desk);
        Cooker cooker = new Cooker(desk);

        new Thread(foodie).start();
        new Thread(cooker).start();
    }
}

class Foodie implements Runnable {
    private Desk desk;

    public Foodie(Desk desk) {
        this.desk = desk;
    }

    @Override
    public void run() {
        while (true) {
            synchronized (desk.getLock()) {
                if (desk.isFlag()) {
                    System.out.println("购买食品");
                    desk.setFlag(false);
                    desk.getLock().notifyAll();
                } else {
                    try {
                        desk.getLock().wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }
}


class Cooker implements Runnable {

    private Desk desk;

    public Cooker(Desk desk) {
        this.desk = desk;
    }

    @Override
    public void run() {
        while (true) {
            synchronized (desk.getLock()) {
                if (!desk.isFlag()) {
                    System.out.println("生产食品");
                    desk.setFlag(true);
                    desk.getLock().notifyAll();
                } else {
                    try {
                        desk.getLock().wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }
}

class Desk {
    private boolean isFlag;

    private Object lock = new Object();

    public Desk(boolean isFlag) {
        this.isFlag = isFlag;
    }

    public boolean isFlag() {
        return isFlag;
    }

    public void setFlag(boolean flag) {
        isFlag = flag;
    }

    public Object getLock() {
        return lock;
    }

}

三、线程池

经常创建和销毁、使用量特别大的资源,比如并发情况下的线程,对性能影响很大。

创建好多个线程,放入线程池中,使用时直接获取,使用完放回池中。可以避免频繁创建销毁、实现重复利用。

好处:

  • 提高响应速度
  • 降低资源消耗
  • 便于线程的管理

3.1 创建线程池-Executors

  1. 使用Executors静态方法,创建线程池
  2. submit方法 注:池子会帮忙创建对象,任务执行完毕,也会自动把线程归还给线程池
  3. 关闭连接池,shutdown方法

3.1.1 Executors

JDK 5.0起提供了线程池相关API:ExecutorServiceExecutors

创建线程池方法
  • newCachedThreadPool() :创建一个根据需要创建新线程的线程池,但在可用时将重新使用以前构造的线程。
  • newFixedThreadPool(int nThreads):创建一个线程池,该线程池重用固定数量的从共享无界队列中运行的线程。 参数可以规定最多可以容纳多少个线程
  • Executors.newSingleThreadExecutor() :创建一个只有一个线程的线程池
  • Executors.newScheduledThreadPool(n):创建一个线程池,它可安排在给定延迟后运行命令或者定期地执行。

ExecutorService:真正的线程池接口。常见子类ThreadPoolExecutor

  • void execute(Runnable command):执行任务/命令,没有返回值,一般用来执行Runnable
  • <T> Future<T> submit(Callable<T> task):执行任务,有返回值,一般又来执行Callable
  • void shutdown() :关闭连接池
public class ThreadPoolDemo1 {
    public static void main(String[] args) throws InterruptedException {
        // 创建默认线程池对象,池子默认是空的,默认最多可以容纳int类型的最大值
        ExecutorService executorService = Executors.newCachedThreadPool();
        // Executors --- 可以帮助创建线程池对象
        // ExecutorService --- 控制线程池对象

        executorService.submit(() -> {
            System.out.println(Thread.currentThread().getName() + "在执行了");
        });

        Thread.sleep(2000);

        executorService.submit(() -> {
            System.out.println(Thread.currentThread().getName() + "在执行了");
        });

        executorService.shutdown();
    }
}
public class ThreadPoolDemo2 {
    public static void main(String[] args) {
        ExecutorService executorService = Executors.newFixedThreadPool(10);

        executorService.submit(() -> {
            System.out.println(Thread.currentThread().getName() + "在执行了");
        });

        executorService.submit(() -> {
            System.out.println(Thread.currentThread().getName() + "在执行了");
        });

        executorService.shutdown();
    }
}

public class Demo {
    public static void main(String[] args) {
        ExecutorService service = Executors.newFixedThreadPool(10);
        
        // 设置线程池属性
        ThreadPoolExecutor s = (ThreadPoolExecutor)service;
        s.setCorePoolSize(15);
//        s.setKeepAliveTime();

        service.execute(new MyRunnable());
        MyCallable myCallable = new MyCallable();
        FutureTask<Integer> ft = new FutureTask<>(myCallable);

        service.submit(ft);
        Integer integer;
        try {
            integer = ft.get();
            System.out.println(integer);
        } catch (InterruptedException | ExecutionException e) {
            e.printStackTrace();
        }
        service.shutdown();
    }
}

class MyRunnable implements Runnable{

    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            System.out.println(i);
        }
    }
}

class MyCallable implements Callable<Integer>{

    @Override
    public Integer call(){
        for (int i = 0; i < 100; i++) {
            System.out.println(i);
        }
        return 1;
    }
}


3.2 创建线程池-ThreadPoolExecutor

  • ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler) :创建一个新 ThreadPoolExecutor给定的初始参数。
    注:
    • 参数一:核心线程数量
    • 参数二:最大线程数
    • 参数三:空闲线程最大存活时间
    • 参数四:时间单位(从TimeUnit里面获取单位),----TimeUnit
    • 参数五:任务队列, — 让任务队列中等待,等有线程空闲了,再从这个队列中获取任务并且执行
    • 参数六:创建线程工厂,— 按照默认的方式创建线程对象
    • 参数七:任务的拒绝策略 — ① 什么适合拒绝 当提交的任务 > 池子中最大线程数量 + 队列容量 ② 如何拒绝 任务拒绝策略
// 参数一:核心线程数量 不能小于0
// 参数二:最大线程数   不能小于0,最大数量>=核心线程数量
// 参数三:空闲线程最大存活时间  不能小于0
// 参数四:时间单位  
// 参数五:任务队列  不能为Null
// 参数六:创建线程工厂  不能为Null
// 参数七:任务的拒绝策略  不能为Null

public class Demo {
    public static void main(String[] args) {
        ThreadPoolExecutor pool = new ThreadPoolExecutor(2,5,2, TimeUnit.SECONDS,new ArrayBlockingQueue<>(10), Executors.defaultThreadFactory(),new ThreadPoolExecutor.AbortPolicy());

        pool.submit(new MyRunnable());
        pool.submit(new MyRunnable());

        pool.shutdown();
    }
}

class MyRunnable implements Runnable{

    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            System.out.println(i);
        }
    }
}

面试题

1. sleep()和wait()的异同

  1. 相同点:一旦执行方法,都可有使得当前的线程进入阻塞状态

  2. 不同点:

    1. 俩个方法声明的位置不同:Thread类中声明sleep(),Object类中声明wait()
    2. 调用的范围不同:sleep()可以再任何需要得场景下调用。wait()必须使用在同步代码块或同步方法中
    3. 关于是否释放同步监视器:如果俩个方法都使用在同步代码块或同步方法中,sleep()不会释放锁,wait()会释放锁。

2.synchronized Lock 的对比

  1. Lock是显式锁(手动开启和关闭锁,别忘记关闭锁),synchronized是隐式锁,出了作用域自动释放
  2. Lock只有代码块锁,synchronized有代码块锁和方法锁
  3. 使用Lock锁,JVM将花费较少的时间来调度线程,性能更好。并且具有更好的扩展性(提供更多的子类)
  4. Lock -> 同步代码块(已经进入了方法体,分配了相应资源) -> 同步方法(在方法体之外)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

猩空中的猩⭐

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值