Day06-线程间通信


title: Day06-线程间通信
date: 2020-06-04 11:03:33
author: 子陌


线程间的通信


线程间通信:多个线程在处理同一资源,但是任务却不同

例程

1 多线程执行同一资源:

// 资源
class Resource {
    
    
    String name;
    String sex;
}

// 输入
class Input implements Runnable {
    
    
    Resource r;
    Input(Resource r) {
    
    
        this.r = r;
    }
    public void run() {
    
    
        boolean b = true;
        while (true) {
    
    
            synchronized (Resource.class) {
    
    
                if (b) {
    
    
                    r.name = "zimo";
                    r.sex = "man";
                } else {
    
    
                    r.name = "子陌";
                    r.sex = "女";
                }
            }
            b = !(b);
        }
    }
}

// 输出
class Output implements Runnable {
    
    
    Resource r;
    Output(Resource r) {
    
    
        this.r = r;
    }
    public void run() {
    
    
        while (true) {
    
    
            synchronized (Resource.class) {
    
    
                System.out.println(r.name + ":" + r.sex);
            }
        }
    }
}

class ResourceDemo {
    
    
    public static void main(String[] args) {
    
    
        // 创建资源
        Resource r = new Resource();
        // 创建任务
        Input in = new Input(r);
        Output out = new Output(r);
        // 创建线程
        Thread t1 = new Thread(in);
        Thread t2 = new Thread(out);
        // 开启线程
        t1.start();
        t2.start();
    }
}

2 多线程执行同一资源 - 加入等待唤醒功能:

  1. wait()、notify()、notifyAll(),用来操作线程为什么定义在了Object类中?
    • 这些方法存在于同步中
    • 使用这些方法时必须要表示所属的同步的锁(对象监视器)
    • 锁可以是任意对象,所以任意对象调用的方法一定定义Object类中
  2. wait()、sleep()有什么区别?
    • wait():释放cpu执行权,释放锁
    • sleep():释放cpu执行权,不释放锁
/*
*	涉及的方法:
*	1.wait();			让线程处于冻结状态,被wait的线程会被存储到线程池中。
*	2.notify();			唤醒线程池中的一个线程(任意的,无序的)。
*	2.notifyAll();		唤醒线程池中的所有线程。
*/
// 资源
class Resource {
    
    
    String name;
    String sex;
    boolean flag = false;
}

// 输入
class Input implements Runnable {
    
    
    Resource r;
    Input(Resource r) {
    
    
        this.r = r;
    }
    public void run() {
    
    
        boolean b = true;
        while (true) {
    
    
            synchronized (Resource.class) {
    
    
                if(r.flag){
    
    
                    // 如果有内容,那么写线程等待
                    r.wait();
                }else{
    
    
                    // 如果没有内容,就往里写
                    if (b) {
    
    
                        r.name = "zimo";
                        r.sex = "man";
                    } else {
    
    
                        r.name = "子陌";
                        r.sex = "女";
                    }
                    // 标记修改
                    r.flag = true;
                    // 唤醒输出线程
                    r.notify();
                    b = !(b);
                }
            }
        }
    }
}

// 输出
class Output implements Runnable {
    
    
    Resource r;
    Output(Resource r) {
    
    
        this.r = r;
    }
    public void run() {
    
    
        while (true) {
    
    
            synchronized (Resource.class) {
    
    
                if(!r.flag){
    
    
                    r.wait();
                }else{
    
    
                    System.out.println(r.name + ":" + r.sex);
                    r.flag = false;
                    r.notify();
                }
            }
        }
    }
}

class ResourceDemo {
    
    
    public static void main(String[] args) {
    
    
        // 创建资源
        Resource r = new Resource();
        // 创建任务
        Input in = new Input(r);
        Output out = new Output(r);
        // 创建线程
        Thread t1 = new Thread(in);
        Thread t2 = new Thread(out);
        // 开启线程
        t1.start();
        t2.start();
    }
}

代码优化:

// 资源
class Resource {
    
    
    private String name;
    private String sex;
    private boolean flag = false;
    public synchronized void set(String name, String sex){
    
    
        if(flag){
    
    
            try{
    
    
                this.wait();
            }catch(InterruptedException e){
    
    
                
            }
        }
        this.name = name;
        this.sex = sex;
        flag = true;
        this.notify();
    }
    public synchronized void out(){
    
    
        if(!flag){
    
    
            try{
    
    
                this.wait();
            }catch(InterruptedException e){
    
    
                
            }
        }
        System.out.println("name :" + name + ",sex :" + sex);
        flag = false;
        this.notify();
    }
}

// 输入
class Input implements Runnable {
    
    
    Resource r;
    Input(Resource r) {
    
    
        this.r = r;
    }
    public void run() {
    
    
        boolean b = true;
        while (true) {
    
    
            if (b) {
    
    
                r.set("zimo", "man");
            } else {
    
    
                r.set("子陌", "女");
            }
            b = !(b);
        }
    }
}

// 输出
class Output implements Runnable {
    
    
    Resource r;
    Output(Resource r) {
    
    
        this.r = r;
    }
    public void run() {
    
    
        while (true) {
    
    
            r.out();
        }
    }
}

class ResourceDemo {
    
    
    public static void main(String[] args) {
    
    
        // 创建资源
        Resource r = new Resource();
        // 创建任务线程
        Thread t1 = new Thread(new Input(r));
        Thread t2 = new Thread(new Output(r));
        // 开启线程
        t1.start();
        t2.start();
    }
}

3 多生产者-多消费者模式:

// 多生产者-多消费者
// 产品资源
class Resource{
    
    
    private String name;
    private int count;
    private boolean flag = false;
    public synchronized void set(String name){
    
    
        while(flag){
    
    
            try{
    
    this.wait();}catch(InterruptedException e){
    
    }
        }
        this.name = name + this.count;
        count++;
        System.out.println(Thread.currentThread().getName() + "..生产者.." + this.name);
        
        flag = true;
        notifyAll();
    }
    public synchronized void out(){
    
    
        while(!flag){
    
    
            try{
    
    this.wait();}catch(InterruptedException e){
    
    }
        }
        System.out.println(Thread.currentThread().getName() + "..消费者.." + this.name);
        flag = false;
        notifyAll();
    }
}

// 生产者
class Producer implements Runnable{
    
    
    private Resource r;
    Producer(Resource r){
    
    
        this.r = r;
    }
    public void run(){
    
    
        while(true){
    
    
            r.set("烤鸭");
        }
    }
}

// 消费者
class Consumer implements Runnable{
    
    
    private Resource r;
    Consumer(Resource r){
    
    
        this.r = r;
    }
    public void run(){
    
    
        while(true){
    
    
            r.out();
        }
    }
}
class ProducerConsumerDemo{
    
    
    public static void main(String[] args){
    
    
        Resource r = new Resource();
        Producer pro = new Producer(r);
        Consumer con = new Consumer(r);
        Thread t1 = new Thread(pro);
        Thread t2 = new Thread(pro);
        Thread t3 = new Thread(con);
        Thread t4 = new Thread(con);
        t1.start();
        t2.start();
        t3.start();
        t4.start();
    }
}
  • if标记:只有一次判断,会导致不该运行的线程运行了,出现数据错误的情况
  • while判断标记:解决了线程重新唤醒执行权后,是否能继续往下运行!如果本方唤醒了本方,没有意义,notify + while 会导致死锁
  • notifyAll:解决了,本方线程一定会唤醒对方线程的问题

JDK升级1.5 - 同时更名为5.0版本,同理1.6为6.0

notify()不仅唤醒了对方,同时唤醒了所有人,所以5.0推出java.util.concurrent.licks

同步代码块synchronized,对于锁的操作是隐士的,jdk1.5后将同步锁封装成了对象,并将锁操作的隐式方式定义到了该对象中,将隐式动作变成了显示动作

  • Lock替代了Condition方法的使用
  • Condition替代了Object监视器方法的使用
class LockDemo{
    
    
    // 创建一个锁对象
    Lock lock = new ReentrantLock();
    // 通过已有的锁 获取该锁上的监视器对象
    Condition c = lock.newCondition();
    //public synchronized void out(){
    
    
    public void out(){
    
    
        lock.lock();		/// 获取锁
        try{
    
    
            while(!flag){
    
    
                //try{this.wait();}catch(InterruptedException e){}
                try{
    
    c.await();}catch(InterruptedException e){
    
    }
            }
            System.out.println(Thread.currentThread().getName() + "..消费者.." + this.name);
            flag = false;
            c.signalAll();
        }finally{
    
    
            lock.unlock();
        }
    }
    
    // 通过已有的锁获取两组监视器:一组监视生产者,一组监视消费者
    // 生产的时候消费不能动
    
    // 创建一个锁对象
    Lock lock = new ReentrantLock();
    // 通过已有的锁 获取该锁上的监视器对象
    Condition producer_con = lock.newCondition();	// 生产者监视器
    Condition consumer_con = lock.newCondition();	// 消费者监视器

    public synchronized void set(String name){
    
    
        while(flag){
    
    
            try{
    
    producer_con.wait();}catch(InterruptedException e){
    
    }
        }
        this.name = name + this.count;
        count++;
        System.out.println(Thread.currentThread().getName() + "..生产者.." + this.name);
        
        flag = true;
        consumer_con.signal();
    }
    public void out(){
    
    
        lock.lock();		/// 获取锁
        try{
    
    
            while(!flag){
    
    
                //try{this.wait();}catch(InterruptedException e){}
                try{
    
    consumer_con.await();}catch(InterruptedException e){
    
    }
            }
            System.out.println(Thread.currentThread().getName() + "..消费者.." + this.name);
            flag = false;
            producer_con.signal();
        }finally{
    
    
            lock.unlock();
        }
    }
}

线程唤醒等待解析

  • Lock接口:替代了同步代码块或者同步函数。将同步的隐式锁操作变成显示锁操作。同时更为灵活,可以一个锁上加上多组监视器
    • lock():获取锁
    • unlock():释放锁,通常需要定义fianlly代码块中
  • Condition接口:替代了Object中的wait、notify、notifyAll方法,将这些监视器方法单独进行了封装,变成了Condition监视器对象,可任意锁进行组合
    • await():等价于wait()
    • signal():等价于notify()
    • signalAll():等价于notifyAll()

4、wait()和sleep()的区别

  1. wait可以指定时间,也可以不指定时间

    sleep必须指定时间

  2. 在同步中时,对于cpu的执行权和锁的处理不同

    • wait:释放执行权,释放锁
    • sleep:释放执行权,不释放锁

停止线程

  1. stop()方法
  2. run()方法结束

1) 定义循环结束标记

  • 因为线程运行代码一般都是循环,只要控制了循环即可
class StopThread implements Runnable{
    
    
    private boolean flag = true;
    public void run(){
    
    
        while(flag){
    
    
            System.out.println(Thread.currentThread().getName() + ".....");
        }
    }
    public void setFlag(){
    
    
        this.flag = false;
    }
}

class StopThreadDemo{
    
    
    public static void main(String[] args){
    
    
        StopThread st = new StopThread();
       	Thread t1 = new Thread(st);
        Thread t2 = new Thread(st);
        t1.start();
        t2.start();
        
        int num = 1;
        for(;;){
    
    
            if(++num == 50){
    
    
                st.setFlag();
                break;
            }
            System.out.println("main...." + num);
        }
    }
}

如果线程处于冻结状态,无法读取标记,如何结束?

2)使用interrupt(中断)方法

  • 该方法是结束线程的冻结状态,使线程(强制)回到运行状态中来
  • 强制动作会发生InterruptException异常,记得要处理
class StopThread implements Runnable{
    
    
    private boolean flag = true;
    public synchronized void run(){
    
    
        while(flag){
    
    
            try{
    
    
                wait();		// 当主线程死亡,子线程没有结束进入等待
            }catch(InterruptedException e){
    
    
                System.out.println(Thread.currentThread().getName() + "......" + e);
                flag = false;
            }
            System.out.println(Thread.currentThread().getName() + "*******");
        }
    }
    public void setFlag(){
    
    
        this.flag = false;
    }
}

class StopThreadDemo{
    
    
    public static void main(String[] args){
    
    
        StopThread st = new StopThread();
       	Thread t1 = new Thread(st);
        Thread t2 = new Thread(st);
        t1.start();
        t2.start();
        
        int num = 1;
        for(;;){
    
    
            if(++num == 50){
    
    
                //st.setFlag();		// 线程进入冻结,无法读取标记
                t1.interrupt();
                t2.interrupt();                
                break;
            }
            System.out.println("main...." + num);
        }
        System.out.println("main....end");
    }
}

stop方法已经过时不再使用

3)守护线程(联合线程/后台线程)

  • 如果前台线程都结束了,守护线程(后台线程)无论处于什么状态都将自动结束
class StopThread implements Runnable{
    
    
    private boolean flag = true;
    public synchronized void run(){
    
    
        while(flag){
    
    
            try{
    
    
                wait();		// 当主线程死亡,子线程没有结束进入等待
            }catch(InterruptedException e){
    
    
                System.out.println(Thread.currentThread().getName() + "......" + e);
                flag = false;
            }
            System.out.println(Thread.currentThread().getName() + "*******");
        }
    }
    public void setFlag(){
    
    
        this.flag = false;
    }
}

class StopThreadDemo{
    
    
    public static void main(String[] args){
    
    
        StopThread st = new StopThread();
       	Thread t1 = new Thread(st);
        Thread t2 = new Thread(st);
        t1.start();
        t2.setDaemon(true);		// 开启t2守护线程(后台线程)
        t2.start();
        
        int num = 1;
        for(;;){
    
    
            if(++num == 50){
    
    
                t1.interrupt();
                //t2.interrupt();                
                break;
            }
            System.out.println("main...." + num);
        }
        System.out.println("main....end");
    }
}

线程的其他方法

  • join()方法:函数执行线程释放执行权和执行资格,等待调用join()方法的线程执行完成
  • setPriority()方法:设置线程优先级方法
  • yield():暂停一下,释放一下CPU的执行权,同时自己还有机会抢到
class Demo implements Runnable{
    
    
    public void run(){
    
    
        for(int i = 0; i < 50; i++){
    
    
            System.out.println(Thread.currentThread().toString() + "......" + i);
        }
    }
}
class JoinDemo{
    
    
    public static void main(String[] args){
    
    
        Demo d = new Demo();
        
        Thread t1 = new Thread(d);
        Thread t2 = new Thread(d);
        t1.start();
        t1.join();		// t1线程要申请加入进来,运行。临时加入一个线程运算时可以使用join()方法
        t2.start();
        t2.setPriority(Thread.MAX_PRIORITY);
        for(int i = 0; i < 50; i++){
    
    
            System.out.println(Thread.currentThread().getName() + "......" + i);
        }
    }
}

class ThreadTest{
    
    
    public static void main(String[] args){
    
    
        // main
        for(int i = 0; i < 50; i++){
    
    
            System.out.println(Thread.currentThread().getName() + "......" + i);
        }
        // Thread
        new Thread(){
    
    
            public void run(){
    
    
                for(int j = 0; j < 50; j++){
    
    
                    System.out.println(Thread.currentThread().getName() + "......" + j);
                }
            }
        }.start();
		// Runnable
        Runnable r = new Runnable(){
    
    
            public void run(){
    
    
                for(int k = 0; k < 50; k++){
    
    
                    System.out.println(Thread.currentThread().getName() + "......" + k);
                }
            }
        }
        new Thread(r).start();
    }
}

Thread创建线程的时候可以进行组的划分,可以同时进行一整个线程组的操作


面试案例

class Test implements Runnable {
    
    
    public void run(Thread t){
    
    
        
    }
}
// 如果错误,错误发生在哪一行?
/*
*	第一行:抽象类runnable接口没有被覆盖,要么被abstract修饰
*	run是子类的Test特有的方法
*/


class ThreadTest {
    
    
    public static void main(String[] args){
    
    
        // 如果都没有,以Thread自己为主
        new Thread(new Runnable(){
    
    
            // 没有子类方法,以任务为主
            public void run(){
    
    
                System.out.println("Runnable run");
            }
        }){
    
    
            // 子类自己有方法,以子类为主
            public void run(){
    
    
                System.out.println("Thread run");
            }
        }.start();
    }
}

猜你喜欢

转载自blog.csdn.net/qq_38205875/article/details/107813690