java学习之多线程

1 线程概述

1.1进程和程序

  • 程序是指令和数据的集合,其本身没有任何运行的含义,是一个静态的概念,换句话说,程序就是一些代码.
  • 而进程是程序执行的一次过程,是一个动态的概念,是系统资源分配的最小单位.
  • 通常一个进程中可以包含若干个线程,线程是CPU调度执行的最小单位.同一个进程里的多个线程共享进程的资源.
    注意: 真正的多线程是指有多个CPU,即多核.

1.2关于线程

  • 线程是独立的执行路径
  • 在线程运行时,即使没有自己的创建的线程,后台也会有多个线程,如主线程,gc线程(垃圾回收线程)
  • main()成之为主线程,为系统的入口,用于执行整个程序
  • 在一个线程中,如果开辟了多个线程,线程的运行由调度器安排调度,调度器是操作系统紧密相关的,先后顺序是不能认为干预的.
  • 对同一份资源操作时,会存在资源争夺的问题,需要加入并发控制
  • 线程会带来额外的开销,如cpu调度时间,并发控制开销
  • 每个线程在自己的工作内存中交互,内存控制不当会造车数据的不一致.

1.3并发与并行

  • 同一时刻,多个任务交替执行,造成一种"貌似同时"的错觉,简单的说,单核cup是实现的多任务就是并发
  • 同一时刻都多个任务同时执行,多核CPU可以实现并行.

1.4 java中怎么创建线程类的三种方式

  • 继承Thread类的方式
  • 实现Runnable接口的方式
  • 实现Callable接口的方式,这个方式有返回值的,可以带回反回值.
    方式一:继承Thread类
package hai.bok.thread.dem01;

/**
 * @auther 海文宇
 * @Date 2022/3/15 22:46
 * @Description
 */
public class MyThread extends Thread{
    //重写run方法
    @Override
    public void run() {
        for (int i = 0; i < 10000; i++) {
            System.out.println("子线程:"+i);
        }
    }
}
package hai.bok.thread.dem01;

import hai.bok.thread.dem01.MyThread;

/**
 * @auther 海文宇
 * @Date 2022/3/15 22:43
 * @Description
 * 线程启动的两种方式
 * 1.继承呢过Thread类
 *  步骤:1.自定义一个类继承Thread类
 *      2.重写run方法
 *      3.创建线程
 *      4.启动线程
 *  注意:
 *      1.开启线程是调用的线程对象是start方法,不是run方法
 *  调用run方法 没有多线程的效果,就跟调用你的普通方法一样.
 *      2.线程不能多次启动,只能启动一次,(只能调用start方法一次)
 * 2.实现Runnable类
 * 步骤:1.自定义一个类实现Runnable类
 *     2.重写run方法,编写子线程的任务
 *     3.创建Thread类,用构造方法把Runnable接口的子类作为参数传入
 *     4.启动线程
 *
 */
public class TestThread {
    public static void main(String[] args) {
        //创建线程对象
        MyThread myThread = new MyThread();
        //启动线程对
        myThread.start();//有启动的话会出现线程抢占的效果
//        myThread.run();//可以执行,但是没有线程抢占的效果
        //主线程
        for (int i = 0; i < 10000; i++) {
            System.out.println("主线程:"+i);
        }
    }
}

方式二:实现Runnable接口

package hai.bok.thread.dem01;

/**
 * @auther 海文宇
 * @Date 2022/3/15 23:10
 * @Description
 * 使用Runnable接口实现
 */
public class MyRunnable implements Runnable {
    @Override
    public void run() {
        for (int i = 1000; i < 2000; i++) {
            System.out.println("子线程:"+i);
        }
    }
}
package hai.bok.thread.dem01;

import hai.bok.thread.dem01.MyRunnable;

/**
 * @auther 海文宇
 * @Date 2022/3/15 23:12
 * @Description
 */
public class TestRunnable {
    public static void main(String[] args) {
        MyRunnable mr = new MyRunnable();
        //创建Thread类,用构造方法把Runnable接口的子类作为参数传入
        Thread thread=new Thread(mr);
        //启动线程
        thread.start();
        //主线程
        for (int i = 1; i <= 100; i++) {
            System.out.println("主线程:"+i);
        }
    }
}

方式三:实现Callable接口的方式

package hai.bok.thread.dem02;

import java.util.concurrent.Callable;

/**
 * @auther 海文宇
 * @Date 2022/3/15 23:36
 * @Description
 * 实现Callable的接口的方式
 */
public class CallableThread implements Callable<Integer> {
    private int m;
    private int n;

    public CallableThread() {
    }

    public CallableThread(int m, int n) {
        this.m = m;
        this.n = n;
    }

    public int getM() {
        return m;
    }

    public void setM(int m) {
        this.m = m;
    }

    public int getN() {
        return n;
    }

    public void setN(int n) {
        this.n = n;
    }

    //run方法有返回值,还带了一个异常
    @Override
    public Integer call() throws Exception {
        int sum=0;
        for (int i = m; i <=n; i++) {
            System.out.println("子线程"+i);
            sum+=i;
        }
        return sum;
    }
}

package hai.bok.thread.dem02;

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

/**
 * @auther 海文宇
 * @Date 2022/3/15 23:32
 * @Description
 * 之前的线程的启动,run()方法没有返回值,在主线程中没有办法获取子线程的返回结果
 * 所有这里可以使用Callable接口的方式来开启线程
 * 需求:计算m~n的和
 */
public class Test01 {
    public static void main(String[] args) {
        //使用Callable接口,创建类
        CallableThread ct=new CallableThread(1,100);
        //创建FutureTast对象,有返回值类型是Integer;
        FutureTask<Integer> task=new FutureTask<Integer>(ct);
        //创建线程对象,把task当做参数传递
        Thread thread=new Thread(task);
        //启动线程
        thread.start();

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

        try {
            Integer sum = task.get();//获取子线程的结果
            System.out.println("子线程的计算结果:"+sum);
        } catch (ExecutionException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

1.4 使用匿名内部类的方式实现线程类

  1. 使用匿名内部类 方式开启线程,节约内存资源,同时也会更加简洁
  2. 当匿名内部类方式同时继承Thread类方式和实现Runnable接口方式
package hai.bok.thread.dem03;

/**
 * @auther 海文宇
 * @Date 2022/3/16 0:12
 * @Description
 * 1.使用匿名内部类 方式开启线程,节约内存资源,同时也会更加简洁
 * 2.当匿名内部类方式同时继承Thread类方式和实现Runnable接口方式两种启动,以Thread类方式优先
 */
public class Test01 {
    public static void main(String[] args) {
        //没有重写run方法分配任务,匿名线程对象启动.
        new Thread().start();

        //匿名内部类线程对象的启动,没有run方法,也没有分配任务
        new Thread(){}.start();

        //匿名内部类线程对象的启动,重写run方法
        new Thread(){
            @Override
            public void run() {
                for (int i = 0; i < 1000; i++) {
                    System.out.println("匿名内部类继承Thread类线程对象的启动方式:"+i);
                }
            }
        }.start();

        //匿名内部类Tread类线程对象实现Runnable接口
        new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 1000; i++) {
                    System.out.println("匿名内部类Tread类线程对象实现Runnable接口:"+i);
                }
            }
        }).start();

        //匿名内部类继承Tread类线程对象同时实现Runnable接口的方式
        new Thread(new Runnable() {
            @Override
            public void run() {
                //使用Runnable方式
                for (int i = 0; i < 1000; i++) {
                    System.out.println("匿名内部类实现Runnable接口2:"+i);
                }
            }
        }){
            //继承Thread类实现方式
            @Override
            public void run() {
                for (int i = 0; i < 1000; i++) {
                    System.out.println("匿名内部类继承Thread类线程对象的启动方式2:"+i);
                }
            }

        }.start();;
    }
}

2 线程的常用方法

1 设置名称

构造方法:

  • Thread(Runnable target,String)传入Runnable方式,命名.
  • Thread(String name)命名

方法:

  • String getName()获取某个线程的名称
  • void setName(String name)设置线程的名称
  • Static Thread.currentThread()获取当前的线程.

2 设置优先级

2.1.线程优先级的特点:

  • 随机性和不确定性,系统随机分配,哪个线程抢到了执行权我们是不能控制的
  • java使用的是抢占式调度模式(即优先级高的线程使用cpu,优先级相同的任意选择一个执行),优先级高的线程会相对来说获得CPU时间片段多一点

2.2 设置优先级的字段和方法

  • static int MAX_PRIPORITY=10: 线程可以拥有的最大优先级
  • static int MIN_PRIPORITY=1: 线程可以拥有的最小优先级
  • static int NORM_PRIPORITY=5: 配给线程的默认优先级
  • void setPripority(int newPripority)更改此线程的优先级
  • int getPripority()返回此线程的优先级

注意
设置了优先级,也不能够从优先级的测试具体的体现一定性,(也就是说,设置了优先级,优先级高的不一定就是一定先执行完.),只是说线程或得CUP执行的时间会多一点,线程的任务耗费时间设置的长一点,效果体现更加明显.

package hai.bok.thread.dem05;

/**
 * @auther 海文宇
 * @Date 2022/3/16 21:05
 * @Description
 */
public class PriporityThread extends Thread {
    @Override
    public void run() {
        for (int i = 0; i <= 100; i++) {
            System.out.println(getName()+":"+i);
        }

    }
}
package hai.bok.thread.dem05;

/**
 * @auther 海文宇
 * @Date 2022/3/16 21:04
 * @Description
 * 练习优先级
 */
public class Test01 {
    public static void main(String[] args) {
        //差创建3个子线程
        PriporityThread p1 = new PriporityThread();
        PriporityThread p2 = new PriporityThread();
        PriporityThread p3 = new PriporityThread();

        //设置三个线程的名字
        p1.setName("李四");
        p2.setName("张三");
        p3.setName("王五");

        //设置优先级,优先级的范围是1~10,超出这个范围会报错
        p1.setPriority(Thread.MAX_PRIORITY);//10
        p2.setPriority(Thread.NORM_PRIORITY);//5
        p3.setPriority(Thread.MIN_PRIORITY);//1

        //启动线程
        p1.start();
        p2.start();
        p3.start();
    }
}

3 线程休眠的方法

  • static void sleep(long millis):使当前正在执行的线程指定的毫秒数暂停(暂时停止执行)具体取决于系统器,和调度程序的精确度和准确性.
  • 可以使用线程休眠实现时钟效果.
package hai.bok.thread.dem05;

import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * @auther 海文宇
 * @Date 2022/3/16 21:37
 * @Description
 */
public class SleepTread extends Thread {
    @Override
    public void run() {
        //死循环
        while (true){
            //每休眠一秒显示一次时间
            String format = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss").format(new Date());
            System.out.println(getName()+":"+format);
            try {
                Thread.sleep(1000);//休眠一秒
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

package hai.bok.thread.dem05;

/**
 * @auther 海文宇
 * @Date 2022/3/16 21:45
 * @Description
 */
public class TestSleepThread {
    public static void main(String[] args) {
        SleepTread st = new SleepTread();
        st.setName("时钟");
        st.start();
    }
}

4 线程加入的方法

  • void join() 等待这个线程死亡
  • 调用join()方法会让某个线程优先执行结束
  • 同样的这也不是绝对的,设置了join,只是相对优先执行的概论会大一点,没有绝对的就一定是执行完.
package hai.bok.thread.dem05;

/**
 * @auther 海文宇
 * @Date 2022/3/16 21:55
 * @Description
 */
public class JoinThread extends Thread{
    @Override
    public void run() {
        for (int i = 1; i <= 100; i++) {
            System.out.println(getName()+":"+i);
        }
    }
}

package hai.bok.thread.dem05;

/**
 * @auther 海文宇
 * @Date 2022/3/16 21:57
 * @Description
 */
public class TestJoinThread {
    public static void main(String[] args) {
        //创建3个线程
        JoinThread j1 = new JoinThread();
        JoinThread j2 = new JoinThread();
        JoinThread j3 = new JoinThread();
        //设置名字
        j1.setName("张三");
        j2.setName("李四");
        j3.setName("王五");
        //打开线程
        j1.start();

        try {
            //j1子线程加入,优先让这个线程执行.
            j1.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        j2.start();
        j3.start();
    }
}

5 线程礼让的方法

  • static void yield() 唯一一个可以让出资源的方法,让出当前线程的执行权,让出后自己排队等待cup的执行权.也会继续抢夺cpu资源
package hai.bok.thread.dem05;

/**
 * @auther 海文宇
 * @Date 2022/3/16 22:45
 * @Description
 */
public class YieldThread extends Thread{
    @Override
    public void run() {
        for (int i = 1; i <= 100; i++) {
            System.out.println(getName()+":"+i);
            //让出线程执行权,让出后自己又会去排队等待cup的执行权,会和其他的线程抢夺资源
            Thread.yield();
        }
    }
}

package hai.bok.thread.dem05;

/**
 * @auther 海文宇
 * @Date 2022/3/16 22:49
 * @Description
 */
public class TestYield {
    public static void main(String[] args) {
        YieldThread t1 = new YieldThread();
        YieldThread t2 = new YieldThread();
        t1.setName("哥哥");
        t2.setName("妹妹");
        t1.start();
        t2.start();
    }
}

3 线程的5种状态

  1. 创建状态:new线程类后就创建了
  2. 就绪状态:线程创建后就就进入了就绪态,开始的等待CUP资源
  3. 运行状态:调用start方法开始运行.
  4. 阻塞状态:线程遇到输入输出等,进入阻塞状态,请求资源
  5. 结束状态:运行结束后,线程结束.

4 线程同步(同步方法,同步代码块)

  • 需求:火车售票,总共有100票,3个窗口
  • 使用Runnable接口的方式和继承Thread类的方式实现

1.1 第一种方式:继承Thread类的方式

  • 解决方式一:同步代码块synchronized(锁对象){}
  • 解决方式二:同步方法public synchronized 返回值 方法名(参数){}
  • 解决方式三:Java提供的锁机制Lock锁
package hai.bok.thread.dem06;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
 * @auther 海文宇
 * @Date 2022/3/16 23:41
 * @Description
 * 方式一:继承Thread类的方式
 */
public class SellTickeys extends Thread{
    private static int tickets=100;//100张票属于3个窗口,100属于共享数据
    public static Lock lock = new ReentrantLock();
    //java.util.concurrent.locks Interface Lock
    //Lock锁对象,void lock() 获得锁。void unlock() 释放锁。

    @Override
    public void run() {
        //模拟售票
        //解决方式一:同步代码块synchronized(锁对象){}
        /*synchronized (MyLock.LOCK){//可以使用this,当前类,定义的类作为一把锁
            while(true){
                if(tickets>0){
                    //设置休眠
                    try {
                        //线程休眠
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName()+
                            "正在售出第"+(tickets--)+"张票");
                }
            }
        }*/

        //解决方式二:
        //sellTickets();

        //解决方式三:Java提供的锁机制Lock锁
        //使用try...finally{}来加锁和释放锁,如果try块有异常,一定会执行finally释放锁的操作
        try {
            //加锁
            lock.lock();
            while (true){
                if (tickets > 0){
//                    try {
//                        Thread.sleep(100);
//                    } catch (InterruptedException e) {
//                        e.printStackTrace();
//                    }
                    System.out.println(Thread.currentThread().getName() + "正在售出第" + (tickets--) + "张票");
                }
            }
        } finally {
            //释放锁
            lock.unlock();
        }
        //使用JDK提供Lock锁,不需要我们自己定义锁的
        //直接使用它的方法就好了,这个使用更加方便,更加满足面向对象思想

    }

    // 解决方式二:同步方法public synchronized 返回值 方法名(参数){}
    /**
     * 三个子线程对象都可以调用这个普通方法,而不是三个方法属
     * 于三个子线程对象的,应该是一份这个方法给三个子线程对象使用,这样才能真正上锁
     * 所以方法得用static修饰
     * */
    public static synchronized void sellTickets(){//
        while(true){
            //解决方式一:同步代码块synchronized(锁对象){}
            if(tickets>0){
                //设置休眠
                try {
                    //线程休眠
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName()+
                        "正在售出第"+(tickets--)+"张票");
            }
        }
    }
}
//定义一把锁类
class MyLock{
    public static final MyLock LOCK=new MyLock();
}
package hai.bok.thread.dem06;

/**
 * @auther 海文宇
 * @Date 2022/3/16 23:49
 * @Description
 */
public class TestSellTickets {
    public static void main(String[] args) {
        SellTickeys t1 = new SellTickeys();
        SellTickeys t2 = new SellTickeys();
        SellTickeys t3 = new SellTickeys();

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

        //启动
        t1.start();
        t2.start();
        t3.start();
    }
}

1.2 第二种方式:实现Runnable接口的方式

  • 解决方式一:同步代码块synchronized(锁对象){}
  • 解决方式二:同步方法public synchronized 返回值 方法名(参数){}
  • 解决方式三:Java提供的锁机制Lock锁
package hai.bok.thread.dem06;

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

/**
 * @auther 海文宇
 * @Date 2022/3/17 1:06
 * @Description
 */
public class    SellTicketsRunnable implements Runnable{
    private static int tickets=100;
    private static Lock lock=new ReentrantLock();

    @Override
    public void run() {

       // 解决方式一:同步代码块
/*        synchronized (MyLock_.LOCK){
            while (true){
                if(tickets>0){
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName()+
                            "正在售出第"+(tickets--)+"张票");
                }
            }
        }*/

        //解决方式二;
//        sellTickets();

        //解决方式三
        try{
            lock.lock();
            while(true){
                //解决方式一:同步代码块synchronized(锁对象){}
                if(tickets>0){
                    //设置休眠
                    try {
                        //线程休眠
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName()+
                            "正在售出第"+(tickets--)+"张票");
                }
            }
        }finally {
            lock.unlock();
        }


    }

    public static synchronized void sellTickets(){//
        while(true){
            //解决方式一:同步代码块synchronized(锁对象){}
            if(tickets>0){
                //设置休眠
                try {
                    //线程休眠
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName()+
                        "正在售出第"+(tickets--)+"张票");
            }
        }
    }
}
class MyLock_ {
    public static final MyLock LOCK=new MyLock();
}
package hai.bok.thread.dem06;

/**
 * @auther 海文宇
 * @Date 2022/3/17 1:09
 * @Description
 */
public class Test01 {
    public static void main(String[] args) {
        SellTicketsRunnable tr = new SellTicketsRunnable();
        Thread thread1 = new Thread(tr);
        Thread thread2 = new Thread(tr);
        Thread thread3 = new Thread(tr);
        thread1.setName("窗口1:");
        thread2.setName("窗口2:");
        thread3.setName("窗口3:");
        thread1.start();
        thread2.start();
        thread3.start();
    }
}

5 线程死锁

  • 线程同步的优点:接c解决数据安全问题
  • 线程同步的缺点:容易造成死锁现象

死锁: 死锁指多个线程在执行过程中,他们在抢夺资源的时候,产生的一种相互等待的现象.

6 线程分组

线程组:把多个线程分为一个组,方便统一管理多个线程.

Java API提供这些对线程的一个分组,目的就是可以把一个组的多个线程当做一个单元的一个组的,对一个组的线程再访问的时候就方便直接对他们由一个操作.

1.1线程的分组

  • Thread(ThreadGroup group, Runnable target) 分配新的 Thread 对象。
  • Thread(ThreadGroup group, Runnable target, String name) 分配新的 Thread 对象,以便将 target 作为其运行对象,将指定的 name 作为其名称,并作为 group 所引用的线程组的一员。
  • Thread(ThreadGroup group, Runnable target, String name, long stackSize) 分配新的 Thread 对象,以便将 target 作为其运行对象,将指定的 name 作为其名称,作为 group 所引用的线程组的一员,并具有指定的堆栈大小。
  • Thread(ThreadGroup group, String name) 分配新的 Thread 对象。
package hai.bok.thread.dem08;

/**
 * @auther 海文宇
 * @Date 2022/3/18 16:06
 * @Description
 */
public class Test01MyRunnable implements Runnable{
    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            System.out.println(Thread.currentThread().getName()+":"+i);
        }
    }
}


package hai.bok.thread.dem08;

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

/**
 * @auther 海文宇
 * @Date 2022/3/18 16:05
 * @Description
 */
public class Test01 {
    public static void main(String[] args) {
        Test01MyRunnable myRunnable=new Test01MyRunnable();

        //第一组
        ThreadGroup tg1=new ThreadGroup("三国演义");//分组名称
        Thread t1=new Thread(tg1,myRunnable,"刘备");
        Thread t2=new Thread(tg1,myRunnable,"曹操");
        Thread t3=new Thread(tg1,myRunnable,"孙权");
        //第二组
        ThreadGroup tg2=new ThreadGroup("西游记");
        Thread t4=new Thread(tg2,myRunnable,"孙悟空");
        Thread t5=new Thread(tg2,myRunnable,"沙悟净");
        Thread t6=new Thread(tg2,myRunnable,"猪八戒");
        //启动
        t1.start();
        t2.start();
        t3.start();
        t4.start();
        t5.start();
        t6.start();

       // activeCount() 返回此线程组中活动线程的估计数
        // getName()返回此线程组的名称。
        System.out.println(t1.getName());
        System.out.println(t3.getThreadGroup().getName());

        System.out.println(tg2.activeCount());
        tg1.setDaemon(true);//设置守护线程
        tg2.stop();//终止一个组

        //获取主线程的名称
        Thread main=Thread.currentThread();
        String name = main.getThreadGroup().getName();
        System.out.println(name);

    }
}

1.2等待唤醒机制

  • void notify() 唤醒正在等待对象监视器的单个线程。
  • void notifyAll() 唤醒正在等待对象监视器的所有线程。
  • void wait() 导致当前线程等待,直到另一个线程调用该对象的 notify()方法或 notifyAll()方法。

生产者消费者模型

  1. 玩具类
  2. 编写生产者
  3. 编写消费者
  4. 编写测试类
    生产者
    先判断是否有玩具
    如果有,就等待消费者消费,wait
    如果没有 ,就需要生产,生产完之后通知消费者,notify
    消费者
    先判断是否有玩具
    如果有,就消费,
    如果没有,就等待生产者生产
    Object类中等待和唤醒方法:
  • wait():让某个线程是等待状态(阻塞状态)会将线程放到等待池中,而且会释放锁
  • notify():唤醒在同一把锁上处于等待的某个线程
  • notifyAll():唤醒在同一把锁上处于等待的所有线程

注意: 顺序和加锁,不会抛出异常,如果没有按照顺序先唤醒后等待,执行的时候会抛出异常。

玩具类

package hai.bok.thread.dem09;

/**
 * @auther 海文宇
 * @Date 2022/3/18 21:34
 * @Description
 * 生产者消费者问题
 * 玩具类
 */
public class Toy {
    //属性:名称,数量,是否有商品玩具
    private String name;
    private int num;
    private boolean flag;//true,有玩具,false,没有玩具

    public Toy() {
    }

    public Toy(String name, int num, boolean flag) {
        this.name = name;
        this.num = num;
        this.flag = flag;
    }

    public String getName() {
        return name;
    }

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

    public int getNum() {
        return num;
    }

    public void setNum(int num) {
        this.num = num;
    }

    public boolean isFlag() {
        return flag;
    }

    public void setFlag(boolean flag) {
        this.flag = flag;
    }
}

生产者类:

package hai.bok.thread.dem09;

/**
 * @auther 海文宇
 * @Date 2022/3/18 21:38
 * @Description
 * 生产者
 * 	先判断是否有玩具
 * 		如果有,就等待消费者消费,wait
 * 		如果没有 	,就需要生产,生产完之后通知消费者,notify
 */
public class setThread extends Thread{
    //玩具类
    private Toy t;
    //玩具种类有很多,可以生产多种,或者来控制生产个数
    private int i;

    public setThread() {
    }

    public setThread(Toy t) {
        this.t = t;
    }

    public Toy getT() {
        return t;
    }

    public void setT(Toy t) {
        this.t = t;
    }

    public int getI() {
        return i;
    }

    public void setI(int i) {
        this.i = i;
    }

    @Override
    public void run() {
        while (true){
            //锁
            synchronized (t){
                //判断是否有玩具类
                if(t.isFlag()){
                    //有,就等待
                    try {
                        t.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                //如果没有,就需要生产,生产完之后通知消费者,notify
                if(i%2==0){//偶数,生产叮当猫玩具,生产数量为10个
                    t.setName("叮当猫");
                    t.setNum(10);
                }else {//基数,生产汤姆猫
                    t.setName("汤姆猫");
                    t.setNum(5);
                }
                i++;
                //要生产,设置true,代表有数据,设置false,代表没有数据.
                //设置标志位,代表这里有数据了
                t.setFlag(true);
                //生产完之后通知消费者,notify
                t.notify();
            }
        }
    }
}

消费者类:

package hai.bok.thread.dem09;

/**
 * @auther 海文宇
 * @Date 2022/3/18 21:58
 * @Description
 * 消费者
 *  先判断是否有玩具
 * 		如果没有有,就等待生产者生产
 * 		如果有,就消费
 */
public class GetThread extends Thread {
    //玩具类
    private Toy t;

    public GetThread() {
    }

    public GetThread(Toy t) {
        this.t = t;
    }

    @Override
    public void run() {
       while (true){
           //一把锁
           synchronized (t){
               //先判断是否有玩具
               if(!t.isFlag()){//如果没有,就等待生产者生产
                   try {
                       t.wait();
                   } catch (InterruptedException e) {
                       e.printStackTrace();
                   }
               }
               //如果有,就消费
               String toyName = t.getName();//获取玩具名称
               int toyNum = t.getNum();//获取数量
               System.out.println(toyName+"|"+toyNum);

               //消费后,数量减一
               t.setNum(--toyNum);

               //消费完了,就通知生产者生产
               if(toyNum<=0){
                    //设置标记位,代表没有玩具
                   t.setFlag(false);
                   //通知生产
                   t.notify();
               }
           }
       }
    }
}

测试类:

package hai.bok.thread.dem09;

/**
 * @auther 海文宇
 * @Date 2022/3/18 22:12
 * @Description
 */
public class Test {
    public static void main(String[] args) {
        //玩具类
        Toy toy = new Toy();
        //生产者线程
        setThread st = new setThread(toy);
        //消费者线程
        GetThread gt = new GetThread(toy);
        st.start();
        gt.start();
    }
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值