多线程之锁机制(代码片段)

StoneGeek StoneGeek     2022-11-14     645

关键词:

前言

  在Java并发编程实战,会经常遇到多个线程访问同一个资源的情况,这个时候就需要维护数据的一致性,否则会出现各种数据错误,其中一种同步方式就是利用Synchronized关键字执行锁机制,锁机制是先给共享资源上锁,只有拿到锁的线程才可以访问共享资源,其他线程进入等待状态。下面将以实例代码讲解一下

一、wait()、nofity()、nofityAll()讲解

  示例代码

package thread;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.TimeUnit;

/**
 * Created by StoneGeek on 2018/5/19.
 * 博客地址:http://www.cnblogs.com/sxkgeek
 * 当线程执行wait()时,会把当前的锁释放,然后让出CPU,进入等待状态。
 * 当线程执行notify()/notifyAll()方法时,会唤醒一个处于等待状态该对象锁的线程,然后继续往下执行,直到执行完退出对象锁锁住的区域(synchronized修饰的代码块)后再释放锁
 * 个人认为synachronized()执行完后会释放锁
 */
public class WaitNotify 
    static boolean flag = true;
    static Object lock = new Object();

    public static void main(String[] args) throws Exception 
        Thread waitThread = new Thread(new Wait(), "WaitThread");
        waitThread.start();
        TimeUnit.SECONDS.sleep(1);
        Thread notifyThread = new Thread(new Notify(), "NotifyThread");
        notifyThread.start();
    

    static class Wait implements Runnable 
        public void run() 
            // 加锁,拥有lock的Monitor
            synchronized (lock) 
                // 当条件不满足时,继续wait,同时释放了lock的锁
                while (flag) 
                    System.out.println(Thread.currentThread().getName()
                            + " flag is true. wait@ "
                            + new SimpleDateFormat("HH:mm:ss")
                            .format(new Date()));
                    try 
                        lock.wait();
                        System.out.println("此处继续执行"+Thread.currentThread().getName());
//                        flag=true;
                     catch (InterruptedException e) 
                        e.printStackTrace();
                    
                
                // 条件满足时,完成工作
                System.out.println(Thread.currentThread().getName()
                        + " flag is false. running@ "
                        + new SimpleDateFormat("HH:mm:ss").format(new Date()));
            
            synchronized (lock)
                System.out.println(Thread.currentThread().getName()+"执行结束");
            
        
    

    // wait()会立刻释放synchronized(obj)中的obj锁,以便其他线程可以执行obj.notify()
    // 但是notify()不会立刻立刻释放sycronized(obj)中的obj锁,必须要等notify()所在线程执行完synchronized(obj)块中的所有代码才会释放这把锁.
    // yield(),sleep()不会释放锁
    static class Notify implements Runnable 
        public void run() 
            // 加锁,拥有lock的Monitor
            synchronized (lock) 
                // 获取lock的锁,然后进行通知,通知时不会释放lock的锁,
                // 直到当前线程释放了lock后,WaitThread才能从wait方法中返回
                System.out.println(Thread.currentThread().getName()
                        + " hold lock. notify @ "
                        + new SimpleDateFormat("HH:mm:ss").format(new Date()));
                lock.notifyAll();
                flag = false;
            
            // 再次加锁
            synchronized (lock) 
                System.out.println(Thread.currentThread().getName()
                        + " hold lock again. sleep@ "
                        + new SimpleDateFormat("HH:mm:ss").format(new Date()));
            
            synchronized (lock)
                System.out.println(Thread.currentThread().getName()+"执行结束");
            
        
    

  执行结果如下

1  WaitThread flag is true. wait@ 20:50:39
2  NotifyThread hold lock. notify @ 20:50:40
3  NotifyThread hold lock again. sleep@ 20:50:40
4  NotifyThread执行结束
5  此处继续执行WaitThread
6  WaitThread flag is false. running@ 20:50:40
7  WaitThread执行结束

  解释:

  首先创建一个lock对象,然后给这个lock上锁来对多个进程同步,flag是一个标志,用来跳出while循环。

     当线程执行wait()时,会把当前的锁释放,然后让出CPU,进入等待状态。

  此时轮到notifythread线程,并且执行notifyAll(),这个意思是能够唤醒所有正在等待这个lock对象的monitor的线程,但是

  必须要等notify()所在线程执行完synchronized(obj)块中的所有代码才会释放这把锁,

  此时接着waitthread被唤醒,继续执行while循环,执行完之后,由于flag在notifythread中置为false,所以跳出while循环(如果在实例代码的wail()后加flag=true结果是截然不同,由于notirythread进程执行完,此时会一直陷入wait,大家可以试试),

  执行console打印5 6 7

  notify()与notifyAll()的区别 

  notify()方法能够唤醒一个正在等待该对象的monitor的线程,当有多个线程都在等待该对象

  的monitor的话,则只能唤醒其中一个线程

  而调用notifyAll()方法能够唤醒所有正在等待这个对象的monitor的线程

  当时的疑惑

  (1)既然notify或者notifyAll需要执行完synchronized块中的内容,那么他还有什么存在的价值的

    后来执行完之后,才发现要是没有这个方法,那么synchronized块执行完之后,waitthread还是在等待状态,无法被唤醒。

  (2)wait被notify唤醒之后,是接着执行,所以console打印5,并不是从头执行(如果在实例代码的wail()后加flag=true结果是截然不同,由于notirythread进程执行完,waitthread进程重新执行wait方法,此时会一直陷入wait,无其他进程唤醒此进程)

二、wait()/wait(long)和sleep(long)方法的区别

  将示例代码中的lock.wait()改为Thread.sleep(1000),console打印

WaitThread flag is true. wait@ 21:29:49
此处继续执行WaitThread
WaitThread flag is true. wait@ 21:29:50
此处继续执行WaitThread
WaitThread flag is true. wait@ 21:29:51
此处继续执行WaitThread
WaitThread flag is true. wait@ 21:29:52
此处继续执行WaitThread

  由此说明sleep并没有释放锁。

  区别:

    1、Sleep(long)是Thread的方法,而wait()/wait(long)是Object的方法

    2、Sleep(long)可以放在sychnoized块内也可以不放在里面,但是wait()/wait(long)必须放在语句块内

    3、Sleep(long)不释放锁,只是让当前线程暂停一段时间,而wait()/wait(long)是释放锁

    4、wait()将当前线程放到阻塞队列,只有调用notify()/notifyAll()方法后,才将其从阻塞队列中移动到就绪队列,等待被CPU调度,而wait(long)方法执行后就是放到阻塞队列,等待时间到期或者被wait()/wait(long)唤醒后就可以放到就绪队列被CPU调度

  目前还有一个疑惑,就是线程中的run方法有两个同样的synchroized(lock),是不是跟一个synchroized(lock)效果是一样的,目前就运行结果来看是这样子的!

 

java浅谈线程安全之锁(代码片段)

...概念。1、本地锁:在单进程的系统中,存在多个线程去同时操作某个共享变量时,就需要使用本地锁,最常用的关键字:synchronized2、分布式锁:在分布式系统中,我们知道会有多台服务器同时部署一... 查看详情

java:java学习笔记之锁机制的简单理解和使用(代码片段)

...见面试题参考锁机制1、背景问题的产生:Java允许多线程并发控制,当多个线程同时操作一个可共享的资源变量时(如数据的增删改查),将会导致数据不准确,相互之间产生冲突。如下例:假设有一... 查看详情

java:java学习笔记之锁机制的简单理解和使用(代码片段)

锁机制的简单理解和使用锁机制1、背景2、定义3、锁的种类4、乐观锁VS悲观锁4.1、悲观锁4.2、乐观锁4.3、举例说明4.4、CAS算法4.4.1、CAS使用4.4.2、CAS缺点4.4.3、总结5、自旋锁VS适应性自旋锁5.1、自旋锁5.2、适应性自旋锁5.3、总结6... 查看详情

java多线程与并发模型之锁

这是一篇总结Java多线程开发的长文。文章是从Java创建之初就存在的synchronized关键字引入,对Java多线程和并发模型进行了探讨。希望通过此篇内容的解读能帮助Java开发者更好的理清Java并发编程的脉络。互联网上充斥着对Java多线... 查看详情

多线程进阶之等待唤醒机制(代码片段)

/**线程间通讯实例*多个线程在处理同一资源,但是任务不同*///资源classResourceStringname;Stringsex;//输入classInputimplementsRunnableResourcer;//类对象publicInput(Resourcer)//类对象做构造参数-->初始化资源this.r=r;publicvoidrun()intx=0;whil 查看详情

多线程机制(代码片段)

一.多线程的基本使用1.1 线程的创建和使用        进程是资源分配的基本单位,所有与该进程有关的资源,如打印机,输入的缓冲队列等都被记录在进程控制块中,以表示该进程拥有这些资源或正在使用它们... 查看详情

sqlalchemy多线程下事务隔离机制详解(代码片段)

1.起因通过开启多线程,并发查询订单详情信息,通过将不同订单对象发送给不同线程,执行完所需要的业务逻辑之后,对订单对象的数据进行修改,然后执行commit,查询数据库发现数据没有更新,且后台日志没有任何的报错**... 查看详情

mysql原理篇之锁--14(代码片段)

Mysql原理篇之锁--14解决并发事务带来问题的两种基本方式一致性读(ConsistentReads)锁定读(LockingReads)共享锁和独占锁锁定读的语句写操作多粒度锁MySQL中的行锁和表锁其他存储引擎中的锁InnoDB存储引擎中的锁InnoD... 查看详情

数据库小技能:oracle基础之锁(代码片段)

文章目录I锁1.1锁定数据行1.2锁类型II事务2.1事务ACID特性2.2事务控制语句I锁什么是锁?锁(lock)机制用于管理对共享资源的并发访问。1.1锁定数据行forupdate和forupdatenowait是对操作的数据行进行加锁,在事务提交前防止其他操作对数... 查看详情

数据库小技能:oracle基础之锁(代码片段)

文章目录I锁1.1锁定数据行1.2锁类型II事务2.1事务ACID特性2.2事务控制语句I锁什么是锁?锁(lock)机制用于管理对共享资源的并发访问。1.1锁定数据行forupdate和forupdatenowait是对操作的数据行进行加锁,在事务提交前防止其他操作对数... 查看详情

java之锁-volatile

锁是JAVA多线程关键,也是面试中必问的,在此好好总结一下。(先要从进程和线程说起,此处先欠下,回头专门说一下操作系统是怎么管理进程和线程的)说到多线程就要说说JAVA的内存模型:图片来自于网络。  Java内存模型... 查看详情

java多线程中断机制三种方法源码分析(代码片段)

interrupt()、interrupted()、isInterrupted()这三个方法都涉及到多线程的一个知识点----中断机制三个中断方法中断标识位是JDK源码看不到的,是虚拟机线程实现层面的。下面结合代码逐一看一下这三个方法的作用,以及为什么中断标识... 查看详情

java多线程之中断机制(如何处理中断?)(代码片段)

JAVA多线程之中断机制(如何处理中断?)-大熊猫同学-博客园一,介绍这篇文章主要记录使用interrupt()方法中断线程,以及如何对InterruptedException进行处理。感觉对InterruptedException异常进行处理是一件谨慎且有技巧的活儿... 查看详情

java并发之锁的使用浅析

    锁像synchronized同步块一样,是一种线程同步机制。让自Java5开始,java.util.concurrent.locks包提供了另一种方式实现线程同步机制——Lock。那么问题来了既然都可以通过synchronized来实现同步访问了,那么为什... 查看详情

redis6.0引入的多线程机制简介(代码片段)

简单介绍了Redis6.0的多线程模型。文章目录1Redis与单线程2Redis6.0的多线程此前我们学习了Redis的单线程模型:Redis的线程模型—文件事件处理器的详解,但是Redis6.0版本中引入多线程,下面简单的来看看吧!1Redis与... 查看详情

多线程的互斥锁应用raii机制(代码片段)

什么是RAII机制RAII是ResourceAcquisitionIsInitialization(翻译成“资源获取即初始化”)的简称,是C++语言的一种管理资源、避免资源泄漏的惯用法,该方法依赖构造函数资和析构函数的执行机制。RAII的做法是使用... 查看详情

线程机制与事件机制(代码片段)

进程与线程1<!DOCTYPEhtml>2<htmllang="en">3<head>4<metacharset="UTF-8">5<title>01_进程与线程</title>6</head>7<body>8<!--91.进程:程序的一次执行,它占有一片独有的内存空间102.线程:CPU的基本调度单位,是程序执... 查看详情

多线程的通知机制(代码片段)

典型应用场景–阻塞队列生产者(Productor)—放入元素消费者(Consumer)—取出元素生产者放元素时,队列满了,该怎么办?消费者取元素时,队列空了,该怎么办?没有采用阻塞队列之前采用一种轮询模式࿰... 查看详情