多线程详解

news2024/12/23 17:00:37

多线程详解

Process和Thread

  • 程序是指令和数据的有序结合,其本身没有任何运行的含义,是一个静态的概念

  • 进程是执行程序的一次执行过程,是一个动态的概念,是系统资源分配的单位

  • 通常在一个进程中可以包含若干个线程。线程是CPU调度和执行的单位

核心概念

  • 线程就是独立的执行路径

  • 在程序运行的时候,即使没有自己创建线程,后台也会有多个线程

  • main()称之为主线程,为系统的入口,用于执行整个程序

  • 在一个进程中,如果开辟了多个线程,线程的运行由调度器安排调度。

    调度器是与操作系统紧密相关的,先后顺序是不能人为的干预的

  • 对同一份资源操作时,会存在资源抢夺问题,需要加入并发控制。

  • 线程会带来额外的开销,如CPU调度时间,并发控制开销

  • 每个线程在自己的工作内存交互,内存控制不当会造成数据不一致

三种创建方式

  • 继承Thread类(重点)

    • 启动程序:子类对象:.start()

    • 不建议使用:避免OOP单继承局限性

  • 实现Runnable接口(重点)

    • 启动线程:传入目标对象+Thread对象.start()

    • 推荐使用:避免单继承局限性,灵活方便,方便同一个对象被多个线程使用

  • 实现Callable接口

继承Thread类

  • 自定义线程继承Thread类

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

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

//创建线程方式一:1 继承Thread类  2重写run()方法 3 调用start开启线程
public class TestThread1 extends Thread{
    //重写run()方法
    @Override
    public void run() {
        for (int i = 0; i <50 ; i++) {
            System.out.println("我在睡觉"+i);
        }
    }
    public static void main(String[] args) {
        //创建一个线程对象
        TestThread1 testThread1=new TestThread1();
        //调用一个start()方法开始线程
        testThread1.start();
        for (int i = 0; i <100 ; i++) {
            System.out.println("我在吃饭"+i);
        }
    }
}

网图下载

//练习用Thread,实现多线程同步下载图片
public class TestThread2 extends  Thread{
    private  String url;
    private  String name;
    public  TestThread2( String url,String name){
        this.url=url;
        this.name=name;
    }
    //线程执行体
    @Override
    public void run() {
        WebDownLoader webDownLoader=new WebDownLoader();
        webDownLoader.downloader(url,name);
        System.out.println("下载了文件名为"+name);
    }
    public static void main(String[] args) {
        TestThread2 t1 =new TestThread2("https://t7.baidu.com/it/u=1819248061,230866778&fm=193&f=GIF","2.jpg");
        TestThread2 t2 =new TestThread2("https://img1.baidu.com/it/u=3776011775,176672726&fm=253&fmt=auto&app=138&f=JPEG?w=889&h=500","3.jpg");
        TestThread2 t3=new TestThread2("https://t7.baidu.com/it/u=963301259,1982396977&fm=193&f=GIF","4.jpg");
      t1.start();
      t2.start();
      t3.start();
    }
}
//下载器
class WebDownLoader{
    //下载方法
    public void  downloader (String url,String name) {
        try {
            FileUtils.copyURLToFile(new URL(url),new File(name) );
        } catch (IOException e) {
           e.printStackTrace();
            System.out.println("io异常,downloader方法出现问题");
        }
    }
}

 

实现Runnable接口

  • 定义MyRunnable类接口实现Runnable接口

  • 实现run()方法,编写线程执行体

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

//创建线程的方式二 1 实现Runnable接口 2 重写run方法,执行线程需要丢入runnable接口实现类,调用start方法
public class TestThread3 implements Runnable{
    //重写run()方法
    @Override
    public void run() {
        for (int i = 0; i <50 ; i++) {
            System.out.println("我在睡觉"+i);
        }
    }
    public static void main(String[] args) {
       //创建runnable接口的实现类对象
        TestThread3 testThread3=new TestThread3();
        //创建线程对象,通过线程对象来开启线程(代理)
        new Thread(testThread3).start();
        for (int i = 0; i <100 ; i++) {
            System.out.println("我在吃饭"+i);
        }
    }
}

 

初识并发问题

//多个线程操作同一个对象时
//多个线程操作同一个对象时,线程不安全,数据紊乱
public class TestThread4 implements Runnable{
    private int ticketNums=10;
    @Override
    public void run() {
        while(true){
            if (ticketNums<=0){
                break;
            }
            //模拟延时
            try {
                Thread.sleep(200);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            System.out.println(Thread.currentThread().getName()+"--》拿到了第"+ticketNums--+"张票");
        }
    }
    public static void main(String[] args) {
        TestThread4 ticket=new TestThread4();
         new Thread(ticket,"花海").start();
         new Thread(ticket,"枫").start();
         new Thread(ticket,"林夕").start();
    }
}

 

龟兔赛跑

//模拟龟兔赛跑
public class Race implements Runnable{
    //胜利者
    private String winner;
    @Override
    public void run() {
        //赛道
        for (int i = 0; i <= 100; i++) {
            //兔子睡觉
            if (Thread.currentThread().getName().equals("兔子")&&(0==i%50)){
                try {
                    Thread.sleep(1);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
            //判断游戏是否结束
            boolean flag=gameOver(i);
            if (flag){
                break;
            }
            System.out.println(Thread.currentThread().getName()+"跑了"+i+"步");
        }
    }
    //判断游戏胜利
    public boolean gameOver(int steps){
        if (winner!=null){//已经出现胜利者
            return true;
        }else{
            if (steps>=100){
                winner=Thread.currentThread().getName();
                System.out.println("winner is:"+winner);
            }
        }
        return false;
    }
    public static void main(String[] args) {
        Race race=new Race();
        new Thread(race,"兔子").start();
        new Thread(race,"乌龟").start();
    }
}

实现Callable接口

 

//创建线程方式三  实现Callable接口
//可以定义返回值
//可以抛出异常
public class TestCallable implements Callable<Boolean> {
    private  String url;
    private  String name;
    public  TestCallable( String url,String name){
        this.url=url;
        this.name=name;
    }
    //线程执行体
    @Override
    public Boolean call() {
        WebDownLoader1 webDownLoader=new WebDownLoader1();
        webDownLoader.downloader(url,name);
        System.out.println("下载了文件名为"+name);
        return true;
    }
    public static void main(String[] args) throws  Exception {
        TestCallable t1 =new TestCallable("https://t7.baidu.com/it/u=1819248061,230866778&fm=193&f=GIF","2.jpg");
        TestCallable t2 =new TestCallable("https://img1.baidu.com/it/u=3776011775,176672726&fm=253&fmt=auto&app=138&f=JPEG?w=889&h=500","3.jpg");
        TestCallable t3=new TestCallable("https://t7.baidu.com/it/u=963301259,1982396977&fm=193&f=GIF","4.jpg");
       //创建执行服务
        ExecutorService ser= Executors.newFixedThreadPool(3);
        //提交执行
        Future<Boolean> result1= ser.submit(t1);
        Future<Boolean> result2= ser.submit(t2);
        Future<Boolean> result3= ser.submit(t3);
        //获取结果
        boolean results1=result1.get();
        boolean results2=result1.get();
        boolean results3=result1.get();
       //关闭服务
        ser.shutdownNow();
    }
}
//下载器
class WebDownLoader1{
    //下载方法
    public void  downloader (String url,String name) {
        try {
            FileUtils.copyURLToFile(new URL(url),new File(name) );
        } catch (IOException e) {
            e.printStackTrace();
            System.out.println("io异常,downloader方法出现问题");
        }
    }
}

静态代理

//静态代理模式
//真实对象和代理对象都要实现同一个接口
//代理对象要代理真实角色
//好处;1 代理对象可以做真实对象做不了的事情  2  真实对象做自己的事
public class TestProxy {
    public static void main(String[] args) {
        WeddingCompany weddingCompany =new WeddingCompany(new You());
        weddingCompany.HappyMarry();
    }
}
interface  Marry{
    void  HappyMarry();
}
//真实角色(你结婚)
class You implements Marry{
    @Override
    public void HappyMarry() {
        System.out.println("林夕");
    }
}
//代理角色(婚庆公司,帮助你结婚)
class  WeddingCompany implements  Marry{
    private Marry target;

    public WeddingCompany(Marry target) {
        this.target = target;//真实对象
    }
    @Override
    public void HappyMarry() {
        before();
        this.target.HappyMarry();
        after();
    }
    private void after() {
        System.out.println("打扫现场");
    }
    private void before() {
        System.out.println("布置现场");
    }
}

Lamda模式

  • 使用Lamda表达式的原因

    • 避免匿名内部类定义过多

    • 可以使代码看起来简洁

  • Functional Interface(函数式接口)

    • 定义:任何接口,如果只含有唯一一种抽象方法,那麽他就是一个函数式接口

    • 对于函数式接口,我们可以通过Lamda表达式来创建该接口的对象

//推导Lamda表达式
public class TestLamda1 {
    //静态内部类
    static  class  Like2 implements ILike{
        @Override
        public void lamda() {
            System.out.println(" i like Lamda2");
        }
    }

    public static void main(String[] args) {
        ILike like = new Like();
        like.lamda();
        like=new Like2();
        like.lamda();
        //局部内部类
        class  Like3 implements ILike{
            @Override
            public void lamda() {
                System.out.println(" i like Lamda3");
            }
        }
        like=new Like3();
        like.lamda();
        //匿名内部类;没有类的名称,必须借助接口或者父类
        like=new ILike() {
            @Override
            public void lamda() {
                System.out.println(" i like Lamda4");
            }
        };
          like.lamda();
          //Lamda简化
        like=()-> {
            System.out.println(" i like Lamda5");
        };
        like.lamda();
    }

}
//定义一个函数式接口
interface ILike{
    void lamda();
}
//实现类
class  Like implements ILike{
    @Override
    public void lamda() {
        System.out.println(" i like Lamda");
    }
}
public class TestLamda2 {
    public static void main(String[] args) {
        ILove love = (int a)-> {
                System.out.println("i love"+a);
            };
        love.love(520);
        }
    }
interface ILove{
    void love(int a);
}

线程状态

 

停止线程

//停止线程
//1 推荐线程自己停下来  2建议使用一个标志位进行终止变量,当flag=false时,线程终止执行
public class TestStop implements Runnable{
    //创建一个标志位
    private boolean flag=true;
    @Override
    public void run() {
        int i=0;
        while(flag){
            System.out.println("run -----Thread"+i++);
        }
    }
    //设置一个公开的方法停止线程,转换标志位
    public void stop(){
        this.flag=false;
    }
    public static void main(String[] args) {
        TestStop testStop = new TestStop();
        new Thread(testStop).start();
        for (int i = 0; i < 1000; i++) {
            System.out.println("main"+i);
            if (i==900){
                //调用stop方法切换标志位,停止线程
                testStop.stop();
                System.out.println("线程停止");
            }
        }
    }
}

 

线程休眠-Sleep

  • 每一个对象都有一把锁,sleep不会释放锁

    • 模拟网络延时

//模拟网络延时:方法问题的发生性
public class TestThread4 implements Runnable{
    private int ticketNums=10;
    @Override
    public void run() {
        while(true){
            if (ticketNums<=0){
                break;
            }
            //模拟延时
            try {
                Thread.sleep(200);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            System.out.println(Thread.currentThread().getName()+"--》拿到了第"+ticketNums--+"张票");
        }
    }
    public static void main(String[] args) {
        TestThread4 ticket=new TestThread4();
         new Thread(ticket,"花海").start();
         new Thread(ticket,"枫").start();
         new Thread(ticket,"林夕").start();
    }
}
  • 模拟倒计时

//模拟倒计时
public class TestSleep {
    public static void main(String[] args) {
        try {
            tenDown();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
    //模拟倒计时
    public static void tenDown()throws Exception{
        int nums=10;
        while(true){
            Thread.sleep(1000);
            System.out.println(nums--);
            if (nums<=0){
                break;
            }
        }
    }
}

 

线程礼让-Yield

  • 礼让线程:让当前正在执行的线程暂停,但不阻塞

  • 将线程从运行状态转为就绪状态

  • 让CPU重新调度,礼让不一定成功

//礼让不一定成功
public class TestYield {
    public static void main(String[] args) {
        MyYield myYield = new MyYield();
       new Thread(myYield,"A").start();
       new Thread(myYield,"B").start();
    }
}
class MyYield implements Runnable{
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName()+"线程开始执行");
        Thread.yield();//礼让
        System.out.println(Thread.currentThread().getName()+"线程执行结束");
    }
}

线程强制执行-Join

  • Join合并线程,待此线程执行完成后,再执行其他线程,其他线程阻塞(插队

//测试Join方法
public class TestJoin implements  Runnable {
    @Override
    public void run() {
        for (int i = 0; i < 1000; i++) {
            System.out.println("vip"+i);
        }
    }
    public static void main(String[] args) throws Exception{
        TestJoin testJoin=new TestJoin();
        Thread thread=new Thread(testJoin);
        thread.start();
        for (int i = 0; i < 500; i++) {
            if (i==200){
                thread.join();
            }
            System.out.println("main"+i);
        }
    }
}

观测线程状态

 

public class TestState {
    public static void main(String[] args) {
        Thread thread=new Thread(()->{
            for (int i = 0; i < 5; i++) {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
            System.out.println("$$$$$$$$");
        });
        //观察状态
        Thread.State state=thread.getState();
        System.out.println(state);//new
        //观测启动后
        thread.start();
        //启动进程
        state= thread.getState();
        System.out.println(state);//run
        while(state!=Thread.State.TERMINATED){
            //只要线程不终止,就一直输出状态
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            state=thread.getState();//更新线程状态
            System.out.println(state);
        }
    }
}

线程优先级-Priority

  • Java提供一个线程调度器来监控程序中启动后进入就绪状态的所有线程,线程调度器按优先级决定该调度哪个线程执行

  • 优先级低只是意味着获得调度的概率低,并不是优先级低就不会被调用,都是看Cpu的调度

 

//测试线程优先级
public class TestPriority {
    public static void main(String[] args) {
        System.out.println(Thread.currentThread().getName()+"---"+Thread.currentThread().getPriority());
        MyPriority myPriority = new MyPriority();
        Thread t1  = new Thread(myPriority);
        Thread t2 = new Thread(myPriority);
        Thread t3 = new Thread(myPriority);
        Thread t4 = new Thread(myPriority);
        
      //设置优先级,再启动
        t1.start();

        t2.setPriority(Thread.MIN_PRIORITY);
        t2.start();

        t3.setPriority(Thread.MAX_PRIORITY);
        t3.start();

        t4.setPriority(Thread.NORM_PRIORITY);
        t4.start();
    }
}
class MyPriority implements Runnable{
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName()+"---"+Thread.currentThread().getPriority());
    }

守护线程-Daemon

  • 线程分为用户线程和守护线程

  • 虚拟机必须确保用户线程执行完毕

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

 

//测试守护线程
public class TestDaemon {
    public static void main(String[] args) {
        God god = new God();
        Me me = new Me();
        Thread thread = new Thread(god);
        thread.setDaemon(true);
        thread.start();
        Thread thread1 = new Thread(me);
        thread1.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("再见");
    }
}

线程同步机制

  • 队列和锁

  • 由于同一进程的多个线程共享同一块存储空间,在带来方便的同时,也带来了访问冲突问题,为了保正数据在方法中被访问时的正确性,在访问时加入锁机制(synchronized),当一个线程获得对象的排他锁,独占资源,其他线程必须等待,使用后释放锁即可,存在以下问题

    • 一个线程的持有锁会导致其他所有需要此锁的线程挂起

    • 在多线竞争下,加锁,释放锁会导致比较多的上下文切换和调度延时,引起性能问题

    • 如果一个优先级高的线程等待一个优先级低的教程释放锁会导致优先级倒置,引起性能问题

三大不安全事件

//不安全的买票
public class TestTicket {
    public static void main(String[] args) {
        BuyTicket buyTicket=new BuyTicket();
        new Thread(buyTicket,"花海").start();
        new Thread(buyTicket,"枫").start();
        new Thread(buyTicket,"林夕").start();
    }
}
class  BuyTicket implements Runnable{
    private int ticketNums=10;
    boolean flag=true;//外部停止方法
    @Override
    public void run() {
        while (flag){
            buy();
        }
    }
    public  void  buy(){
        //判断是否有票
        if (ticketNums<=0){
            flag=false;
            return;
        }
        //模拟延时
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        //买票
        System.out.println(Thread.currentThread().getName()+"拿到"+ticketNums--);
    }
}
===========================================
    //不安全的取钱
public class TestBack {
    public static void main(String[] args) {
        Account account=new Account(100,"基金");
         Drawing you=new Drawing(account,50,"林夕");
         Drawing her=new Drawing(account,100,"花海");
         you.start();
         her.start();
    }
}
//账户
class  Account{
    int money;//余额
    String name;//名字

    public Account(int money, String name) {
        this.money = money;
        this.name = name;
    }
}
//银行:模拟取钱
class Drawing extends Thread{
    Account account;//账户
    int drawingMoney;//取了多少钱
    int nowMoney;//现在手里多少钱
    public Drawing(Account account,int drawingMoney,String name){
        super(name);
        this.account=account;
        this.drawingMoney=drawingMoney;
    }
    //取钱
    @Override
    public void run() {
        //判断有没有钱
        if (account.money-drawingMoney<0){
            System.out.println(Thread.currentThread().getName()+"你没钱了");
            return;
        }
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        account.money=account.money-drawingMoney;
        nowMoney=nowMoney+drawingMoney;
        System.out.println(account.name+"余额"+account.money);
        System.out.println(this.getName()+"手里"+nowMoney);
    }
}
================================================
    import java.util.ArrayList;
import java.util.List;

public class TestList {
    public static void main(String[] args) {
        List<String > list=new ArrayList<>();
        for (int i = 0; i < 10000; i++) {

            new Thread(() -> {
                list.add(Thread.currentThread().getName());
            }).start();
        }
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        System.out.println(list.size());
    }
   }

同步方法和同步块

同步方法

  • 由于我们可以通过private关键字来保证数据对象只能被方法访问,所以我们只需要针对方法提出一套机制,这套机制就是synchronized关键字,它包括两种方法:synchronized方法和synchronized块

    public synchronized void method(int args){}
  • synchronized方法控制对对象的访问,每个对象对应一把锁,每个synchronized方法都必须调用该方法的对象的锁才能执行,否则线程会阻塞,方法一旦执行,就独占该锁,直到该方法返回才释放锁,后面被阻塞的线程才能获得这个锁,继续执行

同步块

  • synchronized(Obj){}
  • Obj称为同步监视器

    • Obj可以是任何对象,但是推荐使用共享资源作为同步监视器

    • 同步方法中无需指定同步监视器,因为同步方法的同步监视器就是this,就是这个对象本身,或者是class

  • 同步监视器的执行过程

    • 第一个线程访问,锁定同步监视器,执行其中代码

    • 第二个线程访问,发现同步监视器被锁定,无法访问

    • 第一个线程访问完毕,解锁同步监视器

    • 第二个线程访问,发现同步监视器没有锁,然后锁定并访问

死锁

//死锁:多个线程互相抱着对方需要的资源,然后形成僵持
public class TestLock {
    public static void main(String[] args) {
        MakeUp girl1=new MakeUp(0,"林夕");
        MakeUp girl2=new MakeUp(1,"花海");
        girl1.start();
        girl2.start();
    }
}
//口红
class Lipstick{

}
//镜子
class Mirror{}
class MakeUp extends Thread {
    //需要的资源只有一份,用static来保证只有一份

    static Lipstick lipstick = new Lipstick();
    static Mirror mirror = new Mirror();
    int choice;//选择
    String girlName;//使用化妆品的人

    MakeUp(int choice, String girlName) {
        this.choice = choice;
        this.girlName = girlName;
    }

    @Override
    public void run() {
        //化妆
        try {
            makeUp();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
    private void makeUp() throws Exception {
        if (choice == 0) {
            synchronized (lipstick) {//获得口红的锁
                System.out.println(this.girlName + "获得口红的锁");
                }
            Thread.sleep(1000);
            synchronized (mirror) {//一秒钟后想获得镜子的锁
                System.out.println(this.girlName + "获得镜子的锁");
            }
        } else {
            synchronized (mirror) {
                System.out.println(this.girlName + "获得镜子的锁");
                }Thread.sleep(1000);
            synchronized (lipstick) {//一秒钟后想获得口红的锁
                System.out.println(this.girlName + "获得口红的锁");
            }
        }
    }
}

Lock锁

  • 从JDK 5.0开始,JAVA提供了更强大的线程同步机制———通过显式定义同步锁对象来实现同步。同步锁使用Lock对象充当

  • ReentrantLock类实现了Lock,它拥有与synchronized相同的并发性和内存语义,在实现线程安全的控制下,比较常用的是ReentrantLock,可以显式加锁,释放锁

//测试Lock锁
public class TestLock1 {
    public static void main(String[] args) {
        TestLock2 p1=new TestLock2();
        new Thread(p1,"花海").start();
        new Thread(p1,"枫").start();
        new Thread(p1,"林夕").start();
    }
}
class  TestLock2 implements Runnable {
    int ticketNums = 10;
    boolean flag=true;
//定义lock锁
    private final ReentrantLock lock=new ReentrantLock();
    @Override
    public void run() {
        try {
            lock.lock();
            while (flag){
                buy();
            }
        } catch (Exception e) {
            e.printStackTrace();

        }finally {
            lock.unlock();
        }
    }
    public  void buy(){
        if (ticketNums<=0){
            flag=false;
            return;
        } try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
            System.out.println(ticketNums--);
    }
}

synchronized与Lock的对比

  • Lock是显示锁(手动开启和关闭锁),synchronized是隐式锁,除了作用域自动释放

  • Lock只有代码块锁,synchronized有代码块锁和方法锁

  • 使用Lock锁,JVM将花费较少的时间来调度线程,性能更好,并且具有更好的拓展性

线程协作

(生产者消费者问题)

管程法

//测试生产者消费者模型--》利用缓冲区解决
public class TestPc {
    public static void main(String[] args) {
        SynContainer container = new SynContainer();
        new Productor(container).start();
        new Consumer(container).start();
    }
}
//生产者
class Productor extends  Thread {
    SynContainer container;
    public Productor(SynContainer 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  Consumer extends  Thread{
    SynContainer container;
    public Consumer(SynContainer container){
        this.container=container;
    }
    //消费
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println("消费了"+container.pop().id+"只鸡");
        }
    }
}
//产品
class  Chicken {
    int id;
    public Chicken(int id) {
        this.id = id;
    }
}
//缓冲区
class SynContainer{
    //需要一个容器大小
    Chicken[] chickens=new Chicken[10];
    //容器计数器
    int count=0;
    //生产者放入产品
    public synchronized  void push(Chicken chicken){
        //如果容器满了,就要等待消费者消费
        if (count==chickens.length){
            try {
                this.wait();
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            //通知消费者消费,生产等待
        }
        //如果没有满,我们就需要丢入产品
        chickens[count]=chicken;
        count++;
        //可以通知消费者消费了
        this.notifyAll();
    }
    //消费者消费产品
    public synchronized Chicken pop(){
        if (count==0){
            //等待生产者,消费者等待
            try {
                this.wait();
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
        //如果可以消费
        count--;
        Chicken chicken=chickens[count];
        //吃完了,通知生产者生产
        this.notifyAll();
        return chicken;
    }
}

信号灯法

//测试生产者消费者问题:信号灯法
public class TestPc1 {
    public static void main(String[] args) {
        TV tv=new TV();
        new Player(tv).start();
        new Watcher(tv).start();
    }
}
//生产者--演员
class Player extends Thread {
    TV tv;
    public Player(TV tv) {
        this.tv = tv;
    }
    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            if (i%2==0){
                this.tv.play("正在播出");
            }else {
                this.tv.play("广告");
            }
        }
    }
}
//消费者--观众
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 {
    //演员表演,观众等待  T
    //观众观看,演员表演 F
    String  voice;//表演的节目
    boolean flag=true;
    //表演
    public synchronized void play( String  voice){
        if (!flag){
            try {
                this.wait();
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
        System.out.println("演员表演了:"+voice);
        //通知观众观看
        this.notifyAll();//通知唤醒
        this.voice=voice;
        this.flag=!this.flag;
    }
    //观看
    public  synchronized  void watch(){
        if (flag) {
            try {
                this.wait();
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
        System.out.println("观看了"+voice);
        //通知演员表演
        this.notifyAll();
        this.flag=!this.flag;
    }
}

线程池

//测试线程池
public class TestPool {
    public static void main(String[] args) {
        //创建服务,创建线程池
        //newFixedThreadPool  参数为:线程池大小
        ExecutorService service= Executors.newFixedThreadPool(10);
        //执行
        service.execute(new MyThread());
        service.execute(new MyThread());
        service.execute(new MyThread());
        service.execute(new MyThread());
        //关闭链接
        service.shutdown();
    }
}
class  MyThread implements Runnable{

    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName());
    }

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/650842.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

(数组) 922. 按奇偶排序数组 II ——【Leetcode每日一题】

❓922. 按奇偶排序数组 II 难度&#xff1a;简单 给定一个非负整数数组 nums&#xff0c; nums 中一半整数是 奇数 &#xff0c;一半整数是 偶数 。 对数组进行排序&#xff0c;以便当 nums[i] 为奇数时&#xff0c;i 也是 奇数 &#xff1b;当 nums[i] 为偶数时&#xff0c…

开发语言的更新换代,都是为了更好地提高生产力,Kotlin也是如此~

作为一名Android开发&#xff0c;学习Kotlin是很有必要的。以下是一些原因&#xff1a; 1.Kotlin是官方支持的语言。 在2017年Google宣布支持Kotlin作为官方开发语言后&#xff0c;Kotlin已成为Android生态系统的重要组成部分。此举表明Kotlin的发展前景非常广阔&#xff0c;…

uniapp兼容多pda扫描扫码

前景 网上有现成的针对单个pda扫码录入的代码&#xff0c;但是公司的需求是在多个不同厂商pda上运行&#xff0c;这就会导致不同的pda默认的广播动作和广播标签不一致的情况&#xff0c;目前也没有获取这俩字段的api。 单个pda扫描扫码代码 先创建一个scanCode.js的文件 le…

UnoCSS给了我一个不用tailwindcss的理由

相同的原由 & 前言 如果你没有听说过 tailwindcss 或者 unocss&#xff0c;请先 return 先去了解一下&#x1f61d;。 开发上&#xff1a;可能为你甚至你们的前端团队节省很多写样式的时间&#xff0c;也能让你或者你们的项目开发体验有很大提升生产上&#xff1a;你们的…

VS2013创建一个MFC工程的步骤

目录 1、新建项目&#xff0c;选择”MFC应用程序“&#xff1b; 2、应用程序类型&#xff0c;选择“基于对话框”&#xff1b; 3、对话框的标题&#xff0c;默认是和项目的名字一致&#xff0c;按需修改&#xff1b; 4、高级功能&#xff0c;可以保持默认&#xff1b; 5、…

一个女测试工程师的大厂日常

今天给大家分享两个朋友的故事&#xff0c;他们分别在国内两家顶尖的互联网大厂&#xff0c;一个在头条&#xff0c;一个在蚂蚁。 头条的故事 头条的主人公&#xff0c;在入职后的一年里&#xff0c;晚上十点半下班是比较早了&#xff0c;基本上都是十一点半左右下班&#xff…

低成本开发专属小程序支持自定义开发设计

传统定制开发小程序成本高&#xff0c;还需要不断地沟通和交流才能一步步地去实现你想要的功能和效果&#xff0c;那么除了传统的定制开发小程序外&#xff0c;用户其实还可以选择使用模板开发小程序&#xff0c;不仅能实现小程序的所有基础功能&#xff0c;还不用编程维护和开…

进程的描述与控制

文章目录 前趋图和程序执行前趋图程序顺序执行程序并发执行 进程引入目的进程的描述进程的定义进程的特征进程的基本状态与转换挂起操作和进程状态的转换挂起操作引入引入挂起操作后进程3个基本状态间的转换引入挂起操作后进程5个基本状态间的转换进程管理中的数据结构OS中用于…

antdb-upgrade大版本升级介绍

antdb-upgrade pg_upgrade 是postgresql 大版本升级的得力工具。 数据库系统数据部分通过 new version的pg_upgrade自动升级完成数据库用户数据部分&#xff0c;主要有两种用法&#xff1a; 使用pg_upgrade copy物理拷贝方式升级(非copy to/copy from逻辑拷贝)。使用pg_upgra…

内网渗透—隧道搭建SPP与NPS内网穿透

内网渗透—隧道搭建&SPP与NPS内网穿透 1. 前言2. SPP2.1. SPP代理通信2.1.1. 服务端配置2.1.2. 客户端配置2.1.3. CS设置2.1.3.1. 设置生成的监听器2.1.3.2. 设置监听的监听器 2.1.4. 执行效果 2.2. SPP隧道建立2.2.1. 服务端设置2.2.2. 客户端配置2.2.3. CS设置2.2.3.1. 设…

Mybatis学习笔记三

目录 一、MyBatis的缓存1.1 MyBatis的一级缓存1.2 MyBatis的二级缓存1.3 二级缓存的相关配置1.4 MyBatis缓存查询的顺序1.5 整合第三方缓存EHCache&#xff08;了解&#xff09; 二、MyBatis的逆向工程2.1 创建逆向工程的步骤2.2 简单使用查询增改 三、 分页插件3.1 分页插件使…

Endnote解决文章题目Title大小写问题,以及专有名词保持全部大写

在写毕业论文或综述类文章时&#xff0c;需要添加大量参考文献&#xff08;100左右或更多&#xff09;&#xff0c;而每个期刊的要求是不一样的&#xff0c;因此手动输入或修改参考文献的格式是愚蠢的&#xff08;狗头保命&#xff09;&#xff0c;所以需要强大的endnote来进行…

一篇博客搞定C++11之Lambda表达式(附案例代码+解析)

Lambda表达式 1.lambda表达式语法2.捕获列表说明3.lambda表达式实现原理4.具体案例5.总结 1.lambda表达式语法 ambda表达式的语法非常简单&#xff0c;具体定义如下&#xff1a; [ captures ] ( params ) specifiers exception -> ret { body } 先不用急于解读这个定义&…

面向对象内部类

概念 将一个类 A 定义在另一个类 B 里面&#xff0c;里面的那个类 A 就称为内部类 &#xff08;InnerClass&#xff09;&#xff0c;类 B 则称为外部类&#xff08;OuterClass&#xff09; 根据内部类声明的位置&#xff08;如同变量的分类&#xff09;&#xff0c;我们可以分…

首次使用云服务器搭建网站(二)

书接上文&#xff0c;我们已经完成了服务器的租赁&#xff0c;宝塔面板的下载与安装。 接下来我们将正式开始网站搭建。 一、网站创建 点击网站、添加站点 输入网站域名、数据库选择MySQL数据库&#xff0c;选择utf8&#xff0c;数据库账号密码会自动生成。无论你要创建什么样…

【linux网络配置】多个网卡一起使用,一个网卡连内网,一个网卡连外网

一、问题背景 因为有一个工作站在内网中&#xff0c;但是没有办法联网&#xff08;校园网账户有限&#xff09;。 虽然工作站没有联网&#xff0c;但是我仍然可以通过局域网远程控制工作站&#xff0c;使其访问校园网验证页面实现上网。 当给工作站安装软件或依赖项时&#…

SpringBoot配置文件application.properties的理解

一、存放位置分类 1.当前项目根目录下的config目录下 2.当前项目的根目录下 3.resources目录下的config目录下 4.resources目录下 按照这上面的顺序&#xff0c;4个配置文件的优先级依次降低。 我们在项目里面4个位置分别设置了各种的application.properties文件。每个文件…

MySQL查看和修改最大连接数

标题&#xff1a;MySQL查看和修改最大连接数 MySQL 是一种广泛使用的开源关系型数据库管理系统&#xff0c;被许多应用程序用作其后端存储解决方案。在高并发的环境下&#xff0c;MySQL 的最大连接数变得尤为重要。本文将介绍如何查看当前的最大连接数&#xff0c;并详细说明每…

安卓实战开发之——使用 WIFI 进行设备搜索并获取相应信息

目录 一、前言 二、准备条件 三、功能要求 四、显示效果 五、关键代码 一、前言 此玩意是本人很早很早&#xff08;记不清有多早了&#xff0c;反正很早&#xff09;做过的一个课程任务了&#xff0c;无意之中翻到了&#xff0c;所以把它放上来。不愿再找以前写的代码了…

大数据环境搭建 Hadoop+Hive+Flume+Sqoop+Azkaban

目录 零&#xff1a;版本说明一、安装CentOS二、Hadoop单机配置三、Hive安装部署 零&#xff1a;版本说明 Hadoop&#xff1a;3.1.0 CentOS&#xff1a;7.6 JDK&#xff1a;1.8 一、安装CentOS 这里网上教程很多&#xff0c;就不贴图了 【内存可以尽量大一些&#xff0c;不然…