811笔记---小白学Java

Java 为什么多线程都是默认先运行完 main?

package com.qfedu.com;

/**
 * @Author pengyu
 * @Date 2022/8/10 19:39
 */
class A extends Thread {
    @Override
    public void run() {
        System.out.println("run");
    }

    @Override
    public synchronized void start() {
        System.out.println("start");
    }
}
public class Demo05 {
    public static void main(String[] args) {
        System.out.println("jjjj");
        A a = new A();
        new Thread(a).start();
        /**
         * 一个 Java程序从 main 开始之后,进程启动,为整个程序提供各种资源,
         * 而此时将启动一个线程,这个线程就是主线程,它将调度资源,进行具体的操作。
         * Thread、Runnable 的开启的线程是主线程下的子线程,
         * 是父子关系,此时该 Java 程序即为多线程的,
         * 这些线程共同进行资源的调度和执行。
         * Java 中 main 方法启动的是一个进程也是一个主线程,
         * main 方法里面的其他线程均为子线程,所以一般其他线程总是在 main 线程后执行。
         * 控制台输出:
         * jjjj
         * run
         */
    }
}

守护线程

setDaemon(boolean on)

  • 将此线程标记为daemon线程或用户线程。当运行的唯一线程都是守护进程线程时,Java虚拟机将退出。
  • 线程启动前必须调用此方法。

参数

如果 true,将此线程标记为守护线程

虚拟机必须确保用户线程执行完毕,不用等待守护线程执行完毕

示例

package com.qfedu.demo;

/**
 * @Author pengyu
 * @Date 2022/8/10 19:56
 */
class B implements Runnable {
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println("我是B:" + i);
        }
    }
}
class A implements Runnable {

    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            System.out.println("我是A:" + i);
        }
    }
}
public class Demo01 {
    public static void main(String[] args) {
        B b = new B();
        Thread thread = new Thread(b);
        //如果 true ,将此线程标记为守护线程
        //当运行的唯一线程都是守护进程线程时,Java虚拟机将退出。
        thread.setDaemon(true);
        thread.start();

        new Thread(new A()).start();
    }
}

线程的等待与唤醒

方法

  • public final void wait():当前线程进入等待状态
  • public final native void notify():随机唤醒一条锁对象对应线程中的一条(此线程必须是睡眠状态)
  • public final void notifyAll( ):唤醒所有锁(效率低)

因为wait需要释放锁,所以必须在synchronized中使用,没有锁时使用会抛出IllegalMonitorStateException(正在等待的对象没有锁)

线程状态

当我们的线程被创建并启动以后,并不是一启动就进入了执行状态,也不是一直处于执行状态。下面来看一下线程的生命周期吧。

线程状态发生条件
NEW新建线程刚被创建,但是并未启动。还没调用start方法。
Runnable可运行线程可以在java虚拟机中运行的状态,可能正在运行自己代码,也可能没有,这取决于操作系统处理器。
Blocked锁阻塞当一个线程试图获取一个对象锁,而该对象锁被其他的线程持有,则该线程进入Blocked状态;当该线程持有锁时,该线程将变成Runnable状态。
Waiting无限等待一个线程在等待另一个线程执行一个(唤醒)动作时,该线程进入Waiting状态。进入这个状态后是不能自动唤醒的,必须等待另一个线程调用notify或者notifyAll方法才能够唤醒。
Timed Waiting及时等待同waiting状态,有几个方法有超时参数,调用他们将进入Timed Waiting状态。这一状态将一直保持到超时期满或者接收到唤醒通知。带有超时参数的常用方法有Thread.sleep 、Object.wait。
Teminated被终止因为run方法正常退出而死亡,或者因为没有捕获的异常终止了run方法而死亡。

jion方法

只是线程中的一个方法,可以先执行当前线程再执行父线程

  • public final void join():让线程在当前线程优先执行,直至线程执行完毕时,再执行当前线程.
  • public final void join(long millis):让线程执行millis毫秒,然后将线程执行器抛出,给其他线程争抢

示例

package com.qfedu.demo;

import com.qfedu.test.Factory;

/**
 * @Author pengyu
 * @Date 2022/8/11 19:12
 */
class Father implements Runnable {

    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            System.out.println("线程" + i);
        }
    }
}
class Son implements Runnable {

    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            System.out.println("我是子线程" + i);
        }
    }
}
public class Demo04 {
    public static void main(String[] args) throws InterruptedException {
        Father father = new Father();
        new Thread(father).start();

        Son son = new Son();
        Thread thread = new Thread(son);
        thread.start();
        /**
         * 不使用join方法,可能会出现主线程先打印
         * 加了就会先打印子线程,并且打印完
         */
        //thread.join();

        for (int i = 0; i < 10; i++) {
            System.out.println("我是主线程" + i);
        }
    }
}

生产者——消费者问题

在这里插入图片描述

我们需要有个中间的桥梁——车类

package com.qfedu.test;

/**
 * @Author pengyu
 * @Date 2022/8/10 20:42
 */
public class Goods {
    private String name;
    //这里我把价格的英文写成age了,不好意思,应该是price
    private int age;
    private boolean production;

    public Goods(String name, int age, boolean production) {
        this.name = name;
        this.age = age;
        this.production = production;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public boolean isProduction() {
        return production;
    }

    public void setProduction(boolean production) {
        this.production = production;
    }

    @Override
    public String toString() {
        return "Goods{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", production=" + production +
                '}';
    }
}

消费者

package com.qfedu.test;

/**
 * @Author pengyu
 * @Date 2022/8/10 20:43
 * 消费者
 */
public class Customer implements Runnable {
    private Goods goods;

    public Customer(Goods goods) {
        this.goods = goods;
    }

    @Override
    public void run() {
        while (true) {
            synchronized (goods) {
                if (goods.isProduction()) {
                    try {
                        goods.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                } else {
                    System.out.println("消费者购买了" + goods.getName() + ",价格:" + goods.getAge());

                    //改变是否生产属性进行if判定
                    goods.setProduction(true);
                    //唤醒工厂线程开始生产
                    goods.notify();
                }
            }
        }
    }
}

生产者

package com.qfedu.test;

/**
 * @Author pengyu
 * @Date 2022/8/10 20:44
 * 生产者
 */
public class Factory implements Runnable {
    private Goods goods;

    public Factory(Goods goods) {
        this.goods = goods;
    }

    @Override
    public void run() {
        while (true) {
            int count = 0;
            synchronized (goods) {
                if (goods.isProduction()) {
                    if (count % 2 == 0) {
                        goods.setName("破二手车");
                        goods.setAge(90);
                    } else {
                        goods.setName("保时捷");
                        goods.setAge(90);
                    }
                    System.out.println("生产了" + goods.getName() + ",价格" + goods.getAge());
                    //改变是否生产属性进行if判定
                    goods.setProduction(false);
                    count++;
                    //唤醒消费者线程
                    goods.notify();
                } else {
                    //生产者线程等待
                    try {
                        goods.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }
}

测试

package com.qfedu.test;

/**
 * @Author pengyu
 * @Date 2022/8/10 20:53
 */
public class Test01 {
    public static void main(String[] args) {
        Goods car = new Goods("雪佛兰", 1900, false);

        Customer customer = new Customer(car);
        new Thread(customer).start();

        Factory factory = new Factory(car);
        new Thread(factory).start();
    }
}

线程池

阿里规范

线程池不允许使用Executors去创建,而是通过ThreadPoolExecutor的方式,这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。 说明:Executors返回的线程池对象的弊端如下:
1)FixedThreadPool和SingleThreadPool:
  允许的请求队列长度为Integer.MAX_VALUE,可能会堆积大量的请求,从而导致OOM。
2)CachedThreadPool:
  允许的创建线程数量为Integer.MAX_VALUE,可能会创建大量的线程,从而导致OOM。
            
Positive example 1:
    //org.apache.commons.lang3.concurrent.BasicThreadFactory
    ScheduledExecutorService executorService = new ScheduledThreadPoolExecutor(1,
        new BasicThreadFactory.Builder().namingPattern("example-schedule-pool-%d").daemon(true).build());
       
        
            
Positive example 2:
    ThreadFactory namedThreadFactory = new ThreadFactoryBuilder()
        .setNameFormat("demo-pool-%d").build();

    //Common Thread Pool
    ExecutorService pool = new ThreadPoolExecutor(5, 200,
        0L, TimeUnit.MILLISECONDS,
        new LinkedBlockingQueue<Runnable>(1024), namedThreadFactory, new ThreadPoolExecutor.AbortPolicy());

    pool.execute(()-> System.out.println(Thread.currentThread().getName()));
    pool.shutdown();//gracefully shutdown
       
        

线程池的优势:

  1. 降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。
  2. **提高响应速度。**当任务到达时,任务可以不需要等到线程创建就能立即执行。
  3. **提高线程的可管理性。**线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配,调优和监控。

线程池的使用

线程池的真正实现类是 ThreadPoolExecutor,其构造方法有如下4种:


public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue) {
    this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
         Executors.defaultThreadFactory(), defaultHandler);
}
 
public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue,
                          ThreadFactory threadFactory) {
    this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
         threadFactory, defaultHandler);
}
 
public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue,
                          RejectedExecutionHandler handler) {
    this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
         Executors.defaultThreadFactory(), handler);
}
 
public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue,
                          ThreadFactory threadFactory,
                          RejectedExecutionHandler handler) {
    if (corePoolSize < 0 ||
        maximumPoolSize <= 0 ||
        maximumPoolSize < corePoolSize ||
        keepAliveTime < 0)
        throw new IllegalArgumentException();
    if (workQueue == null || threadFactory == null || handler == null)
        throw new NullPointerException();
    this.corePoolSize = corePoolSize;
    this.maximumPoolSize = maximumPoolSize;
    this.workQueue = workQueue;
    this.keepAliveTime = unit.toNanos(keepAliveTime);
    this.threadFactory = threadFactory;
    this.handler = handler;
}

可以看到,其需要如下几个参数:

  • corePoolSize(必需):核心线程数。默认情况下,核心线程会一直存活,但是当将 allowCoreThreadTimeout 设置为 true 时,核心线程也会超时回收。
  • maximumPoolSize(必需):线程池所能容纳的最大线程数。当活跃线程数达到该数值后,后续的新任务将会阻塞。
  • keepAliveTime(必需):线程闲置超时时长。如果超过该时长,非核心线程就会被回收。如果将 allowCoreThreadTimeout 设置为 true 时,核心线程也会超时回收。
  • unit(必需):指定 keepAliveTime 参数的时间单位。常用的有:TimeUnit.MILLISECONDS(毫秒)、TimeUnit.SECONDS(秒)、TimeUnit.MINUTES(分)。
  • workQueue(必需):任务队列。通过线程池的 execute() 方法提交的 Runnable 对象将存储在该参数中。其采用阻塞队列实现。
  • threadFactory(可选):线程工厂。用于指定为线程池创建新线程的方式。
  • handler(可选):拒绝策略。当达到最大线程数时需要执行的饱和策略。

语法格式

// 创建线程池
ThreadPoolExecutor threadPool = new ThreadPoolExecutor(CORE_POOL_SIZE,
                                             MAXIMUM_POOL_SIZE,
                                             KEEP_ALIVE,
                                             TimeUnit.SECONDS,
                                             sPoolWorkQueue,
                                             sThreadFactory);
// 向线程池提交任务
threadPool.execute(new Runnable() {
    @Override
    public void run() {
        ... // 线程执行的任务
    }
});
// 关闭线程池
threadPool.shutdown(); // 设置线程池的状态为SHUTDOWN,然后中断所有没有正在执行任务的线程
threadPool.shutdownNow(); // 设置线程池的状态为 STOP,然后尝试停止所有的正在执行或暂停任务的线程,并返回等待执行任务的列表

示例

package com.qfedu.demo;

import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * @Author pengyu
 * @Date 2022/8/11 18:53
 * 线程池的创建
 */
public class Demo05 {
    public static void main(String[] args) {
        //corePoolSize :核心线程数  5个  一个公司最大容量10人 核心只有5个人 只有这5个人干活
        //maximumPoolSize:线程池最多可以运行线程数 设置为10  约 5个亿左右
        //keepAliveTime:存活时间,空闲下来的线程得回收。按照秒来计算。活动状态,比较活跃 不回收
        //如果你不干活超过100秒,我就会回收掉这个线程!!!
        //unit  是keepAliveTime 单位 可以是秒  小时 天  毫秒, 纳秒!!!
        //workQueue 阻塞队列
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(5, 10, 100, TimeUnit.SECONDS, new LinkedBlockingDeque<>(10));

        for (int i = 0; i < 10; i++) {
            int finalI = i;
            threadPoolExecutor.execute(new Runnable() {
                @Override
                public void run() {
                    System.out.println(Thread.currentThread().getName() + ":" + finalI);
                    try {
                        Thread.sleep(3000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            });
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值