Java笔记_20(多线程&JUC)

一、多线程

1.1、多线程概述

进程

  • 进程是程序的基本执行实体

线程

  • 线程是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。
  • 简单理解:应用软件中互相独立,可以同时运行的功能

什么是多线程?

  • 有了多线程,我们就可以让程序同时做多件事情

多线程的作用?

  • 提高效率

多线程的应用场景?

  • 只要你想让多个事情同时运行就需要用到多线程
    比如∶软件中的耗时操作、所有的聊天软件、所有的服务器

1.2、并发和并行

并发: 在同一时刻,有多个指令在单个CPU上交替执行
并行: 在同一时刻,有多个指令在多个CPU上同时执行

1.3、多线程的实现方式

  1. 继承Thread类的方式进行实现
    1. 自己定义一个类继承Thread
    2. 重写run方法
    3. 创建子类的对象,并启动线程
      • 使用start方法
package thread;

public class Dome1 {
    public static void main(String[] args) {
        thread1 t1 = new thread1();
        thread1 t2 = new thread1();

        t1.setName("线程一");
        t2.setName("线程二");
        //线程一和线程二会同时执行
        t1.start();
        t2.start();
    }
}
package thread;

public class thread1 extends Thread {
    @Override
    public void run() {
        //书写线程要执行的代码
        for (int i = 0; i < 100; i++) {
            System.out.println(getName() +"你好啊");
        }
    }
}
  1. 实现Runnable接口的方式进行实现
    1. 在自己定义的类中接上Runnable接口
    2. 重写里面的run方法创建自己的类的对象
      • 在自己创建的类中使用 Thread.currentThread()方法可以获取到当前线程的对象
    3. 创建一个Thread类的对象,并开启线程
    4. 测试类中将自己创建的类传入到Thread类中
package thread;

public class Dome2 {
    public static void main(String[] args) {
        //创建第一个线程
        thread2 t1 = new thread2();
        Thread tt1 = new Thread(t1);
        tt1.setName("线程一");
        tt1.start();
        //创建第二个线程
        thread2 t2 = new thread2();
        Thread tt2 = new Thread(t2);
        tt2.setName("线程二");
        tt2.start();
    }
}
package thread;

public class thread2 implements Runnable {
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            //获取到当前线程的对象
            /*
            Thread t = Thread.currentThread();
            System.out.println(t.getName()+"hello world");
            */
            //第二种获取对象的方式
            System.out.println(Thread.currentThread().getName()+"hello world");
        }
    }
}

  1. 利用Callable接口Future接口方式实现
    特点: 可以获取到多线程运行的结果
    1. 创建一个类MyCallable实现callable接口
    2. 重写call(是有返回值的,表示多线程运行的结果)
    3. 创建Mycallable的对象(表示多线程要执行的任务)
    4. 创建FutureTask的对象(作用管理多线程运行的结果)
    5. 创建Thread类的对象,并启动(表示线程)
package thread;

import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

public class Dome3 {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        //创建thread3对象
        thread3 t1 = new thread3();
        //创建FutureTask对象(管理多线程的结果)
        FutureTask<Integer> ft = new FutureTask<>(t1);
        Thread tt1 = new Thread(ft);
        tt1.start();

        //获取多线程运行的结果
        Integer integer = ft.get();
        System.out.println(integer);
    }
}
package thread;

import java.util.concurrent.Callable;

public class thread3 implements Callable<Integer> {

    @Override
    public Integer call() throws Exception {
        int num = 0;
        for (int i = 0; i <= 100; i++) {
            num = num+i;
        }
        return num;
    }
}

在这里插入图片描述

1.4、多线程中常见的成员方法

方法名称说明
String getName()返口此线程的名称
void setName(String name)设置线程的名字(构造方法也可以设置名字)
static Thread currentThread()获取当前线程的对象
static void sleep( long time)让线程休眠指定的时间,单位为毫秒
setPriority( int newPriority)设置线程的优先级
final int getPriority()获取线程的优先级
final void setDaemon( boolean on)设置为守护线程
public static void yield()出让线程/礼让线程
public static void join()插入线程/插队线程
  • getName()细节
    • 如果我们没有给线程设置名字,线程也是有默认的名字的
    • 格式: Thread-X(X序号,从0开始的)
    • 如果我们要给线程设置名字,可以用set方法进行设置,也可以构造方法设置
  • currentThread() 细节
    • 当JVM虚拟机启动之后,会自动的启动多条线程其中有一条线程就叫做main线程
    • 他的作用就是去调用main方法,并执行里面的代码
    • 在以前,我们写的所有的代码,其实都是运行在main线程当中
  • sleep( long time)细节
    • 哪条线程执行到这个方法,那么哪条线程就会在这里停留对应的时间
    • 方法的参数:就表示睡眠的时间,单位毫秒
    • 当时间到了之后,线程会自动的醒来,继续执行下面的其他代码
package thread;

public class Dome4 {
    public static void main(String[] args) throws InterruptedException {
        //可以在类中重写构造方法来实现给多线程赋值
        
        thread4 tr1 = new thread4("aaa");
        thread4 tr2 = new thread4("bbb");

        Thread tt1 = new Thread(tr1);
        Thread tt2 = new Thread(tr2);

        tt1.start();
        tt2.start();
     
        System.out.println(Thread.currentThread().getName());//main

        System.out.println("asaaaaaaaaaaaaaaaaaaaaaa");
        //可以时jvm机睡眠 n毫秒
        Thread.sleep(5000);
        System.out.println("bbbbbbbbbbbbbbbbbbbbbbbb");
        Thread.sleep(5000);
        System.out.println("dddddddddddddddddddddddd");


    }
}

方法名称说明
setPriority( int newPriority)设置线程的优先级
final int getPriority()获取线程的优先级
  • 线程的优先级默认是5(包括main线程)
  • 改变了优先级只是先执行的概率变高了,并不是绝对先执行
package thread;

public class Dome5 {
    public static void main(String[] args) {
         thread2 t1 = new thread2();
         thread2 t2 = new thread2();

         Thread tt1 = new Thread(t1);
         Thread tt2 = new Thread(t2);

         tt1.setName("aaaa");
         tt2.setName("bbbb");
        //System.out.println(tt1.getPriority());//5
        //System.out.println(tt2.getPriority());//5

        //改变了优先级只是先执行的概率变高了,并不是绝对先执行
        tt1.setPriority(1);
        tt2.setPriority(10);

        tt1.start();
        tt2.start();
    }
}
package thread;

public class thread2 implements Runnable {
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            //获取到当前线程的对象
            /*
            Thread t = Thread.currentThread();
            System.out.println(t.getName()+"hello world");
            */
            //第二种获取对象的方式
            System.out.println(Thread.currentThread().getName()+"hello world");
        }
    }
}

方法名称说明
final void setDaemon( boolean on)设置为守护线程
  • 当其他的非守护线程执行完毕之后,守护线程会陆续结束
  • 通俗易懂:当女神线程结束了,那么备胎也没有存在的必要了
package thread;

public class Dome6 {
    public static void main(String[] args) {
        thread1 t1 = new thread1();
        thread1_1 t2 = new thread1_1();

        Thread t11 = new Thread(t1);
        Thread t22 = new Thread(t2);

        t22.setDaemon(true);//创建一个守护线程,当t11执行完毕时t22也会陆续结束

        t11.start();
        t22.start();
    }
}
package thread;

public class thread1 extends Thread {
    @Override
    public void run() {
        //书写线程要执行的代码
        for (int i = 0; i < 10; i++) {
            System.out.println(getName() +"你好啊");
        }
    }
}
package thread;

public class thread1_1 extends Thread{
    @Override
    public void run() {
        //书写线程要执行的代码
        for (int i = 0; i < 100; i++) {
            System.out.println(getName() +"你不好吗");
        }
    }
}
方法名称说明
public static void yield()出让线程/礼让线程
public static void join()插入线程/插队线程
  • 尽可能地让结果均匀,由于是让出当前CPU的执行权,多线程再次抢夺执行权时,同样可能再次被原来线程抢夺到
package thread;

public class Dome7 {
    public static void main(String[] args) {
        thread5 t1 = new thread5();
        thread5 t2 = new thread5();

        Thread tt1 = new Thread(t1);
        Thread tt2 = new Thread(t2);

        tt1.start();
        tt2.start();
    }
}
package thread;

public class thread5 extends Thread {
    @Override
    public void run() {
        //书写线程要执行的代码
        for (int i = 0; i < 10; i++) {
            System.out.println(getName() +"你不好吗");
        }
        //表示让出当前CPU的执行权
        Thread.yield();
    }
}

方法名称说明
public static void join()插入线程/插队线程
package thread;

public class Dome8 {
    public static void main(String[] args) throws InterruptedException {
        thread1 t = new thread1();
        Thread t1 = new Thread(t);

        t1.start();
        //表示把t这个线程,插入到当前线程之前。
        //当前线程:main线程
        t1.join();
        /*
        Thread-0你好啊
        Thread-0你好啊
        Thread-0你好啊
        Thread-0你好啊
        Thread-0你好啊
        Thread-0你好啊
        Thread-0你好啊
        Thread-0你好啊
        Thread-0你好啊
        Thread-0你好啊
        你好
        你好
        你好
        你好
        你好
        * */
        //当前执行在mian线程下
        for (int i = 0; i < 5; i++) {
            System.out.println("你好");
        }
    }
}
package thread;

public class thread1_1 extends Thread{
    @Override
    public void run() {
        //书写线程要执行的代码
        for (int i = 0; i < 100; i++) {
            System.out.println(getName() +"你不好吗");
        }
    }
}

1.5、线程的生命周期

在这里插入图片描述

  • sleep()方法执行结束之后会编程就绪状态

1.6、线程安全的问题

把操作共享数据的代码锁起来

格式:

synchronized(){
	操作共享数据的代码
}

特点1: 锁默认打开,有一个线程进去了,锁自动关闭
特点2: 里面的代码全部执行完毕,线程出来,锁自动打开

  • 细节:
    • synchronized锁的代码必须在循坏外面,否则会导致一个线程执行完所有的代码
    • synchronized锁对象一定要是唯一的,一般使用当前类的字节码文件:类.class

如果没有代码会出现的问题:

  1. 静态变量会发生重复
  2. 静态变量会超出方法里给限定的范围
  • 问题原因:
    • 线程执行时,会有随机性
package threadTest;

public class test1 {
    public static void main(String[] args) {
        test1_thead t1 = new test1_thead();
        test1_thead t2 = new test1_thead();
        test1_thead t3 = new test1_thead();

        t1.setName("窗口1");
        t2.setName("窗口2");
        t3.setName("窗口3");

        t1.start();
        t2.start();
        t3.start();
    }
}
package threadTest;

public class test1_thead extends Thread{
    static int ticket = 0;

    static Object obj = new Object();
    @Override
    public void run() {

            while (true){
                synchronized(test1_thead.class){
                if (ticket<100){
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                    ticket++;
                    System.out.println(getName()+"正在销售第"+ticket+"张票");
                }else {
                    break;
                }
            }
        }

    }
}

1.7、同步方法

同步方法
就是把synchronized关键字加到方法上

  • 格式:
修饰符 synchronized 返回值类型 方法名(方法参数){...}

特点1: 同步方法是锁住方法里面所有的代码

特点2: 锁对象不能自己指定

  • 非静态:this

  • 静态:当前类的字节码文件对象

  • StringBuilder和StringBuffer的区别

    • StringBuffer是线程安全的
    • 两者的方法是完全相同的
package threadTest;

public class test2 {
    public static void main(String[] args) {
        test2_thread tt = new test2_thread();

        Thread t1 = new Thread(tt);
        Thread t2 = new Thread(tt);
        Thread t3 = new Thread(tt);

        t1.setName("窗口一");
        t2.setName("窗口二");
        t3.setName("窗口三");

        t1.start();
        t2.start();
        t3.start();
    }
}
package threadTest;

public class test2_thread implements Runnable{

    int ticket = 0;

    @Override
    public void run() {
            while (true){
                if (monted())break;
            }
        }
        public synchronized boolean monted(){
            if (ticket == 100){
                return true;
            }else {
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                ticket++;
                System.out.println(Thread.currentThread().getName()+"正在售卖第"+ticket+"张票");
            }
        return false;
    }
}

1.8、lock锁

虽然我们可以理解同步代码块和同步方法的锁对象问题,
但是我们并没有直接看到在哪里加上了锁,在哪里释放了锁,
为了更清晰的表达如何加锁和释放锁,JDK5以后提供了一个新的锁对象LOCK

Lock实现提供比使用synchronized方法和语句可以获得更广泛的锁定操作
Lock中提供了获得锁和释放锁的方法

  • void lock(): 获得锁
  • void unlock(): 释放锁

Lock是接口不能直接实例化,这里采用它的实现类ReentrantLock来实例化
ReentrantLock的构造方法
ReentrantLock(): 创建一个ReentrantLock的实例

package threadTest;

public class test2 {
    public static void main(String[] args) {
        test2_thread tt = new test2_thread();

        Thread t1 = new Thread(tt);
        Thread t2 = new Thread(tt);
        Thread t3 = new Thread(tt);

        t1.setName("窗口一");
        t2.setName("窗口二");
        t3.setName("窗口三");

        t1.start();
        t2.start();
        t3.start();
    }
}
package threadTest;

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

public class test1_thead extends Thread{
    static int ticket = 0;
    Lock lock = new ReentrantLock();

    @Override
    public void run() {

        while (true){
            lock .lock();
            try {
                if (ticket<100){
                    Thread.sleep(100);
                    ticket++;
                    System.out.println(getName()+"正在销售第"+ticket+"张票");
                }else {
                    break;
                }
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }finally {
                lock.unlock();

            }
        }

    }
}

1.9、死锁

  • 两个锁嵌套,导致无法出锁
package threadTest;

public class test3 {
    public static void main(String[] args) {
         test3_thread t1 = new test3_thread();
         test3_thread t2 = new test3_thread();

         t1.setName("线程A");
         t2.setName("线程B");

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

    }
}
package threadTest;

public class test3_thread extends Thread {

    static Object A = new Object();
    static Object B = new Object();

    @Override
    public void run() {

        while (true) {
            if ("线程A".equals(getName())){
                synchronized (A){
                    System.out.println("线程A拿到了A锁,准备拿B锁");

                    synchronized (B){
                        System.out.println("线程A拿到了B锁,顺利执行完一轮");
                    }
                }
            }else if ("线程B".equals(getName())){

                if ("线程B".equals(getName())){
                    synchronized (B){
                        System.out.println("线程B拿到了B锁,准备拿A锁");
                        synchronized (A){
                            System.out.println("线程B拿到了A锁,顺利执行完一轮");
                        }
                    }
                }
            }
        }
    }
}

1.10、生产者和消费者(等待唤醒机制)

生产者消费者模式是一个十分经典的多线程协作的模式

在这里插入图片描述
常见方法

方法名称说明
void wait()当前线程等待,直到被其他线程唤醒
void notify()随机唤醒单个线程
void notifyAll()唤醒所有线程
package threadTest;

public class test4 {
    public static void main(String[] args) {
        test4_Foodie f = new test4_Foodie();
        test4_Cook c = new test4_Cook();

        f.start();
        c.start();
    }
}
package threadTest;

    public class test4_Desk extends Thread {
        //总个数
        public static int count = 10;
        //食物标志
        public static int foodflog = 1;
        //锁对象
        public static Object lock = new Object();

}
package threadTest;

public class test4_Cook extends Thread{
    @Override
    public void run() {
        while (true){
            synchronized (test4_Desk.lock){
                if (test4_Desk.count==0){
                    break;
                }else {
                    //判断桌子上是否有面条
                    if (test4_Desk.foodflog == 1){
                        try {
                            test4_Desk.lock.wait();
                        } catch (InterruptedException e) {
                            throw new RuntimeException(e);
                        }
                    }else {
                        //制作食物
                        System.out.println("厨师正在制作食物");
                        //制作结束唤醒吃货
                        test4_Desk.lock.notifyAll();
                        //改变桌子状态
                        test4_Desk.foodflog = 1;
                    }
                }
            }
        }
    }
}
package threadTest;

public class test4_Foodie extends Thread {
    @Override
    public void run() {
        while (true){
            synchronized (test4_Desk.lock){
                if (test4_Desk.count==0){
                    break;
                }else {
                    //判断桌子上是否有面条
                    if (test4_Desk.foodflog == 0){
                        try {
                            test4_Desk.lock.wait();
                        } catch (InterruptedException e) {
                            throw new RuntimeException(e);
                        }
                    }else {
                        //把吃的总数-1
                        test4_Desk.count--;
                        //如果有
                        System.out.println("吃货还能吃"+test4_Desk.count+"碗面条");
                        //吃完之后唤醒厨师
                        test4_Desk.lock.notifyAll();
                        //改变桌子的状态
                        test4_Desk.foodflog = 0 ;
                    }
                }
            }
        }
    }
}

1.11、等待唤醒机制(阻塞队列方式实现)

在这里插入图片描述
put数据时:放不进去,会等着,也叫做阻塞
take数据时:取出第一个数据,取不到会等着,也叫做阻塞。

生产者和消费者必须使用同一个队列

package threadTest;

import java.util.concurrent.ArrayBlockingQueue;

public class test5 {
    public static void main(String[] args) {
        ArrayBlockingQueue<String> queue  = new ArrayBlockingQueue<>(1);//需要指定上限

        test5_Cook c = new test5_Cook(queue);
        test5_Fooder f = new test5_Fooder(queue);

        c.start();
        f.start();

    }
}
package threadTest;

import java.util.concurrent.ArrayBlockingQueue;

public class test5_Cook extends Thread {

    ArrayBlockingQueue<String> queue;

    test5_Cook(ArrayBlockingQueue<String> queue){
        this.queue = queue;
    }

    @Override
    public void run() {
        while (true){
            try {
                queue.put("面条");
                System.out.println("厨师制作了一碗面条");
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }
}
package threadTest;

import java.util.concurrent.ArrayBlockingQueue;

public class test5_Fooder extends Thread{
    ArrayBlockingQueue<String> queue;

    test5_Fooder(ArrayBlockingQueue<String> queue){
        this.queue = queue;
    }

    @Override
    public void run() {
        while (true){
            try {
                queue.take();
                System.out.println("吃货吃了一碗面条");
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }
}

1.12、线程的状态

在这里插入图片描述
在这里插入图片描述

二、综合练习

2.1、卖电影票

在这里插入图片描述

package thread_Integrated_exercises;

public class test1 {
    public static void main(String[] args) {
        test1_thread t1 = new test1_thread();
        test1_thread t2 = new test1_thread();

        t1.setName("窗口一");
        t2.setName("窗口二");

        t1.start();
        t2.start();
    }
}
package thread_Integrated_exercises;

import java.util.EmptyStackException;

public class test1_thread extends Thread {

    static int tacket = 0;

    @Override
    public void run() {
        while (true){
            synchronized (test1_thread.class){
                if (tacket==1000) {
                    break;
                }else {
                    tacket++;
                    try {
                        Thread.sleep(3000);
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                    System.out.println(getName()+"正在销售第"+tacket+"张票");
                }
            }
        }
    }
}

2.2、送礼品

在这里插入图片描述

package thread_Integrated_exercises.test2;

public class test2_ {
    public static void main(String[] args) {
        test2_thread t1 = new test2_thread();
        test2_thread t2 = new test2_thread();

        t1.setName("用户一");
        t2.setName("用户二");

        t1.start();
        t2.start();
    }
}
package thread_Integrated_exercises.test2;

public class test2_thread extends Thread{
    static int giving = 100;

    @Override
    public void run() {
        while (true){
            synchronized (test2_thread.class){
                if (giving<10){
                    break;
                }else {
                    try {
                        sleep(100);
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                    System.out.println(getName()+"送出"+"第"+giving+"份礼物");
                    giving--;
                    System.out.println("还剩"+giving+"份礼物");
                }
            }
        }
    }
}

2.3、打印奇数数字

在这里插入图片描述

package thread_Integrated_exercises.test3;

public class test3_ {
    public static void main(String[] args) {
        test3_thread t1 = new test3_thread();
        test3_thread t2 = new test3_thread();


        Thread tt1 = new Thread(t1);
        Thread tt2 = new Thread(t2);

        tt1.start();
        tt2.start();
    }
}
package thread_Integrated_exercises.test3;

public class test3_thread implements Runnable {

    static int num = 100;

    @Override
    public void run() {
        while (true){
            synchronized (test3_thread.class){
                if (num<=0){
                    break;
                }else {
                    if (num%2==0){
                        num--;
                        continue;
                    }else {
                        System.out.println(num);
                        num--;
                    }
                }
            }
        }
    }
}

2.4、抢红包

在这里插入图片描述

package thread_Integrated_exercises.test4;

public class test4_ {
    public static void main(String[] args) {
        test4_thread t1 = new test4_thread();
        test4_thread t2 = new test4_thread();
        test4_thread t3 = new test4_thread();
        test4_thread t4 = new test4_thread();
        test4_thread t5 = new test4_thread();

        t1.setName("用户1");
        t2.setName("用户2");
        t3.setName("用户3");
        t4.setName("用户4");
        t5.setName("用户5");

        t1.start();
        t2.start();
        t3.start();
        t4.start();
        t5.start();
    }
}
package thread_Integrated_exercises.test4;

import java.util.Random;

public class test4_thread extends Thread{
    //设置红包个数
    static int count = 3;
    //设置红包的大小
    static double money = 100;
    //设置最小金额
    final double MIN = 0.01;

    private double prize;
    @Override
    public void run() {
        synchronized (test4_thread.class){
            if (count==0){
                //如果红包没了则显示没有抢到红包
                System.out.println(getName()+"没抢到红包");
            }else {
                if (count==1){
                    //如果红包就剩一个了则全部的都是最后一个用户的红包
                    prize = money;
                }else {
                    Random r = new Random();
                    //判断随机数的范围
                    //要留出最小金额
                    double bounds = money - (count -1)*MIN;
                    prize = r.nextDouble(bounds);
                    //当金额小于最小金额时则抢到的金额就是最小的金额
                    if (prize<MIN){
                         prize = MIN;
                    }
                }
                money = money - prize;
                count--;
                System.out.println(getName()+"抢到了"+prize+"元");
            }
        }
    }
}

类的第二种写法(精确金额)

package thread_Integrated_exercises.test4;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Random;

public class test4_thread2 extends Thread {
    //设置红包个数
    static int count = 3;
    //设置红包的大小
    static BigDecimal money = BigDecimal.valueOf(100.0);
    //设置最小金额
    final BigDecimal MIN = BigDecimal.valueOf(0.01);


    @Override
    public void run() {
        synchronized (test4_thread.class){
            BigDecimal prize;
            if (count==0){
                //如果红包没了则显示没有抢到红包
                System.out.println(getName()+"没抢到红包");
            }else {
                if (count==1){
                    //如果红包就剩一个了则全部的都是最后一个用户的红包
                    prize = money;
                }else {
                    Random r = new Random();
                    //判断随机数的范围
                    //要留出最小金额
                    double bounds = money.subtract(BigDecimal.valueOf(count-1).multiply(MIN)).doubleValue();
                    //抽奖金额
                    prize = BigDecimal.valueOf(r.nextDouble(bounds));
                    //当金额小于最小金额时则抢到的金额就是最小的金额
                    if (prize.equals(MIN)){
                        prize = MIN;
                    }
                }
                //设置抽中红包,小数点后保留两位
                prize = prize.setScale(2, RoundingMode.HALF_UP);
                money = money.subtract(prize);
                count--;
                System.out.println(getName()+"抢到了"+prize+"元");
            }
        }
    }
}

2.5、抽奖箱抽奖

在这里插入图片描述

package thread_Integrated_exercises.test5;

import java.util.ArrayList;
import java.util.Collections;

public class test_ {
    public static void main(String[] args) {
        ArrayList<Integer> list = new ArrayList<>();

        Collections.addAll(list,100,200,20,600,30,100,2000,800,60,30,770,20,1100,260,50,40,300,90,60);


        test_thread t1 = new test_thread(list);
        test_thread t2 = new test_thread(list);


        t1.setName("抽奖箱一");
        t2.setName("抽奖箱二");

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

    }
}
package thread_Integrated_exercises.test5;

import java.util.ArrayList;
import java.util.Collections;

public class test_thread extends Thread{
    ArrayList<Integer> list;

    public test_thread(ArrayList<Integer> list){
        this.list = list;
    }
    @Override
    public void run() {
        while (true){
            synchronized (test_thread.class){
                if (list.size() == 0){
                    break;
                }else {
                    Collections.shuffle(list);
                    int prize =  list.remove(0);
                    System.out.println(getName()+"抽到了"+prize+"元");
                }
            }
            try {
                sleep(100);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }
}

2.6、多线程统计并求最大值

在这里插入图片描述

package thread_Integrated_exercises.test6;


import java.util.ArrayList;
import java.util.Collections;

public class tset6_ {
    public static void main(String[] args) {
        ArrayList<Integer> list = new ArrayList<>();

        Collections.addAll(list,100,200,20,600,30,100,2000,800,60,30,770,20,1100,260,50,40,300,90,60);

        test6_thread t1 = new test6_thread(list);
        test6_thread t2= new test6_thread(list);

        t1.setName("抽奖箱一");
        t2.setName("抽奖箱二");

        t1.start();
        t2.start();
    }
}
package thread_Integrated_exercises.test6;

import java.util.ArrayList;
import java.util.Collections;

public class test6_thread extends Thread{
    ArrayList<Integer> list;


    public test6_thread(ArrayList<Integer> list){
        this.list = list;
    }
    @Override
    public void run() {
        ArrayList<Integer> list_ = new ArrayList<>();
        while (true){
            synchronized (test6_thread.class){
                if (list.size() == 0){
                        System.out.println(getName()+list_);
                        System.out.println(getName()+"最大值是:"+Collections.max(list_));
                        int sum = 0;
                        for (Integer integer : list_) {
                            sum = sum + integer;
                        }
                        System.out.println("总和是"+sum);
                    break;
                }else {
                    Collections.shuffle(list);
                    int prize =  list.remove(0);
                    //System.out.println(getName()+"抽到了"+prize+"元");
                    list_.add(prize);
                }
            }
            try {
                sleep(100);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }
}

2.7、多线程统计并求最大值

在这里插入图片描述

package thread_Integrated_exercises.test7;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

public class test7_ {
    public static void main(String[] args) throws ExecutionException, InterruptedException {

        ArrayList<Integer> list = new ArrayList<>();
        Collections.addAll(list,100,200,20,600,30,100,2000,800,60,30,770,20,1100,260,50,40,300,90,60);


        test7_thread t1 = new test7_thread(list);
        test7_thread t2 = new test7_thread(list);

        FutureTask<Integer> ft1 = new FutureTask<>(t1);
        FutureTask<Integer> ft2 = new FutureTask<>(t2);

        Thread tt1 = new Thread(ft1);
        Thread tt2 = new Thread(ft2);

        tt1.setName("抽奖箱一");
        tt2.setName("抽奖箱二");

        tt1.start();
        tt2.start();

        Integer max1 = ft1.get();
        Integer max2 = ft2.get();
        int max = 0;
        if (max1>max2){
            max = max1;
        }else {
            max = max2;
        }
        System.out.println(max);
    }
}

package thread_Integrated_exercises.test7;

import java.util.ArrayList;
import java.util.Collections;
import java.util.concurrent.Callable;

public class test7_thread implements Callable<Integer> {
    ArrayList<Integer> list;

    public test7_thread(ArrayList<Integer> list){
        this.list = list;
    }

    @Override
    public Integer call() throws Exception {
        ArrayList<Integer> list_ = new ArrayList<>();
        while (true){
            synchronized (test7_thread.class){
                if (list.size() == 0){
                    System.out.println(Thread.currentThread().getName()+list_);
                    System.out.println(Thread.currentThread().getName()+"最大值是:"+ Collections.max(list_));
                    int sum = 0;
                    for (Integer integer : list_) {
                        sum = sum + integer;
                    }
                    System.out.println("总和是"+sum);
                    break;
                }else {
                    Collections.shuffle(list);
                    int prize = list.remove(0);
                    //System.out.println(getName()+"抽到了"+prize+"元");
                    list_.add(prize);
                }
            }
            Thread.sleep(100);
        }
        if (list_==null){
            return null;
        }else {
            return Collections.max(list_);
        }
    }
}

三、线程池

3.1、线程池概述

  • 以前写多线程的弊端
    • 用到线程的时候就创建
    • 用完之后线程消失

线程池主要核心原理

  1. 创建一个池子,池子中是空的
  2. 提交任务时,池子会创建新的线程对象,任务执行完毕,线程归还给池子。下回再次提交任务时,不需要创建新的线程,直接复用已有的线程即可
  3. 但是如果提交任务时,池子中没有空闲线程,也无法创建新的线程,任务就会排队等待
    Executors: 线程池的工具类通过调用方法返回不同类型的线程池对象

创建线程池方法:

方法名称说明
public static Executorservice newCachedThreadPool()创建一个没有上限的线程池(int的上限)
public static ExecutorService newFixedThreadPool(int nThreads)创建有上限的线程池
package threadPool.Dome1;

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

public class Dome {
    public static void main(String[] args) throws InterruptedException {
        //获取线程池对象
        ExecutorService pool1 = Executors.newCachedThreadPool();
        //提交任务
        pool1.submit(new myThread());
        pool1.submit(new myThread());
        pool1.submit(new myThread());
        pool1.submit(new myThread());
        pool1.submit(new myThread());
        pool1.submit(new myThread());

        //获取有限的线程池
        ExecutorService pool2 = Executors.newFixedThreadPool(3);
        //无论添加多少个线程,线程池中都只有3个线程
        pool2 .submit(new myThread());
        pool2 .submit(new myThread());
        pool2 .submit(new myThread());
        pool2 .submit(new myThread());

        //销毁线程池
        pool1.shutdown();

        pool2.shutdown();
    }
}
package threadPool.Dome1;

public class myThread implements Runnable{
    @Override
    public void run() {
        for (int i = 1; i <= 100; i++) {
            System.out.println(Thread.currentThread().getName()+"======"+"在打印"+i);
        }
    }
}

3.2、自定义线程池

在这里插入图片描述

任务拒绝策略

任务拒绝策略说明
ThreadPoolExecutor.AbortPolicy默认策略: 丢弃任务并抛出RejectedExecutionException异常
ThreadPoolExecutor.DiscardPolicy丢弃任务,但是不抛出异常这是不推荐的做法
ThreadPoolExecutor.Discardoldestpolicy抛弃队列中等待最久的任务然后把当前任务加入队列中
ThreadPoolExecutor.CallerRunsPolicy调用任务的run()方法绕过线程池直接执行
package threadPool.Dome2;

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class Dome {
    public static void main(String[] args) {
        /*
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor
        (核心线程数量,最大线程数量,空闲线程最大存活时间,任务队列,创建线程工厂,任务的拒绝策略)

        参数一:核心线程数量             不能小于e
        参数二:最大线程数               不能小于e,最大数量>=核心线程数量
        参数三:空闲线程最大存活时间       不能小于0
        参数四:时间单位                  用TimeUnit指定
        参数五:任务队列                 不能为null
        参数六:创建线程工厂              不能为null
        参数七:任务的拒绝策略            不能为null
         */
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
                3,//核心线程数量
                6,//线程总数量
                60,//空闲线程最大存活时间
                TimeUnit.SECONDS,//时间单位
                new ArrayBlockingQueue<>(3),//任务队列
                Executors.defaultThreadFactory(),//创建线程工厂
                new ThreadPoolExecutor.AbortPolicy()//任务的拒绝策略
        );

        threadPoolExecutor.submit(new Thread());
    }
}

不断的提交任务,会有以下三个临界点:

  1. 当核心线程满时,再提交任务就会排队
  2. 当核心线程满,队伍满时,会创建临时线程
  3. 当核心线程满,队伍满,临时线程满时,会触发任务拒绝策略

最大并行数:

  • 就是处理器的最大线程数
    可以通过Runtime.getRuntime().availableProcessors()获取

线程池多大合适

  • CPU密集型运算

    • 最大并行数+1
  • IO密集型运算

    • 最大并行数 * 期望CPU利用率 *总时间(CPU计算时间+等待时间)/CPU计算时间
  • thread dump 可以计算出CPU计算时间和等待时间

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值