栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

Java多线程笔记【三】

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

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:ExecutorService 和 Executors

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

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

  • void execute(Runnable command):执行任务/命令,没有返回值,一般用来执行Runnable
  • Future submit(Callable 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 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{

    @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 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 -> 同步代码块(已经进入了方法体,分配了相应资源) -> 同步方法(在方法体之外)
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/919432.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号