Java-多线程3

线程间通信

  • 共分成四块代码 如下:

代码一:

public class Student {
    String name;
    int age;
}

代码二:

public class SetThread implements Runnable {
    private Student student;
    private int i = 0;
//    private Object obj = new Object();

    SetThread(Student student) {
        this.student = student;
    }

    @Override
    public void run() {
        while (true){
            synchronized (student){
                if(i%2==0){
                    student.name = "李毅";
                    student.age = 18;
                }else {
                    student.name = "小虎";
                    student.age = 16;
                }

                i++;
            }

        }


    }
}

代码三:

package com.shujia.wyh.day26;

public class GetThread implements Runnable {
    private Student student;
//    private Object obj = new Object();

    GetThread(Student student) {
        this.student = student;
    }

    @Override
    public void run() {
//        Student student = new Student();
        while (true){
            synchronized (student){
                System.out.println(student.name + "---" + student.age);
            }

        }
    }
}

代码四:

public class StudentDemo {
    public static void main(String[] args) {
        //定义一个共享的资源
        Student student = new Student();

        //创建Runnable对象
        SetThread setThread = new SetThread(student);
        GetThread getThread = new GetThread(student);

        //借助Thread类创建线程对象
        Thread t1 = new Thread(setThread, "生产者");
        Thread t2 = new Thread(getThread, "消费者");

        //启动线程
        t1.start();
        t2.start();
    }
}

分析线程间通信的案例:

  • 共有资源类:Student
  • 生产者:SetThread 给学生对象进行赋值
  • 消费者:GetThread 给学生对象进行取值
  • 测试类:StudentDemo

问题一:

  • 按照我们分析类思路来写程序,运行后发现每一次运行的结果都是null--0
    这是必然,因为我们在每一个线程类中都单独new了一次学生对象
    ,而我们实际上赋值和取值的对象应该是同一个。
    所以不应该在内部定义。
    怎么解决呢?
    应该在外部把学生对象定义出来,然后使用构造方法的形式进行传参。

问题二:

  • 我们为了运行出来的数据好看一点,在程序加入了循环和判断,赋值的时候,赋不同的值。
    但是呢,在运行的时候,又出现了新的问题。
    1、姓名和年龄不匹配
    2、同一个数据出现了多次(消费者消费了多次)
    原因:
    1)、姓名和年龄不匹配
    这是由线程执行具有随机性导致的
    2)、同一个数据出现了多次(消费者消费了多次)
    由于CPU一个小小的时间片就足矣我们线程执行很多次
    线程安全的问题:
    1、是否有多线程环境,是 一个生产者,一个消费者
    2、是否有共享数据,是 Student
    3、是否有多条语句操作着共享数据 是
    既然都满足,那么就说明有线程安全问题。
    解决方案:
    加锁。
    注意:
    1、不同种类的线程类中都要加锁
    2、并且不同种类的线程类中的锁对象必须是同一个(指的是线程对象共同拥有一个锁对象-> (student对象))

问题三;等待唤醒机制

  • 我们用有限的技术来解决问题2的时候,只解决了数据不一致的问题,但是没有解决重复出现的问题
    经过分析后,发现这是生产者消费者的问题

使用新技术来实现:

  • 如何添加等待唤醒机制呢?
    Object类中有三个方法来解决生产者消费者问题的:
    void wait() 导致当前线程等待,直到另一个线程调用该对象的 notify()方法或 notifyAll()方法。
    void notify() 唤醒正在等待对象监视器的单个线程。
    void notifyAll() 唤醒正在等待对象监视器的所有线程。
    为什么上面这些方法不定义在Thread类中呢?
    这些方法要想被调用,就必须通过锁对象调用。
    多个线程对象中调用等待唤醒的对象要一致,并且与锁对象一致。
    并且通过测试后发现,锁对象的类型可以是任意类型的
    所以这些方法会被定义在Object类中,因为java中所有的类都有一个父类叫做Object,他们创建出来的对象将来也有可能被作为锁对象。
同样是四块代码

代码一:

public class Student2 {
    String name;
    int age;
    boolean flag; //布尔类型的成员变量系统赋予默认值false
}

代码二:

// package com.shujia.wyh.day26;

public class SetThread2 implements Runnable {
    private Student2 student;
    private int i = 0;
//    private Object obj = new Object();

    private Demo demo;

    SetThread2(Student2 student) {
        this.student = student;
    }

    SetThread2(Student2 student,Demo demo) {
        this.student = student;
        this.demo = demo;
    }

    @Override
    public void run() {
        while (true){
            synchronized (demo){
                //先看看学生对象有没有值
                //如果这里flag值是false,说明学生对象的成员变量是没有值,或者说这里的值还没有被消费,就不等待消费者
                //直接生产数据
                //如果这里是true,说明学生对象有数据,生产者等待消费者消费数据
                if(student.flag){
                    //调用wait方法等待
                    //共有的锁对象调用方法
                    try {
//                        student.wait();
                        demo.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }

                if(i%2==0){
                    student.name = "李毅";
                    student.age = 18;
                }else {
                    student.name = "小虎";
                    student.age = 16;
                }

                i++;
                //生产完数据后,通知消费者来消费数据
//                student.notify();
                demo.notify();
                student.flag = true;
            }

        }


    }
}

代码三:

package com.shujia.wyh.day26;

public class GetThread2 implements Runnable {
    private Student2 student;
//    private Object obj = new Object();
    private Demo demo;

    GetThread2(Student2 student) {
        this.student = student;
    }

    GetThread2(Student2 student,Demo demo) {
        this.student = student;
        this.demo = demo;
    }

    @Override
    public void run() {
//        Student student = new Student();
        while (true){
            synchronized (demo){
                //先看看学生对象有没有值
                //当flag为false的时候,说明对象还没有生产数据,这时候消费者等待,等待生产者生产数据
                //当flag为true的时候,说明对象数据生产完毕,作为消费者就应该消费数据
                if(!student.flag){
                    try {
//                        student.wait();
                        demo.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }

                //这一步相当于消费数据
                System.out.println(student.name + "---" + student.age);

                //通知生产者生产数据
//                student.notify();
                demo.notify();
                student.flag = false;
            }

        }
    }
}

代码四:

public class StudentDemo2 {
    public static void main(String[] args) {
        //定义一个共享的资源
        Student2 student = new Student2();

        //随便定义一个对象来进行唤醒和等待
        Demo demo = new Demo();

        //创建Runnable对象
        SetThread2 setThread = new SetThread2(student,demo);
        GetThread2 getThread = new GetThread2(student,demo);

        //借助Thread类创建线程对象
        Thread t1 = new Thread(setThread, "生产者");
        Thread t2 = new Thread(getThread, "消费者");

        //启动线程
        t1.start();
        t2.start();
    }
}

线程组的概念:

  • ThreadGroup:
    线程组代表一组线程。
    此外,线程组还可以包括其他线程组。
    理解:把多个线程组合到一起,可以对这一组线程进行管理
public class ThreadGroupDemo {
    public static void main(String[] args) {
        //创建Runnable对象
        MyRunnable myRunnable = new MyRunnable();

        //借助Thread类创建线程对象
//        Thread t1 = new Thread(myRunnable, "李毅");
//        Thread t2 = new Thread(myRunnable, "许文客");
//
//        //想办法获取线程所在线程组
//        //ThreadGroup getThreadGroup()
//        //返回此线程所属的线程组。
//        ThreadGroup group1 = t1.getThreadGroup();
//        ThreadGroup group2 = t2.getThreadGroup();
//
//        //默认情况下,如果不对线程进行分组,会有一个默认的组
//        //String getName() 返回此线程组的名称。
//        //线程默认所属线程组叫做main,也就是都属于主线程组中的
//        String g1 = group1.getName();
//        String g2 = group2.getName();
//        System.out.println(g1);
//        System.out.println(g2);

        //创建一个组
        //ThreadGroup(String name)
        //构造一个新的线程组。 创建线程组的同时给线程组起名字
        ThreadGroup group = new ThreadGroup("帅哥组");


        //没有办法做到分完组后,再进行修改组
        //Thread(ThreadGroup group, Runnable target, String name)
        //创建线程对象,并分配组
        Thread t1 = new Thread(group, myRunnable, "李毅");
        Thread t2 = new Thread(group, myRunnable, "许文客");

        //打印线程所属组的名字
        String s1 = t1.getThreadGroup().getName();
        String s2 = t2.getThreadGroup().getName();
        System.out.println(s1);
        System.out.println(s2);

        ThreadGroup group2 = new ThreadGroup("美女组");
        //创建线程并且进行分组
        Thread t3 = new Thread(group2, myRunnable, "姚欣");
        Thread t4 = new Thread(group2, myRunnable, "张傲");

        //打印线程所属组的名字
        String s3 = t3.getThreadGroup().getName();
        String s4 = t4.getThreadGroup().getName();
        System.out.println(s3);
        System.out.println(s4);

        //需求:将帅哥组设置成守护线程
        //直接通过线程组名来进行设置
        group.setDaemon(true);

//        t1.setDaemon(true);
//        t2.setDaemon(true);

        //启动线程
        t3.start();
        t4.start();
        t1.start();
        t2.start();

    }
}
posted @ 2022-04-12 08:46  a-tao必须奥利给  阅读(17)  评论(0编辑  收藏  举报