java线程间通信的几种方法_socket通信原理 java

java线程间通信的几种方法_socket通信原理 javaJava线程间通信

大家好,又见面了,我是你们的朋友全栈君。如果您正在找激活码,请点击查看最新教程,关注关注公众号 “全栈程序员社区” 获取激活教程,可能之前旧版本教程已经失效.最新Idea2022.1教程亲测有效,一键激活。

Jetbrains全系列IDE稳定放心使用

⭐写在前面⭐

? 内容回顾
Java 多线程介绍及线程创建
Java 多线程七大状态
Java 多线程方法详解
Java synchronized关键字实现线程同步
?今天我们进行 JDBC 获取数据库连接的5种方式 的学习,感谢你的阅读,内容若有不当之处,希望大家多多指正,一起进步?!!!
♨️如果觉得博主文章还不错,可以?三连支持⭐一下哦?

☘️Java 线程间通信

?线程通信方法

在Java的Object类中提供了waitnotifynotifyAll等方法,这些方法可以实现线程间的通信,因为Object类是所有类的基类,因此所有的对象都具有线程间通信的方法
在这里插入图片描述

void wait():调用一个对象的wait方法,会导致当前持有该对象的锁的线程等待,直到该对象的另一个持有锁的线程调用notify或者notifyAll唤醒。

void wait(long timeout):除了和wait相似,还具有超过定时的超时时间,时间到后或自动唤醒。

void wait(long timeout,int nanou):与 void wait(long timeout) 相同,不过提供了纳秒级别的更精确的超时控制。

void notify():调用一个对象的notify方法,会导致当前持有该锁的所有线程中的随机某一个线程被唤醒

void notifyAll():调用一个对象的notifyAll方法,会导致当前持有该锁的所有线程被唤醒

?线程间通信案例

通信是在不同线程间的通信,一个线程处于wait状态阻塞等待被唤醒,另一个线程通过notify或者notifyAll唤醒,当前的唤醒操作必须是作用与同一个对象,注意在进行唤醒和阻塞时必须要加锁的,加锁需要使用synchronized关键字。

WaitDemo类

public class WaitDemo extends Thread{ 
   
    private Object obj;

    public WaitDemo(Object obj) { 
   
        this.obj = obj;
    }

    @Override
    public void run() { 
   
        synchronized (obj) { 
   
            System.out.println(Thread.currentThread().getName() + "WaitDemo执行开始~~~");
            try { 
   
                obj.wait(); //调用wait方法阻塞线程执行
            } catch (InterruptedException e) { 
   
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + "WaitDemo执行结束~~~");
        }
    }
}

NotifyDemo类

public class NotifyDemo extends Thread{ 
   
    private Object obj;

    public NotifyDemo(Object obj) { 
   
        this.obj = obj;
    }

    @Override
    public void run() { 
   
        synchronized (obj) { 
   
            System.out.println(Thread.currentThread().getName() + "NotifyDemo执行开始~~~");
            try { 
   
                Thread.sleep(1000);
            } catch (InterruptedException e) { 
   
                e.printStackTrace();
            }
            obj.notify(); //调用notify方法唤醒阻塞线程
            System.out.println(Thread.currentThread().getName() + "NotifyDemo执行结束~~~");
        }
    }
}

TestWaitAndNotify测试类

public class TestWaitAndNotify { 
   
    public static void main(String[] args) { 
   
        Object object = new Object();
        WaitDemo waitDemo = new WaitDemo(object);
        NotifyDemo notifyDemo = new NotifyDemo(object);

        waitDemo.setName("WaitDemo线程");
        notifyDemo.setName("NotifyDemo线程");

        waitDemo.start();
        notifyDemo.start();

    }
}

?执行结果:
在这里插入图片描述

?使用注意点

?调用notifywait方法必须是作用于同一个对象,如果不是通一个对象则无法完成通信。
?对于waitnitifynotifyAll的调用,必须在该对象的同步方法或者代码块中,锁作用的对象和wait等方法必须是作用于同一个对象。
?wait方法在调用后进入阻塞之前会释放锁,而sleepjoin是不会释放锁的。
?线程状态转换时,当wait被唤醒或者超时时,线程并不是直接进入就绪状态,而是先进入阻塞状态,抢锁成功后才能进入到可运行状态。

?注意点详解

注意点1 当锁的对象和调用waitnotify的对象不是同一个对象时,会抛出IllegalMonitorStateException异常。
在这里插入图片描述

注意点2 wait方法在调用进入阻塞之前会释放锁
在这里插入图片描述
基于以上分析,一旦wait线程先调用则线程因为锁无法继续执行而阻塞下来,实际上notify依然可以获取锁进行执行,这是因为wait方法在调用进入阻塞之前释放锁,则调用notify操作的线程就可以抢到Object对象的锁,进而调用notify。

注意点3 锁池和等待池

在这里插入图片描述
锁池:假设线程A已经拥有了某个对象的锁,而其他的线程想要调用这个对象的某个synchronized方法,由于这些线程在进入对象的synchronized方法之前必须先获取该对象的锁的拥有权,但是该对象的锁目前被线程A拥有,所以这些线程就回去进入到该对象的锁池
等待池:假设一个线程A调用某个对象的wait方法,线程A就会释放该对象的锁后,进入到该对象的等待池

?小试牛刀

?有三个线程,分别为ABC线程,需要线程交替打印:ABCABC…打印10遍
? 分析:需要使用线程间的通信,A给B通信,A进行notifyB进行wait;B给C通信,B进行notifyC进行Wait;同理C给A通信,C进行notifyA进行wait。

? 思路分析:给每个线程给定编号,表明是第几个进程,再给定一个共享对象,共享对象进行notifywait等操作,共享对象本身需要携带信息表明下一个执行的线程编号,如果当前线程的编号与共享对象中的信息比较,如果相等就执行,否则就阻塞。

?代码示例

NextOpt类:共享对象,携带下一个要执行的线程编号信息

//共享对象,携带下一个要执行的线程编号信息
public class NextOpt { 
   
    //下一个执行线程编号
    private Integer nextValue;

    public Integer getNextValue() { 
   
        return nextValue;
    }

    public void setNextValue(Integer nextValue) { 
   
        this.nextValue = nextValue;
    }
}

ThreadABC类

public class ThreadABC extends Thread { 
   
    //线程间通信对象
    private NextOpt opt;
    //打印名称通过数组获取
    private String[] abc = { 
   "A", "B", "C"};
    //线程编号
    private int index;
    //执行次数
    int count = 0;

    public ThreadABC(NextOpt opt, int index) { 
   
        this.opt = opt;
        this.index = index;
    }

    @Override
    public void run() { 
   
        while (true) { 
    //如果当前线程编号与下一个要执行的序号不一致就阻塞
            synchronized (opt) { 
   
                while (opt.getNextValue() != index) { 
   
                    try { 
   
                        opt.wait();
                    } catch (InterruptedException e) { 
   
                        e.printStackTrace();
                    }
                }

                //打印
                System.out.print(abc[index]);
                //设置下一个要执行的线程的编号
                opt.setNextValue((index + 1) % 3);
                //唤醒所有的阻塞线程
                opt.notifyAll();

                if (count++ > 9) { 
   
                    break;
                }
            }
        }
    }
}

TestThreadABC测试类

public class TestThreadABC { 
   
    public static void main(String[] args) { 
   
        NextOpt opt = new NextOpt();

        opt.setNextValue(0); //设置第一个要执行的线程编号
        ThreadABC thread1 = new ThreadABC(opt,0);
        ThreadABC thread2 = new ThreadABC(opt,1);
        ThreadABC thread3 = new ThreadABC(opt,2);

        thread1.start();
        thread2.start();
        thread3.start();
    }
}

运行结果:
在这里插入图片描述

?生产者消费者模型

线程间通信典型案例:生产者消费者模型

生产者-消费者(producer-consumer)问题,也称作有界缓冲区(bounded-buffer)问题,两个进程共享一个公共的固定大小的缓冲区。
其中一个是生产者,用于将消息放入缓冲区;另外一个是消费者,用于从缓冲区中取出消息。
问题出现在当缓冲区已经满了,而此时生产者还想向其中放入一个新的数据项的情形,其解决方法是让生产者此时进行休眠,等待消费者从缓冲区中取走了一个或者多个数据后再去唤醒它。
同样地,当缓冲区已经空了,而消费者还想去取消息,此时也可以让消费者进行休眠,等待生产者放入一个或者多个数据时再唤醒它。
再具体一点:
a.生产者生产数据到缓冲区中,消费者从缓冲区中取数据。
b. 如果缓冲区已经满了,则生产者线程阻塞。
c. 如果缓冲区为空,那么消费者线程阻塞。
上述过程的描述应该已经体现出生产者和消费者之间的线程通信的流程,生产者一旦将队列生成满了之后就要控制线程停止生产,直到消费者将队列中消费一个之后就可以通知生产者继续生产新的元素,当消费者线程将队列中的元素全部取出之后消费者线程就需要停止消费元素,直到生产者线程向队列中添加一个元素之后可以通知消费者线程继续消费元素。
在这里插入图片描述

编写一个生产者、消费者模型,给定要求:一个生产者、一个消费者、仓库是三个

生产者

public class Producer extends Thread{ 
   
    private LinkedList<Integer> cap;//共享仓库
    private Random random = new Random();

    public Producer(LinkedList<Integer> cap) { 
   
        this.cap = cap;
    }

    @Override
    public void run() { 
   
        while (true) { 
   
            synchronized (cap){ 
   
                if (cap.size() == 3) { 
   //缓冲区满 生产者进行阻塞
                    try { 
   
                        cap.wait();
                    } catch (InterruptedException e) { 
   
                        e.printStackTrace();
                    }
                }

                //生产产品
                int i = random.nextInt(1000);
                System.out.println("生产者生产了" + i);
                cap.add(i);

                //通知消费者消费产品
                cap.notify();
            }
        }
    }
}

消费者

public class Consumer extends Thread{ 
   
    private LinkedList<Integer> cap;

    public Consumer(LinkedList<Integer> cap) { 
   
        this.cap = cap;
    }

    @Override
    public void run() { 
   
        while (true) { 
   
            synchronized (cap) { 
   
                if (cap.size() == 0) { 
    //如果缓冲区为0,消费者阻塞
                    try { 
   
                        cap.wait();
                    } catch (InterruptedException e) { 
   
                        e.printStackTrace();
                    }
                }

                //消费者消费产品
                Integer i = cap.remove();
                System.out.println("消费者消费了" + i);

                //通知生产者生产
                cap.notify();
            }
        }
    }
}

测试类

public class Test { 
   
    public static void main(String[] args) { 
   
        LinkedList<Integer> cap = new LinkedList<>();
        Producer producer = new Producer(cap);
        Consumer consumer = new Consumer(cap);

        producer.start();
        consumer.start();
    }
}

运行结果
在这里插入图片描述

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/184962.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • 安利一款Python开发的仿Linux树形显示目录tree命令「建议收藏」

    安利一款Python开发的仿Linux树形显示目录tree命令「建议收藏」大家好,我是小小明,今天要带大家通过python来实现仿Linux的tree命令。文章目录Linux与Windows的tree命令Linux的tree命令演示Windows的tree命令Python自制tree命令os模块基础代码Rich库关于tree模块的官方示例调用Tree模块实现仿Linux树形显示目录效果安装自定义tree模块首先看看Linux下的tree命令效果如何:Linux与Windows的tree命令Linux的tree命令演示在CentOS的Linux系统下,我们可以再使用yum

    2022年7月25日
    13
  • sublime 4113 激活码【最新永久激活】

    (sublime 4113 激活码)好多小伙伴总是说激活码老是失效,太麻烦,关注/收藏全栈君太难教程,2021永久激活的方法等着你。IntelliJ2021最新激活注册码,破解教程可免费永久激活,亲测有效,下面是详细链接哦~https://javaforall.net/100143.html0UY7RF7AC5-eyJsaWNlbnNlSWQi…

    2022年3月28日
    401
  • 越狱软件源

    越狱软件源全部内容来源于网络。不对其内容的安全性负责,本人不承担任何责任。使用任何内容即表示同意此内容。软件源地址ios6五百年源apt.so/ios6vxios6分享源apt.feng/eq2wowMK越狱源apt.so/mksscc盗版暗影apt.so/anyinkeji杀手源apt.so/tanyao正版暗影apt.so/anyinkj…

    2022年6月9日
    85
  • 【AekdyCoin】求小于等于N的与N互质的数的和

    【AekdyCoin】求小于等于N的与N互质的数的和又向大牛学到了一点。以下内容转大牛文章:ifgcd(n,i)=1thengcd(n,n-i)=1(1反证法:如果存在K!=1使gcd(n,n-i)=k,那么(n-i)%k==0而n%k=0那么必须保证i%k=0k是n的因子,如果i%k=0那么gcd(n,i)=k,矛盾出现;于是问题变的非常简单ANS=N*phi(N)/2i,n-i总是成对

    2022年7月23日
    11
  • 2021phpstorm 激活吗破解方法

    2021phpstorm 激活吗破解方法,https://javaforall.net/100143.html。详细ieda激活码不妨到全栈程序员必看教程网一起来了解一下吧!

    2022年3月15日
    46
  • ImportError: No module named bz2问题解决方法「建议收藏」

    ImportError: No module named bz2问题解决方法「建议收藏」问题:File”out/host/Linux-x86/obj/EXECUTABLES/mksnapshot.arm_intermediates/js2c.py”,line36,in  importbz2ImportError:Nomodulenamedbz2Traceback(mostrecentcalllast): File”out

    2022年6月1日
    40

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号