0
点赞
收藏
分享

微信扫一扫

27、JAVA进阶——线程同步和线程间的通信

✅作者简介:热爱国学的Java后端开发者,修心和技术同步精进。

🍎个人主页:乐趣国学的博客

🍊个人信条:不迁怒,不贰过。小知识,大智慧。

💞当前专栏:JAVA开发者成长之路

✨特色专栏:国学周更-心性养成之路

🥭本文内容:JAVA进阶——线程同步和线程间的通信

目录

​​一、线程同步的必要性​​

​​二、 实现线程同步​​

​​1. 同步方法​​

​​2. 同步代码块​​

​​3. 死锁​​

​​三、线程间通信的必要性​​

​​四、实现线程间通信​​

27、JAVA进阶——线程同步和线程间的通信_java

 一、线程同步的必要性

        前面说的线程都是独立的,而且异步执行,也就是说每个线程都包含了运行时所需要的数据或方法,而不需要外部资源或方法,也不必关心其它线程的状态或行为。

        但是经常有一些同时运行的线程需要共享数据,此时就需要考虑其他线程的状态和行为,否则就不能保证程序运行结果的正确性。

        例子:可以运行下面这个例子看看运行逻辑中的错误

package cn.bdqn.demo01;

public class Site implements Runnable {

private int count=10;
private int num=0;
@Override
public void run() {

while(true){
if(count<=0){
break;
}
count--;
num++;
try {
Thread.sleep(500);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"抢到第"+num+"张票,剩余"+count+"张票!");
}
}
}

public class Test {

public static void main(String[] args) {

Site site=new Site();
Thread thread1=new Thread(site,"逃跑跑");
Thread thread2=new Thread(site,"抢票代理");
Thread thread3=new Thread(site,"黄牛党");
System.out.println("********开始抢票********");
thread1.start();
thread2.start();
thread3.start();
}
}

二、 实现线程同步

        (1)当两个线程或多个线程需要访问同一资源时,需要以某种顺序来确保该资源在某一时刻只能被一个线程使用的方式称为线程同步。

同步方法和同步代码块。这两种方式都使用synchronized关键字实现。

1. 同步方法

        -->通过在方法声明中加入synchronized关键字来声明同步方法。

        -->使用synchronized修饰的方法控制对类成员变量的访问。每个类实例对应一把锁,方法一旦执行,就独占该锁,直到该方法返回时才将锁释放,此后被阻塞的线程方能获得该锁,重新进入可执行状态。

        -->这种机制确保了同一时刻对应每一个实例,其所有声明为synchronized的方法只能有一个处于可执行状态,从而有效的避免了类成员变量的访问冲突。

        -->同步方法的语法格式如下:

        访问修饰符 synchronized 返回类型 方法名(){}  或者 synchronized 访问修饰符 返回类型 方法名(){}

        --》synchronized是同步关键字

        --》访问修饰符是指public、private等。

package cn.bdqn.demo02;

public class Site implements Runnable {

private int count=10;
private int num=0;
boolean flag=false;
public synchronized void sale(){
if(count<=0){
flag=true;
return;
}
count--;
num++;
try {
Thread.sleep(50);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"抢到第"+num+"张票,剩余"+count+"张票!");
}
@Override
public void run() {

while(!flag){
sale();
}
}
}

package cn.bdqn.demo02;
public class Test {
public static void main(String[] args) {

Site site=new Site();
Thread thread1=new Thread(site,"逃跑跑");
Thread thread2=new Thread(site,"抢票代理");
Thread thread3=new Thread(site,"黄牛党");
System.out.println("********开始抢票********");
thread1.start();
thread2.start();
thread3.start();
}
}

2. 同步代码块

        -->同步代码块的语法格式如下:

synchronized(){
//需要同步访问控制的代码
}

        -->synchronized块中的代码必须获得对象syncObject的锁,具体实现机制与同步方法一样。

        -->由于同步代码块可以针对任意代码块,且可任意指定上锁的对象,故灵活性比较高。

package cn.bdqn.demo03;
public class Site implements Runnable {

private int count=10;
private int num=0;
@Override
public void run() {

while(true){
synchronized (this) {
if(count<=0){
break;
}
count--;
num++;
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"抢到第"+num+"张票,剩余"+count+"张票!");
}
}
}
}

package cn.bdqn.demo03;

public class Test {

public static void main(String[] args) {

Site site=new Site();
Thread thread1=new Thread(site,"逃跑跑");
Thread thread2=new Thread(site,"抢票代理");
Thread thread3=new Thread(site,"黄牛党");
System.out.println("********开始抢票********");
thread1.start();
thread2.start();
thread3.start();
}
}

3. 死锁

        -->多线程在使用同步机制时,存在“死锁”的潜在危险。如果多个线程都处于等待状态而无法唤醒时,就构成了死锁(DeadLock),此时处于等待状态的多个线程占用系统资源,但无法运行,因此不会释放自身的资源。

        -->在编程时应注意死锁问题,避免死锁的有效方法是:

        线程因某个条件未满足而受阻,不能让其继续占有资源;

        如果有多个对象需要互斥访问,应确定线程获得锁的顺序,并保证整个程序以相反的顺序释放锁。

三、线程间通信的必要性

        在前面的介绍中,了解了多线程编程中使用同步机制的重要性,并介绍了如何通过同步来正确地访问共享资源。这些线程之间是相互独立的,并不存在任何的依赖关系。

        它们各自竞争CPU资源,互不相让,并且还无条件地阻止其他线程对共享资源的异步访问。然而,有很多现实问题要求不仅要同步地访问同一共享资源,而且线程间还被彼此牵制,相互通信。

四、实现线程间通信

(1)Java提供了如下3个方法实现线程之间的通信:

        --》wait()方法:调用wait()方法会挂起当前线程,并释放共享资源的锁。

        --》notify()方法:调用任意对象的notify()方法会在因调用该对象的wait()而阻塞的线程中随机选择一个线程解除阻塞,但要等到获得锁后才真正执行。

        --》notifyAll()方法:调用了notifyAll()方法会将因调用该对象的wait()方法而阻塞的所有线程一次性全部解除阻塞。

(2)wait()、notify()和notifyAll()这3个方法都是Object类中final方法,被所有的类继承且不允许重写。这3个方法只能在同步方法或者同步代码块中使用,否则会抛出异常。

        码文不易,本篇文章就介绍到这里,如果想要学习更多Java系列知识,请关注博主,博主带你零基础学习Java知识。与此同时,对于日常生活有困扰的朋友,欢迎阅读我的第四栏目:《国学周更—心性养成之路》,学习技术的同时,我们也注重了心性的养成。

27、JAVA进阶——线程同步和线程间的通信_jvm_02

举报

相关推荐

0 条评论