ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

多线程学习

2021-06-28 21:35:42  阅读:124  来源: 互联网

标签:Thread void 学习 线程 new 多线程 public card


1.进程与线程

  • 进程:正在运行的程序,是系统进行资源分配的基本单位。

    • 目前操作系统都是支持多进程,可以同时执行多个进程,通过进程ID(process ID-PID)区分。
    • 单核CPU在同一个时刻,只能运行一个进程;宏观并行、微观串行。
  • 线程,又称轻量级进程(Light Weight Process)。进程中的一条执行路径,也是CPU的基本调度单位。

    • 一个进程由一个或多个线程组成,彼此间完成不同的工作,同时执行,称为多线程。
    • 迅雷是一个进程,当中的多个下载任务即是多个线程。
    • Java虚拟机是一个进程,当中默认包含主线程((main) ,可通过代码创建多个独立线程,与main并发执行。
  • 进程与线程的区别:

    • 1、进程是操作系统资源分配的基本单位,而线程是CPU的基本调度单位。
    • 2、一个程序运行后至少有一个进程。
    • 3、一个进程可以包含多个线程,但是至少需要有一个线程,否则这个进程是没有意义。
    • 4、进程间不能共享数据段地址,但同进程的线程之间可以。
  • 线程的组成:

    • 任何一个线程都具有基本的组成部分:

    • CPU时间片:操作系统(0S)会为每个线程分配执行时间。

    • 运行数据:

      • 堆空间:存储线程需使用的对象,多个线程可以共享堆中的对象。

      • 栈空间:存储线程需使用的局部变量,每个线程都拥有独立的栈。

    • 线程的逻辑代码。

  • 线程的特点:

    • 1、线程抢占式执行。
      • 效率高
      • 可防止单一线程长时间独占CPU
    • 2、在单核CPU中,宏观上同时执行,微观上顺序执行。

2.创建线程

  • 创建线程三种方式
    • 1、【继承Thread类,重写run方法】

    • 2、【实现Runnable接口】

    • 3、【实现Callable接口】

2.1【继承Thread类,重写run方法】,有四个步骤

  • 继承Thread类
  • 覆盖run()方法
  • 创建子类对象
  • 调用start()方法
//获取线程ID和线程名称:
1、在Thread的子类中调用this.getId()或this.getName ()
2、使用Thread.currentThread().getId()和Thread. currentThread().getName ()
//修改线程名称,线程ID不能修改
1、调用线程对象的setName()方法。
2、使用线程子类的构造方法赋值。
/**
 * 线程类
 */
public class MyThread extends Thread{
    public MyThread() {
    }

    public MyThread(String name) {
        super(name);//把name传给父类Thread的构造函数
    }

    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
//            方法1:this.getId()或this.getName ()
//            this.getID:获取线程ID
//            this.getName:获取线程名称
//            System.out.println("线程ID:"+this.getId()+"线程名称:" +this.getName()+
//                    "子线程.........."+i);
//            方法2:Thread.currentThread():获取当前线程
            System.out.println("线程ID:"+ Thread.currentThread().getId() +
                    "线程名称:"+Thread.currentThread().getName()+"子线程.........."+i);
        }
    }
}
public class Test {
    public static void main(String[] args) {
        //1.创建线程对象
        MyThread myThread=new MyThread();
        //2.启动线程,不能调用run()方法,在main()方法中调用run()方法相当于调用对象的一个普通方法
        myThread.setName("我的子线程1");//修改线程名字,要在线程启动之前修改,线程ID不能修改
        myThread.start();

        MyThread myThread1=new MyThread("我的子线程2");
//        myThread.setName("我的子线程2");
        myThread1.start();

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

实现四个窗口各卖100张票的功能:

/**
 * 买票窗口类
 */
public class TicketWin extends Thread{
    private int count=100;

    public TicketWin() {
    }

    public TicketWin(String name) {
        super(name);
    }

    @Override
    public void run() {
       //实现卖票功能
        while (true){
            if(count<=0){
                break;
            }
            System.out.println(Thread.currentThread().getName()+"卖了第"+count+"张票");
            count--;
        }
    }
}
//测试类
public class TestWin {
    public static void main(String[] args) {
        //创建四个线程对象
        TicketWin tw1=new TicketWin("1号窗口");
        TicketWin tw2=new TicketWin("2号窗口");
        TicketWin tw3=new TicketWin("3号窗口");
        TicketWin tw4=new TicketWin("4号窗口");
        //启动线程
        tw1.start();
        tw2.start();
        tw3.start();
        tw4.start();
    }
}

2.2【实现Runnable接口】,有四个步骤

  • 实现Runnable接口
  • 覆盖run()方法
  • 创建实现类的对象
  • 创建线程对象
  • 调用start()方法
public class MyRunnable implements Runnable {
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println(Thread.currentThread().getName()+"........"+i);
        }
    }
}

//测试类
public class TestMyRunnable {
    public static void main(String[] args) {
//        //创建MyRunnable接口,表示线程要执行的功能
//        MyRunnable mr=new MyRunnable();
//        Thread thread=new Thread(mr,"我的线程1");
//        thread.start();
//        for (int i = 0; i < 500; i++) {
//            System.out.println("main..........."+i);
//        }
        //只是用MyRunnable一次的话,代码有些冗长,使用匿名内部类进行优化
    Thread thread=new Thread(new Runnable(){
        @Override
        public void run() {
            for (int i = 0; i < 100; i++) {
                System.out.println(Thread.currentThread().getName()+"........"+i);
            }
        }
    },"我的线程1");
        thread.start();
        for (int i = 0; i < 500; i++) {
            System.out.println("main..........."+i);
        }
    }
}

实现四个窗口一共卖100张票的功能:

/**
 * 票类(共享资源类)
 */
public class TicketWin2 implements Runnable{
    public int ticket=100;
    @Override
    public void run() {
        while (true){
            if (ticket<=0){
                break;
            }
            System.out.println(Thread.currentThread().getName()+"卖了第"+ticket+"张票");
            ticket--;
        }
    }
}

//测试类
public class TestWin2 {
    public static void main(String[] args) {
        TicketWin2 tw1=new TicketWin2();
        Thread thread1=new Thread(tw1,"1号窗口");
        Thread thread2=new Thread(tw1,"2号窗口");
        Thread thread3=new Thread(tw1,"3号窗口");
        Thread thread4=new Thread(tw1,"4号窗口");
        thread1.start();
        thread2.start();
        thread3.start();
        thread4.start();
    }
}
//这样执行会出现线程安全问题

你和你女朋友共用一张银行卡,你向卡中存钱,你女朋友从卡中取钱,使用程序模拟过程:

/**
 * 银行卡类
 */
public class BankCard {
    private double money;

    public double getMoney() {
        return money;
    }

    public void setMoney(double money) {
        this.money = money;
    }
}

//存钱
public class AddMoney implements Runnable{
    private BankCard card;

    public AddMoney() {
    }

    public AddMoney(BankCard card) {
        this.card = card;
    }

    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            card.setMoney(card.getMoney()+1000);
            System.out.println(Thread.currentThread().getName()+"存了1000,余额是:"+card.getMoney());
        }
    }
}

//取钱
public class SubMoney implements Runnable{
    private BankCard card;

    public SubMoney() {
    }

    public SubMoney(BankCard card) {
        this.card = card;
    }

    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            if (card.getMoney()>=1000) {
                card.setMoney(card.getMoney() - 1000);
                System.out.println(Thread.currentThread().getName() + "取了1000,余额是:" + card.getMoney());
            }else{
                System.out.println("余额不足");
                i--;
            }
            }
    }
}

//测试类
public class Test {
    public static void main(String[] args) {
        //1.创建一张银行卡
        BankCard bankCard=new BankCard();
        //2.创建存钱,取钱
        AddMoney add=new AddMoney(bankCard);//可以使用匿名内部类简化代码
        SubMoney sub=new SubMoney(bankCard);
        //3.创建线程
        Thread thread1=new Thread(add,"晨晨");//new Thread(add,"晨晨").start()
        Thread thread2=new Thread(sub,"冰冰");
        //4.启动线程
        thread1.start();
        thread2.start();
    }
}

3.线程的状态

image-20210404173246225

image-20210404211640418

注:JDK1.5之后,就绪和运行统称为Runnable

4.线程常用方法

//休眠: 当前线程主动休眠millis毫秒。
public static void sleep(long millis)
//放弃:当前线程主动放弃时间片,回到就绪状态,竞争下一次时间片。
public static void yield()
//加入:允许其他线程加入到当前线程中。
public final void join()
   
//修改线程优先级:
   //线程对象.setPriority();线程优先级为1-10,默认为5,优先级越高,表示获取CPU机会越多。
   
//守护线程:
   //线程对象.setDaemon(true);设置为守护线程。
   //线程有两类:用户线程(前台线程)、守护线程(后台线程)。
   //如果程序中所有前台线程都执行完毕了,后台线程会自动结束。
   //垃圾回收器线程属于守护线程。

//休眠
public class SleepThread extends Thread{
  @Override
  public void run() {
      for (int i = 0; i < 10; i++) {
          System.out.println(Thread.currentThread().getName()+"......"+i);
          try {
              Thread.sleep(1000);//休眠1000ms
              //这个异常不能抛出,因为这个父类没有抛出异常
              // 子类继承父类,子类不能抛出比父类更多更宽的异常
          } catch (InterruptedException e) {
              e.printStackTrace();
          }
      }
  }
}

//测试类
public class TestSleep {
  public static void main(String[] args) {
      SleepThread sleepThread=new SleepThread();
      sleepThread.start();
      SleepThread sleepThread1=new SleepThread();
      sleepThread1.start();
  }
}
//放弃
public class YieldThread extends Thread{
    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            System.out.println(Thread.currentThread().getName()+"......"+i);
            //主动放弃CPU
            Thread.yield();
        }
    }
}

//测试类
public class TestYield {
    public static void main(String[] args) {
        YieldThread yieldThread=new YieldThread();
        YieldThread yieldThread1=new YieldThread();
        yieldThread.start();
        yieldThread1.start();
    }
}
//加入线程
public class JoinThread extends Thread {
    @Override
    public void run() {
        for (int i = 0; i < 30; i++) {
            System.out.println(Thread.currentThread().getName()+"........"+i);
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

//测试类
public class TestJoin {
    public static void main(String[] args) {
        JoinThread jt=new JoinThread();
        jt.start();

        try {
            jt.join();//加入当前线程(main主线程),并阻塞当前线程,直到加入线程执行完毕
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        for (int i = 0; i < 20; i++) {
            System.out.println(Thread.currentThread().getName()+"=========="+i);
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
//修改线程优先级
public class PriorityThread extends Thread{
   @Override
    public void run(){
       for (int i = 0; i < 50; i++) {
           System.out.println(Thread.currentThread().getName()+"........"+i);
       }
    }
}

//测试类
public class TestPriority {
    public static void main(String[] args) {
        PriorityThread pt1=new PriorityThread();
        PriorityThread pt2=new PriorityThread();
        PriorityThread pt3=new PriorityThread();

        pt1.setPriority(1);
        pt3.setPriority(10);

        pt1.start();
        pt2.start();
        pt3.start();

    }
}
//守护线程
public class DaemonThread extends Thread{
    @Override
    public void run() {
        for (int i = 0; i < 50; i++) {
            System.out.println(Thread.currentThread().getName()+"........"+i);
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

//测试类
public class TestDaemon {
    public static void main(String[] args) {
        //1.创建线程(默认是前台线程)
        //主线程也属于前台线程
        DaemonThread d1=new DaemonThread();
        //设置线程为守护线程(后台线程)
        //前台线程执行完毕,后台线程自动结束
        d1.setDaemon(true);
        d1.start();

        for (int i = 0; i < 50; i++) {
            System.out.println(Thread.currentThread().getName()+"=========="+i);
            try {
                Thread.sleep(200);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

5.线程安全问题

image-20210404204443370

  • 需求:A线程将“Hello”存入数组;B线程将“World”存入数组。
    • A线程查找到了下标0,但还没来得及将数据存放进去,时间片到期,然后B线程查找到下标0并将数据存放了进去,然后A线程继续执行,把数据存放到下标0处,出现了数据覆盖的问题。
  • 多线程安全问题:
    • 当多线程并发访问临界资源时,如果破坏原子操作,可能会造成数据不一致。
    • 临界资源:共享资源(同一对象),一次仅允许一个线程使用,才可保证其正确性。
    • 原子操作:不可分割的多步操作,被视作一个整体,其顺序和步骤不可打乱或缺省。
/**
 * 线程安全问题示例代码
 * 两个线程同时向一个数组中存放数据
 */
public class ThreadSafe {
    private static int index=0;
    public static void main(String[] args) throws Exception{
        //创建数组
        String[] s=new String[5];
        //创建两个操作
        Runnable runnableA=new Runnable() {
            @Override
            public void run() {
                s[index]="hello";
                index++;
            }
        };
        Runnable runnableB=new Runnable() {
            @Override
            public void run() {
                s[index]="world";
                index++;
            }
        };
        //创建两个线程对象
        Thread t1=new Thread(runnableA,"A");
        Thread t2=new Thread(runnableB,"B");
        t1.start();
        t2.start();
        t1.join();
        t2.join();
        System.out.println(Arrays.toString(s));
    }
}

//执行结果
//[world, null, null, null, null]

5.1同步机制

5.1.1同步方式1:同步代码块

  • //同步代码块
    synchronized(临界资源对象){//对临界资源对象加锁
        //代码(原子操作)
    }
    //注:
    	//每个对象都有一个互斥锁标记,用来分配给线程的。
    	//只有拥有对象互斥锁标记的线程,才能进入对该对象加锁的同步代码块。
    	//线程退出同步代码块时,会释放相应的互斥锁标记。
    
    
    /**
     * 线程安全问题
     * 两个线程同时向一个数组中存放数据
     */
    public class ThreadSafe {
        private static int index=0;
        public static void main(String[] args) throws Exception{
            //创建数组
            String[] s=new String[5];
            //创建两个操作
            Runnable runnableA=new Runnable() {
                @Override
                public void run() {
                    synchronized (s){
                        s[index]="hello";
                        index++;
                    }
                }
            };
            Runnable runnableB=new Runnable() {
                @Override
                public void run() {
                    synchronized (s){
                        s[index]="world";
                        index++;
                    }
                }
            };
            //创建两个线程对象
            Thread t1=new Thread(runnableA,"A");
            Thread t2=new Thread(runnableB,"B");
            t1.start();
            t2.start();
            t1.join();
            t2.join();
            System.out.println(Arrays.toString(s));
        }
    }
    //执行结果一定可以把hello和world写入数组,先后顺序不一定
    
    //使用同步代码块来优化:四个窗口一共卖100张票
    /**
     * 票类(共享资源类)
     */
    public class TicketWin2 implements Runnable{
        public int ticket=100;
        //创建锁
        private Object obj=new Object();
        @Override
        public void run() {
            while (true){
                synchronized (obj){//不可以使用synchronized (new Object),因为在main进程中创建了四个进程,相当于创建了四把锁
                    //可以使用synchronized (this),可以使用当前对象作为锁
                    if (ticket<=0){
                        break;
                    }
                    System.out.println(Thread.currentThread().getName()+"卖了第"+ticket+"张票");
                    ticket--;
                }
            }
        }
    }
    
    //测试类
    public class TestWin2 {
        public static void main(String[] args) {
            TicketWin2 tw1=new TicketWin2();
            Thread thread1=new Thread(tw1,"1号窗口");
            Thread thread2=new Thread(tw1,"2号窗口");
            Thread thread3=new Thread(tw1,"3号窗口");
            Thread thread4=new Thread(tw1,"4号窗口");
            thread1.start();
            thread2.start();
            thread3.start();
            thread4.start();
        }
    }
    
//使用同步代码块来优化:你和你女朋友共用一张银行卡,你向卡中存钱,你女朋友从卡中取钱,使用程序模拟过程:
/**
 * 银行卡类
 */
public class BankCard {
    private double money;

    public double getMoney() {
        return money;
    }

    public void setMoney(double money) {
        this.money = money;
    }
}

//存钱
public class AddMoney implements Runnable{
    private BankCard card;

    public AddMoney() {
    }

    public AddMoney(BankCard card) {
        this.card = card;
    }

    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            synchronized (card) {
                card.setMoney(card.getMoney() + 1000);
                System.out.println(Thread.currentThread().getName() + "存了1000,余额是:" + card.getMoney());
            }
        }
    }
}

//取钱
public class SubMoney implements Runnable{
    private BankCard card;

    public SubMoney() {
    }

    public SubMoney(BankCard card) {
        this.card = card;
    }

    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            synchronized (card){
            if (card.getMoney()>=1000) {
                card.setMoney(card.getMoney() - 1000);
                System.out.println(Thread.currentThread().getName() + "取了1000,余额是:" + card.getMoney());
            }else {
                System.out.println("余额不足");
                i--;
            }
            }
    }
}

//测试类
public class Test {
    public static void main(String[] args) {
        //1.创建一张银行卡
        BankCard bankCard=new BankCard();
        //2.创建存钱,取钱
        AddMoney add=new AddMoney(bankCard);//可以使用匿名内部类简化代码
        SubMoney sub=new SubMoney(bankCard);
        //3.创建线程
        Thread thread1=new Thread(add,"晨晨");//new Thread(add,"晨晨").start()
        Thread thread2=new Thread(sub,"冰冰");
        //4.启动线程
        thread1.start();
        thread2.start();
    }
}

5.1.2 同步方式2:同步方法

  • 同步方法:
synchronized 返回值类型 方法名称 (形参列表){//对当前对象(this)加锁
    //代码(原子操作)
}
注:只有拥有对象互斥锁标记的线程,才能进入该对象加锁的同步方法中。
    线程退出同步方法时,会释放相应的互斥锁标记。

//以四个窗口共卖100张票为例
/**
 * 票类(共享资源类)
 */
public class TicketWin2 implements Runnable{
    public int ticket=100;
    //创建锁
    private Object obj=new Object();
    @Override
    public void run() {
        while (true){
            if(!sale()){
                break;
            }         
            }
        }
    }
//同步方法:

    public synchronized boolean sale(){//锁是当前对象,this
//  public synchronized static boolean sale(){//锁是当前类,Ticket.class
        if (ticket<=0){
                    return false;
                }
                System.out.println(Thread.currentThread().getName()+"卖了第"+ticket+"张票");
                ticket--;
        		return true;
    }
}

//测试类
public class TestWin2 {
    public static void main(String[] args) {
        TicketWin2 tw1=new TicketWin2();
        Thread thread1=new Thread(tw1,"1号窗口");
        Thread thread2=new Thread(tw1,"2号窗口");
        Thread thread3=new Thread(tw1,"3号窗口");
        Thread thread4=new Thread(tw1,"4号窗口");
        thread1.start();
        thread2.start();
        thread3.start();
        thread4.start();
    }
}

5.2同步规则

  • 注意:
    • 只有在调用包含同步代码块的方法,或者同步方法时,才需要对象的锁标记。
    • 如调用不包含同步代码块的方法,或普通方法时,则不需要锁标记,可直接调用。
  • 已知JDK中线程安全的类:
    • StringBuffer
    • Vector
  • Hashtable
    • 以上类中的公开方法,均为synchonized修饰的同步方法。

5.3死锁

  • 死锁;

    • 当第一个线程拥有A对象锁标记,并等待B对象锁标记,同时第二个线程拥有B对象锁标记,并等待A对象锁标记时,产生死锁。
    • 一个线程可以同时拥有多个对象的锁标记,当线程阻塞时,不会释放已经拥有的锁标记,由此可能造成死锁。
    /**
     * 创建两个锁对象
     */
    public class MyLock {
        //两个锁
        public static Object a=new Object();
        public static Object b=new Object();
    }
    
    //男孩线程
    public class Boy extends Thread{
        @Override
        public void run() {
            synchronized (MyLock.a){
                System.out.println("男孩拿到了a");
                synchronized (MyLock.b){
                    System.out.println("男孩可以吃东西了");
                }
            }
        }
    }
    
    //女孩线程
    public class Girl extends Thread{
        @Override
        public void run() {
            synchronized (MyLock.b){
                System.out.println("女孩拿到了b");
                synchronized (MyLock.a){
                    System.out.println("女孩可以吃东西了");
                }
            }
        }
    }
    
    //测试类
    public class TestDeadLock {
        public static void main(String[] args) {
            Boy boy=new Boy();
            Girl girl=new Girl();
            boy.start();
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            girl.start();
        }
    }
    

5.4线程通信

  • 等待:
public final void wait ()
public final void wait(long timeout)
//必须在对obj加锁的同步代码块中。在一个线程中,调用obj.wait() 时,此线程会释放其圳有的所有锁标记。同时此线程阻塞在o的等待队列中。释放锁,进入等待队列。
  • 通知
public final void notify()//随机唤醒一个在等待队列中的线程
public final void notifyAll() //随机所有在等待队列中的线程
//银行卡类
public class BankCard {
    //余额
    private double money;
    private boolean flag=false;//true表示有钱,可以取钱;false表示没钱,可以存钱
    //存钱
    public synchronized void sava(double m){
        while (flag){//有钱
            try {
                this.wait();//进入等待队列,同时释放锁
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        money=money+m;
        System.out.println(Thread.currentThread().getName()+"存了"+m+"余额是"+money);
        //修改标记
        flag=true;
        //唤醒存钱线程
        this.notifyAll();
    }
    //取钱
    public synchronized void take(double m){
        while (!flag){//if(!flag):只判断一次
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        money=money-m;
        System.out.println(Thread.currentThread().getName()+"取了"+m+"余额是"+money);
        //修改标记
        flag=false;
        //唤醒取钱线程
        this.notifyAll();//this.notify():只唤醒一个,多线程执行会出现死锁
    }
}

//存钱操作
public class AddMoney implements Runnable{
    private BankCard card=new BankCard();

    public AddMoney(BankCard card) {
        this.card = card;
    }

    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            card.sava(1000);
        }
    }
}

//取钱操作
public class SubMoney implements Runnable{
    private BankCard card=new BankCard();

    public SubMoney(BankCard card) {
        this.card = card;
    }

    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            card.take(1000);
        }
    }
}

//测试类
public class Test {
    public static void main(String[] args) {
        //1.创建银行卡
        BankCard card=new BankCard();
        //2.创建操作
        AddMoney add=new AddMoney(card);
        SubMoney sub=new SubMoney(card);
        //3.创建线程对象
        Thread chenchen=new Thread(add,"晨晨");
        Thread bingbing=new Thread(sub,"冰冰");
        Thread mingming=new Thread(add,"明明");
        Thread lili=new Thread(sub,"丽丽");
        //4.启动线程
        chenchen.start();
        bingbing.start();
        mingming.start();
        lili.start();
    }
}
  • 生产者、消费者问题:

    若干个生产者在生产产品,这些产品将提供给若干个消费者去消费,为了使生产者和消费者能并发执行,在两者之间设置一个能存储多个产品的缓冲区,生产者将生产的产品放入缓冲区中,消费者从缓冲区中取走产品进行消费,显然生产者和消费者之间必须保持同步,即不允许消费者到一个空的缓冲区中取产品,也不允许生产者向一个满的缓冲区中放入产品。

//面包类
public class Bread {
    private int id;
    private String productName;

    public Bread() {
    }

    public Bread(int id, String productName) {
        this.id = id;
        this.productName = productName;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getProductName() {
        return productName;
    }

    public void setProductName(String productName) {
        this.productName = productName;
    }
}

//存放面包的数组
public class BreadCon {
    //存放面包的数组
    private Bread[] cons=new Bread[6];
    //存放面包的位置
    private int index=0;
    public synchronized void input(Bread b){//锁this
        //判断容器有没有满
        while (index>5){
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        cons[index]=b;
        System.out.println(Thread.currentThread().getName()+"生产了"+b.getId()+ "");
        index++;
        //唤醒
        this.notifyAll();
    }

    public synchronized void output(){//锁this
        //判断容器有没有满
        while (index<=0){
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        index--;
        Bread b=cons[index];
        System.out.println(Thread.currentThread().getName()+"消费了"+b.getId()+ "生产者:"+b.getProductName());
        cons[index]=null;
        //唤醒生产者
        this.notifyAll();
    }

}

//生产操作
public class Produce implements Runnable{
    private BreadCon breadCon;

    public Produce(BreadCon breadCon) {
        this.breadCon = breadCon;
    }

    @Override
    public void run() {
        for (int i = 0; i <6; i++) {
            breadCon.input(new Bread(i,Thread.currentThread().getName()));
        }
    }
}

//消费操作
public class Consume implements Runnable{
     private BreadCon breadCon;

    public Consume(BreadCon breadCon) {
        this.breadCon = breadCon;
    }

    @Override
    public void run() {
        for (int i = 0; i < 3; i++) {
            breadCon.output();
        }
    }
}

//测试类
public class Test {
    public static void main(String[] args) {
        //容器
        BreadCon breadCon=new BreadCon();
        //创建操作:生产和消费
        Produce produce=new Produce(breadCon);
        Consume consume=new Consume(breadCon);
        //创建生产者和消费者
        Thread thread1=new Thread(produce, "北京的生产者");
        Thread thread2=new Thread(produce, "上海的生产者");
        Thread thread3=new Thread(consume, "南京的消费者");
        Thread thread4=new Thread(consume, "天津的消费者");
        Thread thread5=new Thread(consume, "太原的消费者");

        //启动线程
        thread1.start();
        thread2.start();
        thread3.start();
        thread4.start();
        thread5.start();

    }
}

标签:Thread,void,学习,线程,new,多线程,public,card
来源: https://www.cnblogs.com/Ng3061851/p/14946380.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有