java中线程同步机制synchronized,互斥锁,死锁,释放锁的详解(代码片段)

路宇 路宇     2022-12-11     754

关键词:

博主前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住也分享一下给大家,
👉点击跳转到网站

一、线程同步机制synchronized的理解
二、synchronized的具体使用

下面可以通过同步机制,解决多线程卖票,出现的超卖问题,代码如下

public class SellTicket 
    public static void main(String[] args) 
//        SellTicket01 sellTicket01 = new SellTicket01();
//        SellTicket01 sellTicket02 = new SellTicket01();
//        SellTicket01 sellTicket03 = new SellTicket01();
//
//        //这里会出现超卖现象
//        sellTicket01.start();//启动售票线程
//        sellTicket02.start();//启动售票线程
//        sellTicket03.start();//启动售票线程
        /*
        输出结果:
        窗口 Thread-0 售出一张票剩余票数=2
        窗口 Thread-1 售出一张票剩余票数=-1
        售票结束
        窗口 Thread-0 售出一张票剩余票数=-2
        售票结束
        窗口 Thread-2 售出一张票剩余票数=0
        售票结束
         */

//        System.out.println("----使用实现接口的方式来售票----");
//        SellTicket02 sellTicket02 = new SellTicket02();
//        new Thread(sellTicket02).start();//第1个线程-窗口
//        new Thread(sellTicket02).start();//第2个线程-窗口
//        new Thread(sellTicket02).start();//第3个线程-窗口
        /*
        输出结果
        窗口 Thread-0 售出一张票剩余票数=2
        窗口 Thread-2 售出一张票剩余票数=1
        窗口 Thread-2 售出一张票剩余票数=0
        售票结束
        窗口 Thread-0 售出一张票剩余票数=-1
        售票结束
        窗口 Thread-1 售出一张票剩余票数=-2
        售票结束
        */


        System.out.println("----使用线程同步的方式来解决超卖票的情况----");
        SellTicket03 sellTicket03 = new SellTicket03();
        new Thread(sellTicket03).start();//第1个线程-窗口
        new Thread(sellTicket03).start();//第2个线程-窗口
        new Thread(sellTicket03).start();//第3个线程-窗口
        /*
        窗口 Thread-0 售出一张票剩余票数=6
        窗口 Thread-0 售出一张票剩余票数=5
        窗口 Thread-0 售出一张票剩余票数=4
        窗口 Thread-0 售出一张票剩余票数=3
        窗口 Thread-0 售出一张票剩余票数=2
        窗口 Thread-0 售出一张票剩余票数=1
        窗口 Thread-2 售出一张票剩余票数=0
        售票结束...
        售票结束...
        售票结束...
         */
    


//使用Thread方式
class SellTicket01 extends Thread 
    private static int ticketNum = 100;//让多个线程共享

    @Override
    public void run() 
        while (true) 
            //在判断这个条件的时候,三个线程会同时进来 当ticketNum等于1时,
            //三个线程都进来了,会出现超卖的情况
            if (ticketNum <= 0) 
                System.out.println("售票结束");
                break;
            
            //休眠50毫秒,模拟
            try 
                Thread.sleep(50);
             catch (InterruptedException e) 
                e.printStackTrace();
            

            System.out.println("窗口 " + Thread.currentThread().getName() + " 售出一张票"
                    + "剩余票数=" + (--ticketNum));
        
    



//实现接口方式
class SellTicket02 implements Runnable 
    private int ticketNum = 100;

    @Override
    public void run() 
        while (true) 
            if (ticketNum <= 0) 
                System.out.println("售票结束");
                break;
            
            //休眠50毫秒,模拟
            try 
                Thread.sleep(50);
             catch (InterruptedException e) 
                e.printStackTrace();
            

            System.out.println("窗口 " + Thread.currentThread().getName() + " 售出一张票"
                    + "剩余票数=" + (--ticketNum));
        
    



//实现接口方式,使用synchronized实现线程同步
class SellTicket03 implements Runnable 
    private int ticketNum = 100;
    private boolean loop = true;

    public synchronized void sell() //同步方法,在同一时刻,只能有一个线程来执行sell方法
        if (ticketNum <= 0) 
            System.out.println("售票结束...");
            loop = false;
            return;
        
        //休眠50毫秒,模拟
        try 
            Thread.sleep(50);
         catch (InterruptedException e) 
            e.printStackTrace();
        

        System.out.println("窗口 " + Thread.currentThread().getName() + " 售出一张票"
                + "剩余票数=" + (--ticketNum));
    

    @Override
    public void run() 
        while (loop) 
            sell();//sell方法是一个同步方法
        
    

分析同步原理

三、互斥锁的介绍

下面演示在代码块中加锁,和方法上加锁,还是以上面的多线程卖票为例

//实现接口方式,使用synchronized实现线程同步
class SellTicket03 implements Runnable 
    private int ticketNum = 100;
    private boolean loop = true;
    Object object = new Object();//也可以用同一个对象,比如object,因为是三个线程共享一个object对象,满足三个线程共享一个对象

    //同步方法(静态的)的锁为当前类本身
    //1.public synchronized static void m1()锁 是加在SellTicket03.class
    //2.如果在静态方法中,实现一个同步代码块
    /*
        synchronized (SellTicket03.class) 
            System.out.println("m2");
        
     */
    public synchronized static void m1() 
    

    public static void m2() 
        synchronized (SellTicket03.class) 
            System.out.println("m2");
        
    

    //1. public synchronized void sell() 就是一个同步方法
    //2.也可以在代码块上写synchronized ,同步代码块,互斥锁还是在this对象
    public /*synchronized*/ void sell() //同步方法,在同一时刻,只能有一个线程来执行sell方法
        synchronized (/*this*/object) 
            if (ticketNum <= 0) 
                System.out.println("售票结束...");
                loop = false;
                return;
            
            //休眠50毫秒,模拟
            try 
                Thread.sleep(50);
             catch (InterruptedException e) 
                e.printStackTrace();
            

            System.out.println("窗口 " + Thread.currentThread().getName() + " 售出一张票"
                    + "剩余票数=" + (--ticketNum));
        
    

    @Override
    public void run() 
        while (loop) 
            sell();//sell方法是一个同步方法
        
    

互斥锁的注意细节如下

四、线程的死锁

public class DeadLock_ 
    public static void main(String[] args) 
        //模拟死锁现象
        DeadLockDemo A = new DeadLockDemo(true);
        A.setName("A线程");
        DeadLockDemo B = new DeadLockDemo(false);
        B.setName("B线程");
        A.start();
        B.start();
    


//线程
class DeadLockDemo extends Thread 
    static Object o1 = new Object();//保证多线程,共享一个对象,这里使用static
    static Object o2 = new Object();
    boolean flag;

    public DeadLockDemo(boolean flag) 
        this.flag = flag;
    

    @Override
    public void run() 
        //下面业务逻辑分析
        //1.如果flag为true,线程A就会先得到/持有 o1 对象锁,然后尝试去获取o2对象锁
        //2.如果线程A 得不到o2对象锁,就会Blocked
        //3.如果flag为false,线程B就会先得到/持有 o2 对象锁,然后尝试去获取o1对象锁
        //2.如果线程B 得不到o1对象锁,就会Blocked
        if (flag) 
            synchronized (o1) //对象互斥锁,下面是同步代码
                System.out.println(Thread.currentThread().getName() + " 进入1");
                synchronized (o2) //这里获得li对象的监视权
                    System.out.println(Thread.currentThread().getName() + "进入2");
                
            
         else 
            synchronized (o2) 
                System.out.println(Thread.currentThread().getName() + " 进入3");
                synchronized (o1) //这里获得li对象的监视权
                    System.out.println(Thread.currentThread().getName() + "进入4");
                
            
        
    

输出结果

B线程 进入3
A线程 进入1

之后就卡在这里了,写代码时一定要避免

下面操作会释放锁

下面操作不会释放锁

线程相关的练习题如下

代码如下

public class HomeWork01 
    public static void main(String[] args) 
        A a = new A();
        a.start();
        B b = new B(a);
        b.start();
    


class A extends Thread 
    private boolean loop = true;

    public void setLoop(boolean loop) 
        this.loop = loop;
    

    @Override
    public void run() 
        while (loop) 
            System.out.println((int) (Math.random() * 100 + 1));
            try 
                Thread.sleep(1000);
             catch (InterruptedException e) 
                e.printStackTrace();
            
        
        System.out.println("a线程退出...");
    


class B extends Thread 
    private A a;

    public B(A a) //构造器中,传入A类对象
        this.a = a;
    

    @Override
    public void run() 
        while (true) 
            //接收到用户的输入
            System.out.println("请输入命令");
            Scanner scanner = new Scanner(System.in);
            char c = scanner.next().toUpperCase().charAt(0);
            if (c == 'Q') 
                //以通知的方式结束A线程
                a.setLoop(false);
                break;
            
        
        System.out.println("b线程退出...");
    

输出结果如下

85
请输入命令
8
41
79
81
75
41
29
Q
b线程退出...
a线程退出...

练习题二


代码如下

public class HomeWork02 
    public static void main(String[] args) 
        Card card = new Card();
        new Thread(card).start();
        new Thread(card).start();
    


//编程取款的线程
//1.因为这里涉及到多个线程共享线程资源,所以我们使用实现Runnable方式
class Card implements Runnable 
    private boolean loop = true;
    private int balance = 10000;

    @Override
    public void run() 
        while (loop) 
            //解读:
            //1.这里使用synchronized实现了线程同步
            //2.当多个线程执行到这里时,就会去争夺this对象锁
            //3.哪个对象争夺到(获取)this对象锁,就执行synchronized代码块,执行完成后,会释放this对象锁
            //4.争夺不到this对象锁,就blocked,准备继续争夺
            //5.this对象锁 是非公平锁
            synchronized (this) 
                if (balance < 1000) 
                    System.out.println("余额不足..");
                    loop = false;
                    return;
                
                System.out.println(Thread.currentThread().getName() + " 取出1000 剩余余额为:" + (balance -= 1000));
            
            try 
                Thread.sleep(1000);
             catch (InterruptedException e) 
                e.printStackTrace();
            
        
    




输出结果如下

Thread-0 取出1000 剩余余额为:9000
Thread-1 取出1000 剩余余额为:8000
Thread-1 取出1000 剩余余额为:7000
Thread-0 取出1000 剩余余额为:6000
Thread-1 取出1000 剩余余额为:5000
Thread-0 取出1000 剩余余额为:4000
Thread-1 取出1000 剩余余额为:3000
Thread-0 取出1000 剩余余额为:2000
Thread-1 取出1000 剩余余额为:1000
Thread-0 取出1000 剩余余额为:0
余额不足..
余额不足..

synchronized

...线程安全问题?  二.如何解决线程安全问题?  三.synchronized同步方法或者同步块  若有不正之处,请多多谅解 查看详情

java多线程synchronized关键字详解(代码片段)

...多个线程同时访问时可能出现的问题。同步机制可以使用synchronized关键字实现。当synchronized关键字修饰一个方法的时候,该方法叫做同步方法。当synchronized方法执行完或发生异常时,会自动释放锁。下面通过一个例子来... 查看详情

线程------java锁机制(代码片段)

...是谁,2.谁持有了锁。锁的种类:我们先来看一下synchronized锁的种类:  1、对象锁:带有synchronized的同步方法或者带有synchronized(this)的同步代码块。publicsynchronizedvoidgetXXX()或者publicvoidgetXXX()synchronized() ... 查看详情

java中啥同步啥是异步分别用在啥地方

...别用在什么地方上传下载文件要用什么步java同步指的是synchronized机制,而非synchronized的都是异步,弄懂同步的概念就大致明白了两者的差别。有关同步:synchronized用来修饰一个方法或者一个代码块,它用来保证在同一时刻最多... 查看详情

如何解决java多线程问题

...问,所以我们只需针对方法提出一套机制,这套机制就是synchronized关键字,它包括两种用法:synchronized方法和synchronized块。1.synchronized方法:通过在方法声明中加入synchronized关键字来声明synchronized方法。如:1.pu 查看详情

synchronized同步块和volatile同步变量

阅读目录synchronized同步块volatile同步变量Java语言包含两种内在的同步机制:同步块(或方法)和volatile变量。这两种机制的提出都是为了实现代码线程的安全性。其中Volatile变量的同步性较差(但有时它更简单并且开销更低),... 查看详情

java多线程-synchronized同步方法

1、synchronized方法与锁对象  线程锁的是对象。  1)A线程先持有object对象的Lock锁,B线程可以以异步的方式调用object对象中的非synchronized类型的方法  2)A线程先持有object对象的Lock锁,B线程如果在这时调用object对象中的syn... 查看详情

volatile是否就是原子性/线程同步的

...的,为了解决线程并发的问题,在语言内部引入了同步块synchronized和volatile关键字机制。 synchronized  同步块大家都比较熟悉,通过synchronized关键字来实现,所有加上synch 查看详情

多线程简介

...(2)多线程同步机制。在需要同步的方法的方法签名中加入synchronized关键字。使用synchronized块对需要进行同步的代码段进行同步。使用JDK5中提供的java.util.concurrent.lock包中的Lock对象。一段synchronized的代码被一个线程执行之前,他要... 查看详情

java中同步有几种方式啊

1。同步代码块:synchronized(同一个数据)同一个数据:就是N条线程同时访问一个数据。2。同步方法:publicsynchronized数据返回类型方法名()就是使用synchronized来修饰某个方法,则该方法称为同步方法。对于同步方法而言,无需显示... 查看详情

java多线程

使用synchronized锁实现线程同步为什么要用线程同步我们先来看下这段代码的运行结果:Java学习交流群:495273252在多线程上篇博客已经介绍过了,JVM采用的是抢占式调度模型,当一个线程sleep的时候,其它线程会抢占CPU资源。如果... 查看详情

java多线程synchronized同步

非线程安全问题“非线程安全”问题存在于“实例变量”中,如果是方法内部的私有变量,则不存在“非线程问题”。也即是说,方法中的变量永远是线程安全的。如果多个线程共同访问1个对象中的实例变量... 查看详情

synchronized原理

...在本地缓存的某个副本上继续操作从而引起不一致。二、synchronized的用法根据修饰对象分类1、同步方法(1)     同步非静态方法PublicsynchronizedvoidmethodName()……(2)     同步静态方法Publicsyn... 查看详情

[转]java中volatile关键字的含义

...题,在语言内部引入了同步块和volatile关键字机制。 synchronized 同步块大家都比较熟悉,通过synchronized关键字来实现,所有加上synchronized和块语句,在多线程访问的时候,同一时刻只能有一个线程能够用synchronized修饰的方... 查看详情

synchronized(this)与synchronized(class)理解(代码片段)

 1.概念synchronized是Java中的关键字,是利用锁的机制来实现同步的。锁机制有如下两种特性:互斥性:即在同一时间只允许一个线程持有某个对象锁,通过这种特性来实现多线程中的协调机制,这样在同一时间只有一个线程对... 查看详情

java并发编程:使用synchronized获取互斥锁的几点说明

...制,以确保在某一时刻,方法内只允许有一个线程。采用synchronized修饰符实现的同步机制叫做互斥锁机制,它所获得的锁叫做互斥锁。每个对象都有一个monitor(锁标记),当线程拥有这个锁标记时才能访问这 查看详情

线程同步

...程共享数据) C:有多条语句操作共享数据?同步代码块: synchronized(锁对象) 需要同步的代码; 注 1.任意一个对象都可以作为锁对象 (凡是对象的内部都维护了一个状态,Java同步机制就是使用了对象中的状态作为锁的标识) 2.在... 查看详情

java中多线程安全性和同步的常用方法

...码块、同步方法、Lock; 4、同步代码块 代码如下:synchronized(同步监视器)//需要被同 查看详情