Day14-多线程

多线程

线程简介

进程和线程

进程是执行程序的一次执行过程

  1. 线程是程序执行的最小单位,而进程是操作系统分配资源的最小单位;
  2. 一个进程由一个或多个线程组成,线程是一个进程中代码的不同执行路线
  3. 进程之间相互独立,但同一进程下的各个线程之间共享程序的内存空间(包括代码段,数据集,堆等)及一些进程级的资源(如打开文件和信号等),某进程内的线程在其他进程不可见;

注意

真正的多线程是多cpu,如果只有一个cpu,同一个时间点内只能执行一个代码,切换得快,就有同步执行的错觉

image-20210220211805426

线程实现⭐⭐⭐

线程创建

Runnable接口(重点)

1.自定义线程类实现Runnable接口

2.重写run()方法,编写线程执行体

3.创建线程对象,调用start()方法启动线程

package Thread;

public class RunnableT implements Runnable {        //实现runnable接口

    @Override
    public void run() {                             //重写run方法
        for (int i = 0; i < 200; i++) {
            System.out.println("测试接口");
        }
    }

    public static void main(String[] args) {
        RunnableT runnableT = new RunnableT();      //创建对象
        new Thread(runnableT).start();              //调用start方法
        
        for (int i = 0; i <2000 ; i++) {
            System.out.println("主线程代码");
        }
    }
}
Thread类(实现了Runnable接口)
  1. 自定义线程类继承Thread类
  2. 重写run()方法,编写线程执行体
  3. 创建线程对象,调用start()方法启动线程
package Thread;

public class ThreadClass extends Thread {       //继承线程类
    @Override
    public void run() {                         //重写run方法
        for (int i = 0; i < 20; i++) {
            System.out.println("测试多线程");
        }
    }

    public static void main(String[] args) {
        ThreadClass threadClass = new ThreadClass();
        threadClass.start();                    //调用strat方法
        //正常调用run方法会在run方法执行后,在执行主线程main中的代码,但是start让其变成了交替执行,模拟多线程
        for (int i = 0; i < 2000; i++) {
            System.out.println("区别");
        }
    }
}

重点

1.不推荐使用继承Thread类的方式实现多线程,避免OOP单继承局限性

2.使用Runnable接口,较为灵活,一个对象可被多个线程使用

例子
同时遍历多个文件夹
package Thread;

import java.io.File;

public class ThreadTest extends Thread {
    private String url;
    public ThreadTest(String url){
        this.url = url;
    }

    @Override
    public void run() {
        ImgRead imgRead = new ImgRead();
        imgRead.listFile(new File(url));
    }

    public static void main(String[] args) {
        ThreadTest t1 = new ThreadTest("D:\\code\\JavaSE\\基础语法");
        ThreadTest t2 = new ThreadTest("D:\\code\\vue");
        t1.start();
        t2.start();
    }
}
class ImgRead{
    public static void listFile(File file){
        File[] files = file.listFiles();                //用于存放变量
        System.out.println(file.getAbsoluteFile());     //输出传递参数的绝对地址
        if (files!=null && files.length>0){             //判断是否继续循环,递归出口
            for(File file1:files){                      //遍历文件夹中的文件
                if(file1.isDirectory()){                //递归出口
                    listFile(file1);
                }
            }
        }
    }
}
模拟抢票
package Thread;
//出现问题,多个线程使用同一个资源,数据紊乱
public class RunnableTest implements Runnable {
    private int ticket=10;
    @Override
    public void run() {
        while (true){
            if(ticket<=0){
                break;
            }
            try {
                Thread.sleep(1000);                 //延时,以免cpu速度太快,全部被一个线程抢光
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+"拿到了第"+ticket--+"张票");
        }

    }

    public static void main(String[] args) {
        RunnableTest runnableTest = new RunnableTest();
        new Thread(runnableTest,"学生").start();
        new Thread(runnableTest,"老师").start();
        new Thread(runnableTest,"黄牛").start();
    }
}

龟兔赛跑
package Thread;

public class Race implements Runnable {
    public static String winner;
    @Override
    public void run() {
        for (int i = 1; i <= 2000; i++) {
            //让兔子睡觉
            if(Thread.currentThread().getName().equals("兔子")){
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            //判断胜利者,结束循环
            boolean flage =gameOver(i);
            if(flage){
                break;
            }
            System.out.println(Thread.currentThread().getName()+"跑了"+i+"步");
        }
    }
    //判断是否完成比赛
    public Boolean gameOver(int steps){
        //判断是否有胜利者
        if(winner!=null){
            return true;
        }{
            if(steps==2000){
                winner=Thread.currentThread().getName();
                System.out.println("Winner is "+winner);
                return true;
            }
        }
        return false;
    }

    public static void main(String[] args) {
        Race race = new Race();
        new Thread(race,"兔子").start();
        new Thread(race,"乌龟").start();
    }
}

Callable(了解)

1.实现Callable接口,需要返回值类型

2.重写call方法,需要抛出异常

3.创建目标对象

4.创建执行服务 ExecutorService xx = Executors.newFixedThreadPool(number);

5.提交执行 Future xxx = service.submit(t1);

6.获取结果 T xxxx = xxx.get();

7.关闭服务 xx.shutdownNow

package Thread;

import java.io.File;
import java.util.concurrent.*;

/**
 * 1.实现Callable接口
 * 2.重写call方法
 * 3.创建对象
 * 4.开启服务
 * 5.提交执行
 * 6.获取结果
 * 7.关闭服务
 */
public class CallableT implements Callable<Boolean> {
    private String url;
    public CallableT(String url){
        this.url = url;
    }

    @Override
    public Boolean call() throws Exception {
        ImgRead imgRead = new ImgRead();
        imgRead.listFile(new File(url));
        return true;
    }

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        CallableT t1 = new CallableT("D:\\code\\JavaSE\\基础语法");
        CallableT t2 = new CallableT("D:\\code\\vue");

        //创建执行服务
        ExecutorService service = Executors.newFixedThreadPool(2);

        //提交执行
        Future<Boolean> r1 = service.submit(t1);
        Future<Boolean> r2 = service.submit(t2);

        //获取结果
        boolean re1 = r1.get();
        boolean re2 = r2.get();

        //关闭服务
        service.shutdownNow();
    }
}
class ImgRead{
    public static void listFile(File file){
        File[] files = file.listFiles();                //用于存放变量
        System.out.println(file.getAbsoluteFile());     //输出传递参数的绝对地址
        if (files!=null && files.length>0){             //判断是否继续循环,递归出口
            for(File file1:files){                      //遍历文件夹中的文件
                if(file1.isDirectory()){                //递归出口
                    listFile(file1);
                }
            }
        }
    }
}
特点

1.可以抛出异常

静态代理模式

1.真实对象和代理对象实现同一个接口

2.代理对象要代理真实对象

优点:

1.代理对象可以实现很多真实对象做不了的事情

2.真实对象可以专注于自身

import java.util.Scanner;

public class StaticProxy {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        String str =sc.nextLine();
        Company company = new Company(new You(str));
        company.marry();
        sc.close();
    }
}
interface Marry{
    void marry();
}

class You implements Marry{
    private String name;

    public You(String name) {
        this.name = name;
    }

    @Override
    public void marry() {
        System.out.println(name+"要结婚了");
    }
}

class Company implements  Marry{
    Marry x ;

    public Company(Marry x) {
        this.x = x;
    }

    @Override
    public void marry() {
        before();
        this.x.marry();
        after();
    }

    private void after() {
        System.out.println("婚庆公司收尾款");
    }

    private void before() {
        System.out.println("婚庆公司布置现场");
    }
}

Lamda表达式

函数式编程,避免匿名内部类定义过多,简化代码

函数式接口

只包含一个抽象方法,如Runnable接口,其中只包含一个run方法

public class LambdaT {
    public static void main(String[] args) {
        Test test = new DoTest();       //接口无法直接实例化,所以需要实例化实现类
        test.test(1);

        test=a -> System.out.println("测试Lambda次数:"+a);
        test.test(2);                //当参数只有一个时,可以直接省略括号
                                        //当方法体只有一句时,可以省略花括号

        Test2 test2 = (a,name)->{       //多个参数类型要么全去掉,要么全加上
            System.out.println(a);
            System.out.println(name);
        };
        test2.test(15,"程晨橙");
    }
}
interface Test{
    void test(int a);
}
class DoTest implements Test{

    @Override
    public void test(int a) {
        System.out.println("测试Lambda次数:"+a);
    }
}
interface Test2{
    void test(int a,String name);
}

重点

1.接口必须是函数式接口

2.只有一行语句才能简化成一行,否则使用代码块包裹

3.多个参数时,数据类型要么全去掉,要么全加上

线程状态

image-20210221171533089

package Thread;

public class ThreadState {
    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(()->{
            for (int i = 0; i <5 ; i++) {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println("线程结束");
        }) ;
        Thread.State state = thread.getState();
        System.out.println(state);


        //启动线程
        thread.start();
        state=thread.getState();
        System.out.println(state);

        //线程等待
        for (int i = 0; i <10 ; i++) {
            Thread.sleep(100);
            state=thread.getState();
            System.out.println(state);
        }
        System.out.println("//////");
        state=thread.getState();
        System.out.println(state);
    }
}

线程停止

1.通过次数判断来停止线程

2.通过标志位来停止线程

3.最好不要使用stop等自带的方法来停止线程,已经过时,我们可以自己写一个

public class ThreadStop implements Runnable {
    private boolean flag = true;
    @Override
    public void run() {
        int i = 0;
        while (flag){               //此处如果写成flag=true,程序将会陷入死循环,因为相当于我们重新给flag赋值了true,无法通过stop方法改变标志位的值
            System.out.println("线程正在运行-------"+i++);
        }
    }

    public void stop(){
        this.flag=false;
    }

    public static void main(String[] args) {
        ThreadStop threadStop = new ThreadStop();
        new Thread(threadStop).start();

        for (int i = 0; i <1000 ; i++) {
            System.out.println("主线程执行次数"+i);
            if(i==800){
                threadStop.stop();
                System.out.println("线程停止");
            }
        }
    }
}

线程休眠

1.sleep指定当前线程的阻塞毫秒数

2.sleep存在异常interruptedException,需要抛出

3.sleep时间达到后,线程进入就绪状态,等待cpu调度

4.sleep可以模拟网络延时,倒计时

5.sleep不会释放锁

模拟网络延时

package Thread;
//模拟网络延时:放大问题发生性
public class SleepTest implements Runnable {
    private int ticket=10;
    @Override
    public void run() {
        while (true){
            if(ticket<=0){
                break;
            }
            try {
                Thread.sleep(1000);                 //延时,以免cpu速度太快,全部被一个线程抢光
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+"拿到了第"+ticket--+"张票");
        }

    }

    public static void main(String[] args) {
        SleepTest sleepTest = new SleepTest();
        new Thread(sleepTest,"学生").start();
        new Thread(sleepTest,"老师").start();
        new Thread(sleepTest,"黄牛").start();
    }
}

倒计时

package Thread;

public class ThreadSleep {
    public static void main(String[] args) throws InterruptedException {
        ten();
    }


    public static void ten() throws InterruptedException {
        for (int i = 10; i >0 ; i--) {
            Thread.sleep(1000);
            System.out.println(i);
        }
    }
}

线程礼让

1.让当前正在执行的线程暂停,但不阻塞

2.将线程从运行状态转换位就绪状态

3.让cpu重新调度,礼让不一定成功

package Thread;

public class ThreadYieldTest {
    public static void main(String[] args) {
        MyYield myYield = new MyYield();
        new Thread(myYield,"a").start();    //正常多线程也会出现结果交替现象,这里可以增大几率
        new Thread(myYield,"e").start();
    }
}
class MyYield implements Runnable{

    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName()+"线程开始执行");
        Thread.yield();
        System.out.println(Thread.currentThread().getName()+"线程执行完毕");
    }
}

线程强制执行

合并线程,这个线程执行,其他线程阻塞

package Thread;

public class ThreadJoin implements Runnable {
    @Override
    public void run() {
        for (int i = 0; i <1000 ; i++) {
            System.out.println("插队了"+i);
        }
    }

    public static void main(String[] args) throws InterruptedException {
        ThreadJoin threadJoin = new ThreadJoin();
        Thread thread = new Thread(threadJoin);
        thread.start();
        for (int i = 0; i <1000 ; i++) {
            System.out.println("主线程"+i);
            if(i==200){
                thread.join();
            }
        }
    }
}

线程优先级

使用setPriority(number)设置优先级1-10,数字越大,有越高的概率先执行,先设置再启动线程,默认为5,公平竞争

守护线程

1.线程分为用户线程守护线程

2.虚拟机必须确保用户线程执行完毕,如main

3.虚拟机不用等待守护线程执行完毕

4.守护线程,如垃圾回收,后台操作日志

package Thread;

public class ThreadDaemonTest {
    public static void main(String[] args) {
        God god = new God();
        Me me = new Me();
        Thread thread = new Thread(god);
        thread.setDaemon(true);     //默认为false,用户线程
        thread.start();

        new Thread(me).start();
    }
}
class God implements Runnable{

    @Override
    public void run() {
        while (true){
            System.out.println("上帝永生不死");
        }

    }
}
class Me implements Runnable{

    @Override
    public void run() {
        for (int i = 0; i < 36500; i++) {
            System.out.println("人生不过三万六千五百天");
        }
        System.out.println("GoodBye,World!");
    }
}

线程同步⭐⭐⭐

为保证数据在方法中被访问时的正确性,加入锁机制,当一个线程获得对象的排它锁,独占资源,其他线程必须等待

并发

同一个对象被多个线程同时操作

线程不安全例子

package Thread;

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

public class ThreadUnsafe {
    public static void main(String[] args) throws InterruptedException {
        //取票
        BuyTicket buyTicket = new BuyTicket();
        new Thread(buyTicket,"老师").start();
        new Thread(buyTicket,"学生").start();
        new Thread(buyTicket,"黄牛").start();

        System.out.println();
        System.out.println();
        Thread.sleep(1000);

        //取钱
        Account account = new Account("创业基金",10_0000);
        Blank me = new Blank(account,50000,"我");
        Blank badPeople = new Blank(account,10_0000,"law");
        me.start();
        badPeople.start();

        System.out.println();
        System.out.println();
        Thread.sleep(1000);
        System.out.println();
        System.out.println();

        //不安全的集合
        List<String> list = new ArrayList<>();
        for (int i = 0; i < 10000; i++) {
            new Thread(()->{
                list.add(Thread.currentThread().getName());
            }).start();
        }
        System.out.println(list.size());
    }
}

//买票
class BuyTicket implements Runnable{
    private int ticket = 10;
    private boolean flag = true;
    @Override
    public void run() {
        while (flag){
            try {
                buy();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println("票卖完了");
    }
    public void buy() throws InterruptedException {
        if(ticket<=0){
            flag=false;
            return;
        }
        Thread.sleep(100);
        System.out.println(Thread.currentThread().getName()+"拿到了第"+ticket--+"张票");
    }
}

//取钱
class Account{                                  //账户
    String name;
    int money;

    public Account(String name, int money) {
        this.name = name;
        this.money = money;
    }

}
class Blank extends Thread{
    Account account;    //账户
    int drawMoney;      //取了多少钱
    int nowMoney;       //手上有多少钱

    public Blank(Account account, int drawMoney, String name) {
        super(name);    //继承了thread类,此时代表线程名字
        this.account = account;
        this.drawMoney = drawMoney;
    }

    @Override
    public void run() {
        if(account.money-drawMoney<0){
            System.out.println(Thread.currentThread().getName()+"钱不够,取不了");
            return;
        }
        try {
            Thread.sleep(1000);         //进行等待,放大问题,否则观察不到
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        account.money=account.money-drawMoney;
        nowMoney=nowMoney+drawMoney;
        System.out.println(account.name+"账户余额"+account.money);

        //Thread.currentThread().getName() = this.getName() 因为继承了Thread类
        System.out.println(this.getName()+"手上的钱"+nowMoney);
    }
}

同步方法

使用synchronized关键字,给做出修改的对象上一把锁,默认为当前对象

一般分为两种,针对方法,加一个synchronized修饰符,但当上锁内容较多时,影响效率

​ 针对部分代码,使用代码块的方式

synchronized(对象){
    代码块
}

将例子进行修改

package Thread;

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

public class ThreadUnsafe {
    public static void main(String[] args) throws InterruptedException {
        //取票
        BuyTicket buyTicket = new BuyTicket();
        new Thread(buyTicket,"老师").start();
        new Thread(buyTicket,"学生").start();
        new Thread(buyTicket,"黄牛").start();

        System.out.println();
        System.out.println();
        Thread.sleep(1000);

        //取钱
        Account account = new Account("创业基金",10_0000);
        Blank me = new Blank(account,50000,"我");
        Blank badPeople = new Blank(account,10_0000,"law");
        me.start();
        badPeople.start();

        System.out.println();
        System.out.println();
        Thread.sleep(1000);
        System.out.println();
        System.out.println();

        //不安全的集合
        List<String> list = new ArrayList<>();
        for (int i = 0; i < 10000; i++) {
           synchronized (list){
               new Thread(()->{
                   list.add(Thread.currentThread().getName());
               }).start();
           }
        }
        Thread.sleep(1000);
        System.out.println(list.size());
    }
}

//买票
class BuyTicket implements Runnable{
    private int ticket = 10;
    private boolean flag = true;
    @Override
    public void run() {
        while (flag){
            try {
                Thread.sleep(100);                           //休眠,每次拿完票,都应该停一会,让下一个线程拿票
                buy();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println("票卖完了");
    }
    public synchronized void buy() throws InterruptedException {
        if(ticket<=0){
            flag=false;
            return;
        }
        System.out.println(Thread.currentThread().getName()+"拿到了第"+ticket--+"张票");
    }
}

//取钱
class Account{                                  //账户
    String name;
    int money;

    public Account(String name, int money) {
        this.name = name;
        this.money = money;
    }

}
class Blank extends Thread{
    Account account;    //账户
    int drawMoney;      //取了多少钱
    int nowMoney;       //手上有多少钱

    public Blank(Account account, int drawMoney, String name) {
        super(name);    //继承了thread类,此时代表线程名字
        this.account = account;
        this.drawMoney = drawMoney;
    }

    @Override
    public void run() {
        synchronized (account){
            if(account.money-drawMoney<0){
                System.out.println(Thread.currentThread().getName()+"钱不够,取不了");
                return;
            }
            try {
                Thread.sleep(1000);         //进行等待,放大问题,否则观察不到
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            account.money=account.money-drawMoney;
            nowMoney=nowMoney+drawMoney;
            System.out.println(account.name+"账户余额"+account.money);

            //Thread.currentThread().getName() = this.getName() 因为继承了Thread类
            System.out.println(this.getName()+"手上的钱"+nowMoney);
        }
    }
}

注意:应当在适当的地方让线程进行休眠,否则当cpu速度较快时,会出现诸如票被同个线程抢光的情况

Lock

lock接口是控制多个线程对共享资源进行访问的工具

死锁

都需要对方的资源才能继续运行,导致两个线程都无法执行,死锁

某个同步块同时拥有两个以上的锁,就可能发生死锁情况

public class DeadLock {
    public static void main(String[] args) {
        People people1 = new People("成成陈");
        People people2 = new People("陈琛琛");
        people1.start();
        people2.start();

    }
}
class Mouse{

}
class KeyBoard{

}
class People extends Thread{
    private String name;
    public People(String name){
        this.name=name;
    }
    @Override
    public void run() {
        play();
    }
    //使用static保证只有一份资源
    static Mouse mouse = new Mouse();
    static KeyBoard keyBoard = new KeyBoard();
    private void play(){
        if(name=="陈琛琛"){
            synchronized (mouse){
                System.out.println(this.name+"拿到了鼠标");
                synchronized (keyBoard){
                    System.out.println(this.name+"拿到了键盘");
                }
            }
        }else{
            synchronized (keyBoard){
                System.out.println(this.name+"拿到了键盘");
                synchronized (mouse){
                    System.out.println(this.name+"拿到了鼠标");
                }
            }
        }
    }
}

ReentrantLock 可重入锁

该类实现了Lock,拥有和synchronized相同的并发性和内存语义,在实现线程安全的控制中比较常用

import java.util.concurrent.locks.ReentrantLock;

public class ReentrantLockTest {
    public static void main(String[] args) {
        BuyTickeds buyTickeds = new BuyTickeds();
        new Thread(buyTickeds,"黄牛").start();        //此时会出现一个线程将票全抢完的情况
        new Thread(buyTickeds,"老师").start();
        new Thread(buyTickeds,"学生").start();
    }
}
class BuyTickeds implements Runnable{
    int ticked = 1000;
    private final ReentrantLock lock = new ReentrantLock();
    @Override
    public void run() {
        try {
            lock.lock();            //加锁
            while (true){
                if(ticked>0){
                    System.out.println(Thread.currentThread().getName()+"拿到了第"+ticked--+"张票");
                }else{break;}
            }
        }finally {
            lock.unlock();
        }
    }
}

线程协作

生产者和消费者问题

1.仓库中只能存放一个产品,生成者将产品放入仓库,消费者从仓库中取走产品

2.仓库中没有物品,生产者将产品放入仓库,如果仓库中有产品,生产者停止生产,直到仓库中的产品被取走

3.仓库中有产品,消费者取走产品,仓库中没有产品,等待,直到仓库中有产品

线程通信

生产者和消费者都共享一个资源,且相互影响

原理

1.生产者没有生产出产品时,需要通知消费者等待

2.消费者在取走产品时,需要告诉生产者开始生产

问题

synchronized只能实现同步,不不能实现线程间的消息传递

工具

wait()

表示线程一直等待,直到被其他线程通知,释放锁

notify

唤醒一个处于等待状态的线程

解决方式一:管程法

增加缓冲区,缓冲区存放生产者生产的产品

public class PipeMethod {
    public static void main(String[] args) {
        BufferChicken bufferChicken = new BufferChicken();
        new Producer(bufferChicken).start();
        new Customer(bufferChicken).start();
    }
}
class Producer extends Thread{
    BufferChicken container;
    public Producer(BufferChicken container){
        this.container=container;
    }

    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            container.push(new Chicken(i));
            System.out.println("生产了第"+i+"只鸡");
        }
    }
}
class Customer extends Thread{
    BufferChicken container;
    public Customer(BufferChicken container){
        this.container=container;
    }

    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println("吃了第"+container.pop().id+"只鸡");        //pop方法返回了一个chicken对象,再调用该对象的id,查看是哪一只鸡
        }
    }
}
class Chicken{
    int id;
    public Chicken(int id){
        this.id=id;
    }
}
class BufferChicken{
    //需要一个容器
    Chicken[] chickens = new Chicken[10];
    //容器计数器
    int count = 0;

    //生产者放入产品
    public synchronized void push(Chicken chicken) {
        while (count==chickens.length){
            //产品已满,通知消费者消费
            System.out.println("已满");
            try {
                this.wait();
            }catch (InterruptedException e){
                e.printStackTrace();
            }
        }
        //如果没满则需要生产
        chickens[count] = chicken;
        count++;

        //通知生产者消费
        this.notifyAll();
    }

    //消费者消费产品
    public synchronized Chicken pop(){
        while (count==0){
            //没有产品,等待生产者生产
            System.out.println("没得吃");
            try {
                this.wait();
            }catch (InterruptedException e){
                e.printStackTrace();
            }
        }
        //如果可以消费
        count--;
        Chicken chicken =chickens[count];

        this.notifyAll();
        return chicken;     //返回吃了哪只鸡
    }
}

解决方式二:信号灯法

增加标志位

public class SignalLampMethod {
    public static void main(String[] args) {
        TV tv = new TV();
        new Actor(tv).start();
        new Watcher(tv).start();
    }
}

//生产者--->演员
class Actor extends Thread{
    TV tv;
    public Actor(TV tv){
        this.tv=tv;
    }

    @Override
    public void run() {
        for (int i = 0; i <10 ; i++) {
            if(i%2==0){
                tv.perform("《舞蹈风暴》");
            }else{
                System.out.println("广告之后,精彩继续");
            }
        }
    }
}

//消费者-->观众
class Watcher extends Thread{
    TV tv;
    public Watcher(TV tv){
        this.tv=tv;
    }

    @Override
    public void run() {
        for (int i = 0; i <20 ; i++) {
            tv.watch();
        }
    }
}

//产品-->电视节目
class TV{
    //标志位
    boolean flag = true;

    public synchronized void perform(String voices){
        if (flag){
            //当节目正在播放时,演员进行等待
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println("现在正在播放的是"+voices);
        this.notifyAll();
        this.flag=!flag;
    }

    public synchronized void watch(){
        if(!flag){
            //节目没有开始,进行等待
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        //观看完节目,让演员继续表演
        this.notifyAll();
        this.flag=!flag;
    }
}

线程池

将创建好的多个线程放入线程池中,使用时直接获取,使用完放回池子中,避免频繁的创建和销毁线程

好处

1.提高响应速度

2.降低资源消耗

3.便于线程管理

步骤(Runnable)

1.创建线程池

ExecutorService xx = Executors.newFixedThreadPool(number);

2.执行服务

xx.execute(new xxx());

3.关闭服务

xx.shutdownNow();
posted @ 2021-02-25 17:00  2月2日  阅读(74)  评论(0)    收藏  举报