广告
返回顶部
首页 > 资讯 > 后端开发 > Python >Java 多线程的同步代码块详解
  • 553
分享到

Java 多线程的同步代码块详解

2024-04-02 19:04:59 553人浏览 独家记忆

Python 官方文档:入门教程 => 点击学习

摘要

目录synchronized同步代码块同步方法(this锁)静态同步方法死锁问题lock总结火车站抢票问题 由于现实中买票也不会是零延迟的,为了真实性加入了延迟机制,也就是线程休眠语

火车站抢票问题

由于现实中买票也不会是零延迟的,为了真实性加入了延迟机制,也就是线程休眠语句


package test.MyThread.ticketDemo;
public class RunnableThread implements Runnable{
    private int ticket = 100;
    @Override
    public void run(){
        while(true){
            if(ticket>0){
                try {
                    Thread.sleep(100);  //语句一
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName()+"正在出售第 "+ticket+" 张票");  //语句二
                ticket--;  //语句三
            }
        }
    }
}

package test.MyThread.ticketDemo;
public class ticketDemo1 {
    public static void main(String[] args) {
        RunnableThread r1 = new RunnableThread();
        Thread t1 = new Thread(r1,"窗口一");
        Thread t2 = new Thread(r1,"窗口二");
        Thread t3 = new Thread(r1,"窗口三");
        t1.start();
        t2.start();
        t3.start();
    }
}

在这里插入图片描述

但是结果和我们想象中的不一样,三个窗口卖出了同样的票

这是因为,CPU的操作具有原子性,单独执行一条指令或者说语句,在执行完毕前不会被中断。

三个线程被启动后,都会处于就绪状态,然后开始抢夺CPU执行语句。

1.语句一:Thread.sleep(100);

2.语句二: System.out.println(Thread.currentThread().getName()+“正在出售第 “+ticket+” 张票”);

3.语句三: ticket–;

我将程序中需要执行的三条主要语句列了出来

三条线程中,加入线程一先抢到了CPU,这时就会开始执行语句,也就是至少会完成一条语句一,然后进入休眠。

注:如果语句一不是休眠语句,而是别的语句,那么线程一就可以继续往下执行,因为原子性,正在执行的语句不会被打断,所以只会在一条语句结束,下一条语句未开始时,被抢走CPU或者中断,导致线程退出运行状态,转为就绪或者阻塞状态。所以线程一可以一次性完成多条语句,也有可能刚完成一条语句就被抢走了CPU。

接着,线程二,线程三也抢到了CPU,也开始执行语句一,然后也进入休眠状态。之后线程一二三从休眠中醒来,开始争抢CPU完成语句二,但是三者都在完成语句三之前被抢走了CPU,导致一直没有执行ticket–语句,ticket也就没有减少,因此三条线程一共打印三条输出语句,里面的ticket都是相同。

然后三条线程又开始争抢CPU来完成语句三,一个线程让ticket减一,三个线程减少三张票。完成语句三后,又开始新的循环,三个线程开始争抢CPU完成语句一。

因此,看到的结果会是,三条语句的ticket都相同,然后ticket突然减三,接着又输出三条ticket相同的输出语句。

那么,该如何解决这种情况呢?

这种延迟卖票的问题被称为线程安全问题,要发生线程安全问题需要满足三个条件(任何一共条件不满足都不会造成线程安全问题):

1.是否存在多线程环境

2.是否存在共享数据/共享变量

3.是否有多条语句操作着共享数据/共享变量

火车站延迟卖票问题满足这三个条件,因此造成了线程安全问题,而前两条都不可避免,那么就可以着手于破坏掉第三个条件,让线程安全问题不成立。

思路是将多条语句包装成一个同步代码块,当某个线程执行这个同步代码块的时候,就跟原子性一样,其他的线程不能抢占CPU,只能等这个同步代码块执行完毕。

解决办法:

1.synchronized —— 自动锁

2.lock —— 手动锁

synchronized


synchronized(对象){  
	//可能会发生线程安全问题的代码
}
//这里的对象可以是任意对象,我们可以用 Object obj =  new Object()里面的obj放入括号中

使用synchronized的条件:

1.必须有两个或两个以上的线程同一时间只有一个线程能够执行同步代码块多个线程想要同步时,必须共用同一把锁
synchronized(对象)括号里面的对象就是一把锁

使用synchronized的过程:

1.只有抢到锁的线程才可以执行同步代码块,其余的线程即使抢到了CPU执行权,也只能等待,等待锁的释放。

2.代码执行完毕或者程序抛出异常都会释放锁,然后还未执行同步代码块的线程争抢锁,谁抢到谁就能运行同步代码块。

同步代码块

因此,修改后的代码为:


package test.MyThread.ticketDemo;
public class RunnableThread implements Runnable{
    private int ticket = 100;
    Object obj = new Object();
    @Override
    public void run(){
        while(true){
            synchronized (obj) {
                if (ticket > 0) {
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName() + "正在出售第 " + ticket + " 张票");
                    ticket--;
                }
            }
        }
    }
}

package test.MyThread.ticketDemo;
public class ticketDemo1 {
    public static void main(String[] args) {
    //这里没有改动,只是在上一个代码中加了一把锁
        RunnableThread r1 = new RunnableThread();
        Thread t1 = new Thread(r1,"窗口一");
        Thread t2 = new Thread(r1,"窗口二");
        Thread t3 = new Thread(r1,"窗口三");
        t1.start();
        t2.start();
        t3.start();
    }
}

在这里插入图片描述

可以看出来结果符合我们的预期,是正确的

现在又有了新的问题,那就是如果我在构造线程的RunnableThread类里面加入方法呢?同步代码块里面出现方法时,我们应该怎么“上锁”呢?

同步方法(this锁)

同步方法,在public的后面加上synchronized关键字


package test.MyThread.ticketDemo;
public class RunnableThread1 implements Runnable{
    private int ticket = 100;
    Object obj = new Object();
    public boolean flag = true;
    @Override
    public void run(){
        if(flag==true){
            while(ticket>0){
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                SellTicket1();
            }
        }
    }
//同步方法,在public的后面加上synchronized关键字
    public synchronized void SellTicket1(){
        if(ticket>0){
            System.out.println(Thread.currentThread().getName()+"正在出售第 "+ticket+" 张票");
            ticket--;
        }
    }
}

package test.MyThread.ticketDemo;
public class ticketDemo2 {
    public static void main(String[] args) throws InterruptedException {
        RunnableThread1 r = new RunnableThread1();
        Thread t1 = new Thread(r,"窗口一");
        Thread t2 = new Thread(r,"窗口二");
        t1.start();
        t2.start();
    }
}

this锁

先来看看,如果有两条路径,一条路径是使用同步代码块,但是对象是obj,另一条路径是使用同步方法


package test.MyThread.ticketDemo;
public class TicketWindow2 implements Runnable{
    //定义100张票
    private static int tickets = 100;
    Object obj = new Object();
    int i =0;
    @Override
    public void run() {
        while (true){
            if(i%2==0){
                synchronized (obj){
                    if(tickets>0){
                        try {
                            Thread.sleep(100);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        System.out.println(Thread.currentThread().getName()+" 正在出售第 "+(tickets--)+" 张票");
                    }
                }
            }else {
                sellTicket();
            }
            i++;
        }
    }
    public synchronized void sellTicket(){
            if(tickets>0){
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName()+" 正在出售第 "+(tickets--)+" 张票");
            }
    }
}

在这里插入图片描述

结果出错,说明同步方法的用的对象锁不能是任意的对象,不同的线程应该用相同的锁。同步方法是属于对象,而在这个类里面调用方法的是this对象,也就是this.sellTicket(),因此把this提取出来作为对象锁中的对象。这样多个线程都用的是this锁


package test.MyThread.ticketDemo;
public class TicketWindow2 implements Runnable{
    //定义100张票
    private static int tickets = 100;
    Object obj = new Object();
    int i =0;
    @Override
    public void run() {
        while (true){
            if(i%2==0){
                synchronized (this){
                    if(tickets>0){
                        try {
                            Thread.sleep(100);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        System.out.println(Thread.currentThread().getName()+" 正在出售第 "+(tickets--)+" 张票");
                    }
                }
            }else {
                sellTicket();
            }
            i++;
        }
    }
    public synchronized void sellTicket(){
            if(tickets>0){
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName()+" 正在出售第 "+(tickets--)+" 张票");
            }
    }
}

修改完成后再运行代码,发现没有错误

注:

1.一个线程使用同步方法,另一个线程使用同步代码块this锁,可以实现同步

2.一个线程使用同步方法,另一个线程使用同步代码块,但是不是this锁。这种情况不能实现同步。

静态同步方法

同步方法的锁对象是this,

静态同步方法的锁对象是:这个静态同步方法所属的类的字节码文件

下面代码挺长的,但其实就修改了上面同步方法的代码的两处地方

1.public synchronized void sellTicket(){}改为
public synchronized static void sellTicket(){}

2.synchronized (this){}改为synchronized (TicketWindow2.class){}


package test.MyThread.ticketDemo;
public class TicketWindow2 implements Runnable{
    //定义100张票
    private static int tickets = 100;
    Object obj = new Object();
    int i =0;
    @Override
    public void run() {
        while (true){
            if(i%2==0){
                synchronized (TicketWindow2.class){
                    if(tickets>0){
                        try {
                            Thread.sleep(100);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        System.out.println(Thread.currentThread().getName()+" 正在出售第 "+(tickets--)+" 张票");
                    }
                }
            }else {
                sellTicket();
            }
            i++;
        }
    }
    public synchronized static void sellTicket(){
        if(tickets>0){
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+" 正在出售第 "+(tickets--)+" 张票");
        }
    }
}

main()方法里面创建进程和启动进程的代码,和上面同步方法里面的代码相同

结果也和上面的一样,都不再列出来了

死锁问题


package test.MyThread.ticketDemo;
//两个不同的锁对象
public class LockObject {
    public static final Object lock1 = new Object();
    public static final Object lock2 = new Object();
}

package test.MyThread.ticketDemo;
public class DieLockThread extends Thread{
    public boolean flag;
    public DieLockThread(boolean flag){
        this.flag = flag;
    }
    @Override
    public void run() {
        if(flag){
            synchronized(LockObject.lock1){
                System.out.println("lock1");
                synchronized(LockObject.lock2){
                    System.out.println("lock2");
                }
            }
        }else{
            synchronized(LockObject.lock2){
                System.out.println("lock2");
                synchronized(LockObject.lock1){
                    System.out.println("lock1");
                }
            }
        }
    }
}

package test.MyThread.ticketDemo;
public class DieLockDemo {
    public static void main(String[] args) {
        DieLockThread d1 = new DieLockThread(true);
        DieLockThread d2 = new DieLockThread(false);
        d1.start();
        d2.start();
    }
}

在这里插入图片描述

程序会卡在这一步,不能进行下一步也不能停止

利用有参构造,构造出来的线程d1应该是先获得锁对象LockObject.lock1然后执行打印语句。接着获取锁对象LockObject.lock2,然后打印lock2。

但是这里因为线程d2是先获取的锁对象LockObject.lock2,并占据这个锁对象,然后想获得锁对象LockObject.lock1,但LockObject.lock1此时被线程d1占据着

两个线程都在等待对方释放锁对象,然后进行下一步,但是两者都不释放,导致程序卡死在这里。这就造成了死锁。

lock


package test.MyThread.ticketDemo;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class LockThread implements Runnable{
    private int ticket = 100;
    Lock lock = new ReentrantLock();
    @Override
    public void run(){
        while(ticket>0){
            try{
                lock.lock();
                if(ticket>0){
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName() + " 正在出售第 " + (ticket--) + " 张票");
                }
            }finally {
                lock.unlock();
            }
        }
    }
}

package test.MyThread.ticketDemo;
public class LockDemo {
    public static void main(String[] args) {
        LockThread lt = new LockThread();
        Thread t1 = new Thread(lt,"窗口一");
        Thread t2 = new Thread(lt,"窗口二");
        Thread t3 = new Thread(lt,"窗口三");
        t1.start();
        t2.start();
        t3.start();
    }
}

在这里插入图片描述

结果正确

总结

本篇文章就到这里了,希望能够给你带来帮助,也希望您能够多多关注编程网的更多内容!

--结束END--

本文标题: Java 多线程的同步代码块详解

本文链接: https://www.lsjlt.com/news/155795.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • Java 多线程的同步代码块详解
    目录synchronized同步代码块同步方法(this锁)静态同步方法死锁问题lock总结火车站抢票问题 由于现实中买票也不会是零延迟的,为了真实性加入了延迟机制,也就是线程休眠语...
    99+
    2022-11-12
  • Java多线程之synchronized同步代码块详解
    目录1. 同步方法和同步块,哪种更好?2. synchronized同步代码块3. 如果同步块内的线程抛出异常会发生什么?总结面试题: 1同步方法和同步块,哪种更好? 2.如果同步块...
    99+
    2022-11-13
  • Java多线程同步器代码详解
    同步器为每种特定的同步问题提供了解决方案,同步器是一些使线程能够等待另一个线程的对象,允许它们协调动作。最常用的同步器是CountDownLatch和Semaphore,不常用的是Barrier 和ExchangerSemaphoreSem...
    99+
    2023-05-30
    java 多线程 同步器
  • Java多线程之synchronized同步代码块的示例分析
    小编给大家分享一下Java多线程之synchronized同步代码块的示例分析,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!1. 同步方法和同步块,哪种更好?同步...
    99+
    2023-06-29
  • Java多线程之同步锁-lock详解
    目录一、题目描述二、解题思路三、代码详解一、题目描述 题目: 同步锁出现的目的就是为了解决多线程安全问题。 同步锁的几种方式 synchronized 1、同步代码块 2、同步方法 ...
    99+
    2022-11-13
  • Java多线程同步工具类CountDownLatch详解
    目录简介核心方法CountDownLatch如何使用CountDownLatch运行流程运用场景总结简介 CountDownLatch是一个多线程同步工具类,在多线程环境中它允许多个...
    99+
    2022-11-13
  • Java线程同步Lock同步锁代码示例
    java线程同步原理java会为每个object对象分配一个monitor,当某个对象的同步方法(synchronizedmethods)被多个线程调用时,该对象的monitor将负责处理这些访问的并发独占要求。当一个线程调用一个对象的同步...
    99+
    2023-05-30
    java 线程同步 lock
  • java多线程编程同步器Future和FutureTask解析及代码示例
    publicinterfaceFuture<V>Future表示异步计算的结果。它提供了检查计算是否完成的方法,以等待计算的完成,并获取计算的结果。计算完成后只能使用get方法来获取结果,如有必要,计算完成前可以阻塞此方法。取消...
    99+
    2023-05-30
    java 多线程 ava
  • Java实现多线程同步五种方法详解
    Java实现多线程同步的五种方法包括:1. synchronized关键字:使用synchronized关键字可以实现对共享资源的互...
    99+
    2023-08-14
    Java
  • ReentrantLock从源码解析Java多线程同步学习
    目录前言管程管程模型MESA模型主要特点AQS共享变量资源访问方式主要方法队列node节点等待状态ReentrantLock源码分析实例化ReentrantLock加锁A线程加锁成功...
    99+
    2023-05-16
    Java多线程ReentrantLock Java ReentrantLock
  • C++中的多线程同步问题详解
    C++中的多线程同步问题详解在并发编程中,多线程同步是一个重要的问题。当多个线程同时访问共享资源时,会引发各种问题,如竞态条件(Race Condition)、死锁(Deadlock)和活锁(Livock),这些问题都会导致程序的不确定性和...
    99+
    2023-10-22
    C++ 多线程 同步问题
  • Java线程之线程同步synchronized和volatile详解
    上篇通过一个简单的例子说明了线程安全与不安全,在例子中不安全的情况下输出的结果恰好是逐个递增的(其实是巧合,多运行几次,会产生不同的输出结果),为什么会产生这样的结果呢,因为建立的Count对象是线程共享的,一个线程改变了其成员变量num值...
    99+
    2023-05-30
    java 线程 synchronized
  • Java详解多线程协作作业之信号同步
    目录一、信号同步二、基于时间维度1、CountDownLatch2、CyclicBarrier三、基于信号维度一、信号同步 多线程很多时候是协作作业。比如4个线程对电商数据分季度统计...
    99+
    2022-11-13
  • Java 多线程同步技术详解,Windows 平台适用!
    在多线程编程中,同步是一个非常重要的概念。在多个线程同时访问共享资源的情况下,如果没有同步,就有可能会出现竞态条件和数据不一致等问题。Java 提供了多种同步技术,本文将详细介绍 Java 多线程同步技术。 synchronized 关...
    99+
    2023-09-09
    同步 windows 教程
  • C++详解多线程中的线程同步与互斥量
    目录线程同步互斥量线程同步 #include <stdio.h> #include <pthread.h> #include <unistd.h>...
    99+
    2022-11-13
  • Java线程同步的四种方式详解
    Java线程同步属于Java多线程与并发编程的核心点,需要重点掌握,下面我就来详解Java线程同步的4种主要的实现方式 什么是Java线程同步 当使用多个线程来访问同一个数据时,将会...
    99+
    2023-02-18
    Java 线程同步 Java线程同步的四种方式 Java使用synchronized关键字线程同步 Java使用ReentrantLock线程同步 Java使用原子变量实现线程同步 JavaThrea
  • c#中多线程间的同步示例详解
    目录一、引入二、Lock三、Monitor四、Interlocked五、Semaphore六、Event七、Barrier八、ReaderWriterLockSlim九、Mutex十...
    99+
    2022-11-12
  • Java线程死锁代码详解
    public class JStackDemo { public static void main(String[] args) { Thread t1...
    99+
    2022-11-12
  • java多线程编程技术详解和实例代码
     java多线程编程技术详解和实例代码1.   Java和他的API都可以使用并发。可以指定程序包含不同的执行线程,每个线程都具有自己的方法调用堆栈和程序计数器,使得线程在与其他线程并发地执行能够共享程序范围内...
    99+
    2023-05-31
    java 多线程 编程
  • Java线程同步引用的基本代码介绍
    这篇文章主要讲解了“Java线程同步引用的基本代码介绍”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“Java线程同步引用的基本代码介绍”吧!Java线程同步引用有很多的使用范围,在不断的使用...
    99+
    2023-06-17
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作