精华内容
下载资源
问答
  • 主要介绍了Java lock同步锁使用实例解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
  • 本篇文章主要介绍了Android多线程之同步锁的使用,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
  • spring实现集群同步锁

    2016-08-16 18:36:52
    spring实现集群同步锁
  • 主要介绍了Java 同步锁(synchronized)详解及实例的相关资料,需要的朋友可以参考下
  • 同步锁-线程安全问题解决方案

    万次阅读 多人点赞 2021-03-21 15:12:05
    1 同步锁 1.1 前言 经过前面多线程编程的学习,我们遇到了线程安全的相关问题,比如多线程售票情景下的超卖/重卖现象. 上节笔记点这里-进程与线程笔记 我们如何判断程序有没有可能出现线程安全问题,主要有以下三个条件...

    1 同步锁

    1.1 前言

    经过前面多线程编程的学习,我们遇到了线程安全的相关问题,比如多线程售票情景下的超卖/重卖现象.
    上节笔记点这里-进程与线程笔记

    我们如何判断程序有没有可能出现线程安全问题,主要有以下三个条件:

    在多线程程序中 + 有共享数据 + 多条语句操作共享数据

    多线程的场景和共享数据的条件是改变不了的(就像4个窗口一起卖100张票,这个是业务)
    所以思路可以从第3点"多条语句操作共享数据"入手,既然是在这多条语句操作数据过程中出现了问题
    那我们可以把有可能出现问题的代码都包裹起来,一次只让一个线程来执行

    1.2 同步与异步

    那怎么"把有可能出现问题的代码都包裹起来"呢?我们可以使用synchronized关键字来实现同步效果
    也就是说,当多个对象操作共享数据时,可以使用同步锁解决线程安全问题,被锁住的代码就是同步的

    接下来介绍下同步与异步的概念:
    同步:体现了排队的效果,同一时刻只能有一个线程独占资源,其他没有权利的线程排队。
    坏处就是效率会降低,不过保证了安全。
    异步:体现了多线程抢占资源的效果,线程间互相不等待,互相抢占资源。
    坏处就是有安全隐患,效率要高一些。

    1.3 synchronized同步关键字

    1.3.1 写法

    synchronized (锁对象){
    需要同步的代码(也就是可能出现问题的操作共享数据的多条语句);
    }

    1.3.2 前提

    同步效果的使用有两个前提:

    • 前提1:同步需要两个或者两个以上的线程(单线程无需考虑多线程安全问题)
    • 前提2:多个线程间必须使用同一个锁(我上锁后其他人也能看到这个锁,不然我的锁锁不住其他人,就没有了上锁的效果)

    1.3.3 特点

    1. synchronized同步关键字可以用来修饰代码块,称为同步代码块,使用的锁对象类型任意,但注意:必须唯一!
    2. synchronized同步关键字可以用来修饰方法,称为同步方法
    3. 同步的缺点是会降低程序的执行效率,但我们为了保证线程的安全,有些性能是必须要牺牲的
    4. 但是为了性能,加锁的范围需要控制好,比如我们不需要给整个商场加锁,试衣间加锁就可以了

    为什么同步代码块的锁对象可以是任意的同一个对象,但是同步方法使用的是this呢?
    因为同步代码块可以保证同一个时刻只有一个线程进入
    但同步方法不可以保证同一时刻只能有一个线程调用,所以使用本类代指对象this来确保同步

    同步与异步

    1.4.1练习-改造售票案例

    创建包: cn.tedu.tickets
    创建类:TestRunnableV2.java

    package cn.tedu.tickets;
    
    /*本类用于改造多线程售票案例,解决数据安全问题*/
    public class TestRunnableV2 {
        public static void main(String[] args) {
            //5.创建目标业务类对象
            TicketR2 target = new TicketR2();
            //6.创建线程对象
            Thread t1 = new Thread(target);
            Thread t2 = new Thread(target);
            Thread t3 = new Thread(target);
            Thread t4 = new Thread(target);
            //7.以多线程的方式运行
            t1.start();
            t2.start();
            t3.start();
            t4.start();
        }
    }
    
    /*1.多线程中出现数据安全问题的原因:多线程程序+共享数据+多条语句操作共享数据*/
    /*2.同步锁:相当于给容易出现问题的代码加了一把锁,包裹了所有可能会出现数据安全问题的代码
     * 加锁之后,就有了同步(排队)的效果,但是加锁的话,需要考虑:
     * 锁的范围:不能太大,太大,干啥都得排队,也不能太小,太小,锁不住,还是会有安全隐患*/
    //1.创建自定义多线程类
    class TicketR2 implements Runnable {
        //3.定义成员变量,保存票数
        int tickets = 100;
        //创建锁对象
        Object o = new Object();
    
        //2.实现接口中未实现的方法,run()中放着的是我们的业务
        @Override
        public void run() {
            //4.通过循环结构完成业务
            while (true) {
                /*3.同步代码块:synchronized(锁对象){会出现安全隐患的所有代码}
                 * 同步代码块在同一时刻,同一资源只会被一个线程独享*/
                /*这种写法不对,相当于每个线程进来的时候都会new一个锁对象,线程间使用的并不是同一把锁*/
                //synchronized (new Object()){
                //修改同步代码块的锁对象为成员变量o,因为锁对象必须唯一
                synchronized (o) {//同步代码块解决的是重卖的问题
                    //如果票数>0就卖票
                    if (tickets > 0) {
                        try {
                            Thread.sleep(10);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        //4.1打印当前正在售票的线程名以及票数-1
                        System.out.println(Thread.currentThread().getName() + "=" + tickets--);
                    }
                    //4.2退出死循环--没票的时候就结束
                    if (tickets <= 0) break;
                }
            }
        }
    }
    

    1.4.2 练习-改造售票案例

    创建包: cn.tedu.tickets
    创建类:TestThreadV2.java

    package cn.tedu.tickets;
    
    /*本类用于改造多线程售票案例,解决数据安全问题*/
    public class TestThreadV2 {
        public static void main(String[] args) {
            //5.创建多个线程对象并以多线程的方式运行
            TickectT2 t1 = new TickectT2();
            TickectT2 t2 = new TickectT2();
            TickectT2 t3 = new TickectT2();
            TickectT2 t4 = new TickectT2();
            t1.start();
            t2.start();
            t3.start();
            t4.start();
        }
    }
    
    //1.自定义多线程类
    class TickectT2 extends Thread {
        //3.新增成员变量用来保存票数
        static int tickets = 100;
        //static Object o = new Object();
    
        //2.添加重写的run()来完成业务
        @Override
        public void run() {
            //3.创建循环结构用来卖票
            while (true) {
                //Ctrl+Alt+L调整代码缩进
                //7.添加同步代码块,解决数据安全问题
                //synchronized (new Object()) {
                /*static的Object的对象o这种写法也可以*/
                //synchronized (o) {
                /*我们每通过class关键字创建一个类,就会在工作空间中生成一个唯一对应的类名.class字节码文件
                * 这个类名.class对应的对象我们称之为这个类的字节码对象
                * 字节码对象极其重要,是反射技术的基石,字节码对象中包含了当前类所有的关键信息
                * 所以,用这样一个唯一且明确的对象作为同步代码块的锁对象,再合适不过了*/
                synchronized (TickectT2.class) {/*比较标准的写法*/
                    if(tickets > 0){
                        //6.添加线程休眠,暴露问题
                        try {
                            Thread.sleep(10);//让线程休眠,增加线程状态切换的频率
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        //4.1打印当前正在售票的线程名与票数-1
                        System.out.println(getName() + "=" + tickets--);
                    }
                    //4.2给程序设置一个出口,没有票的时候就停止卖票
                    if (tickets <= 0) break;
                }
            }
        }
    }
    

    注意:如果是继承的方式的话,锁对象最好用"类名.class",否则创建自定义线程类多个对象时,无法保证锁的唯一

    1.5 之前遇到过的同步例子

    StringBuffer JDK1.0
    加了synchronized ,性能相对较低(要排队,同步),安全性高
    StringBuilder JDK1.5
    去掉了synchronized,性能更高(不排队,异步),存在安全隐患
    其他同步异步的例子

    快速查找某个类的快捷键:Ctrl+Shift+T

    2 线程创建的其他方式

    2.1 ExecutorService/Executors

    ExecutorService:用来存储线程的池子,把新建线程/启动线程/关闭线程的任务都交给池来管理

    • execute(Runnable任务对象) 把任务丢到线程池

    Executors 辅助创建线程池的工具类

    • newFixedThreadPool(int nThreads) 最多n个线程的线程池
    • newCachedThreadPool() 足够多的线程,使任务不必等待
    • newSingleThreadExecutor() 只有一个线程的线程池

    2.2 练习:线程的其他创建方式

    创建包: cn.tedu.tickets
    创建类: TestThreadPool.java

    package cn.tedu.tickets;
    
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    
    /*本类用于测试线程池*/
    public class TestThreadPool {
        public static void main(String[] args) {
            //5.创建接口实现类TicketR3类的对象作为目标业务对象
            TicketR3 target = new TicketR3();
            /*Executors是用来辅助创建线程池的工具类对象
            * 常用方法是newFixedThreadPool(int)这个方法可以创建指定数目的线程池对象
            * 创建出来的线程池对象是ExecutorService:用来存储线程的池子,负责:新建/启动/关闭线程*/
            //6.使用Executors工具创建一个最多有5个线程的线程池对象ExecutorService池对象
            ExecutorService pool = Executors.newFixedThreadPool(5);
            for (int i = 0; i < 5; i++) {
                /*execute()让线程池中的线程来执行业务,每次调用都会将一个线程加入到就绪队列*/
                pool.execute(target);/*本方法的参数就是你要执行的业务,也就是目标业务类对象*/
            }
        }
    }
    //同步锁问题解决方案笔记:1.4.1从26行复制到58行,TicketR2改成TicketR3
    //1.创建自定义多线程类
    class TicketR3 implements Runnable {
        //3.定义成员变量,保存票数
        int tickets = 100;
        //创建锁对象
        Object o = new Object();
    
        //2.实现接口中未实现的方法,run()中放着的是我们的业务
        @Override
        public void run() {
            //4.通过循环结构完成业务
            while (true) {
                /*3.同步代码块:synchronized(锁对象){会出现安全隐患的所有代码}
                 * 同步代码块在同一时刻,同一资源只会被一个线程独享*/
                /*这种写法不对,相当于每个线程进来的时候都会new一个锁对象,线程间使用的并不是同一把锁*/
                //synchronized (new Object()){
                //修改同步代码块的锁对象为成员变量o,因为锁对象必须唯一
                synchronized (o) {//同步代码块解决的是重卖的问题
                    //如果票数>0就卖票
                    if (tickets > 0) {
                        try {
                            Thread.sleep(10);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        //4.1打印当前正在售票的线程名以及票数-1
                        System.out.println(Thread.currentThread().getName() + "=" + tickets--);
                    }
                    //4.2退出死循环--没票的时候就结束
                    if (tickets <= 0) break;
                }
            }
        }
    }
    

    3 拓展:线程锁

    3.1 悲观锁和乐观锁

    悲观锁:像它的名字一样,对于并发间操作产生的线程安全问题持悲观状态.
    悲观锁认为竞争总是会发生,因此每次对某资源进行操作时,都会持有一个独占的锁,就像synchronized,不管三七二十一,直接上了锁就操作资源了。

    乐观锁:还是像它的名字一样,对于并发间操作产生的线程安全问题持乐观状态.
    乐观锁认为竞争不总是会发生,因此它不需要持有锁,将”比较-替换”这两个动作作为一个原子操作尝试去修改内存中的变量,如果失败则表示发生冲突,那么就应该有相应的重试逻辑。

    3.2 两种常见的锁

    synchronized 互斥锁(悲观锁,有罪假设)

    采用synchronized修饰符实现的同步机制叫做互斥锁机制,它所获得的锁叫做互斥锁。
    每个对象都有一个monitor(锁标记),当线程拥有这个锁标记时才能访问这个资源,没有锁标记便进入锁池。任何一个对象系统都会为其创建一个互斥锁,这个锁是为了分配给线程的,防止打断原子操作。每个对象的锁只能分配给一个线程,因此叫做互斥锁。

    ReentrantLock 排他锁(悲观锁,有罪假设)

    ReentrantLock是排他锁,排他锁在同一时刻仅有一个线程可以进行访问,实际上独占锁是一种相对比较保守的锁策略,在这种情况下任何“读/读”、“读/写”、“写/写”操作都不能同时发生,这在一定程度上降低了吞吐量。然而读操作之间不存在数据竞争问题,如果”读/读”操作能够以共享锁的方式进行,那会进一步提升性能。

    ReentrantReadWriteLock 读写锁(乐观锁,无罪假设)

    因此引入了ReentrantReadWriteLock,顾名思义,ReentrantReadWriteLock是Reentrant(可重入)Read(读)Write(写)Lock(锁),我们下面称它为读写锁。
    读写锁内部又分为读锁和写锁,读锁可以在没有写锁的时候被多个线程同时持有,写锁是独占的。
    读锁和写锁分离从而提升程序性能,读写锁主要应用于读多写少的场景。

    3.3 尝试用读写锁改造售票案例

    package cn.tedu.thread;
    
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    import java.util.concurrent.locks.ReentrantReadWriteLock;
    
    /**
     * 本类用于改造售票案例,使用可重入读写锁
     * ReentrantReadWriteLock
     * */
    public class TestSaleTicketsV3 {
    	public static void main(String[] args) {
    		SaleTicketsV3 target = new SaleTicketsV3();
    		Thread t1 = new Thread(target);
    		Thread t2 = new Thread(target);
    		Thread t3 = new Thread(target);
    		Thread t4 = new Thread(target);
    		t1.start();
    		t2.start();
    		t3.start();
    		t4.start();
    	}
    }
    class SaleTicketsV3 implements Runnable{
    	static int tickets = 100;
    	//1.定义可重入读写锁对象,静态保证全局唯一
    	static ReentrantReadWriteLock lock = new ReentrantReadWriteLock(true);
    	@Override
    	public void run() {
    		while(true) {
    			//2.在操作共享资源前上锁
    			lock.writeLock().lock();
    			try {
    				if(tickets > 0) {
    					try {
    						Thread.sleep(10);
    					} catch (InterruptedException e) {
    						e.printStackTrace();
    					}
    					System.out.println(Thread.currentThread().getName() + "=" + tickets--);
    				}
    				if(tickets <= 0) break;
    			} catch (Exception e) {
    				e.printStackTrace();
    			}finally {
    				//3.finally{}中释放锁,注意一定要手动释放,防止死锁,否则就独占报错了
    				lock.writeLock().unlock();
    			}
    		}
    	}
    } 
    

    3.4 两种方式的区别

    需要注意的是,用sychronized修饰的方法或者语句块在代码执行完之后锁会自动释放,而是用Lock需要我们手动释放锁,所以为了保证锁最终被释放(发生异常情况),要把互斥区放在try内,释放锁放在finally内!
    与互斥锁相比,读-写锁允许对共享数据进行更高级别的并发访问。虽然一次只有一个线程(writer 线程)可以修改共享数据,但在许多情况下,任何数量的线程可以同时读取共享数据(reader 线程)从理论上讲,与互斥锁定相比,使用读-写锁允许的并发性增强将带来更大的性能提高。

    恭喜你,线程与线程锁的学习可以暂时告一段落啦,接着我们可以继续学习别的内容

    下一节 设计模式 点这里

    展开全文
  • Redis 实现同步锁

    千次阅读 2019-08-12 11:33:36
    73 logger.info("====投资同步锁设置轮询间隔、请求超时时长、缓存key失效时长===="); 74 //投资同步锁轮询间隔 毫秒 75 Long retryInterval = Long.parseLong(Constants.getProperty(retryIntervalName)); 76 /...

    1、技术方案

    1.1、redis的基本命令

    1)SETNX命令(SET if Not eXists)

    语法:SETNX key value

    功能:当且仅当 key 不存在,将 key 的值设为 value ,并返回1;若给定的 key 已经存在,则 SETNX 不做任何动作,并返回0。

    2)expire命令

    语法:expire KEY seconds 

    功能:设置key的过期时间。如果key已过期,将会被自动删除。

    3)DEL命令

    语法:DEL key [KEY …]

    功能:删除给定的一个或多个 key ,不存在的 key 会被忽略。

     

    1.2、实现同步锁原理

    1)加锁:“锁”就是一个存储在redis里的key-value对,key是把一组投资操作用字符串来形成唯一标识,value其实并不重要,因为只要这个唯一的key-value存在,就表示这个操作已经上锁。 

    2)解锁:既然key-value对存在就表示上锁,那么释放锁就自然是在redis里删除key-value对。 

    3)阻塞、非阻塞:阻塞式的实现,若线程发现已经上锁,会在特定时间内轮询锁。非阻塞式的实现,若发现线程已经上锁,则直接返回。

    4)处理异常情况:假设当投资操作调用其他平台接口出现等待时,自然没有释放锁,这种情况下加入锁超时机制,用redis的expire命令为key设置超时时长,过了超时时间redis就会将这个key自动删除,即强制释放锁

    (此步骤需在JAVA内部设置同样的超时机制,内部超时时长应小于或等于redis超时时长)。

     

    1.3、处理流程图

         

     

    2、代码实现

    2.1、同步锁工具类

    复制代码

      1 package com.mic.synchrolock.util;
      2 
      3 import java.util.ArrayList;
      4 import java.util.List;
      5 import java.util.UUID;
      6 
      7 import javax.annotation.PostConstruct;
      8 import javax.annotation.PreDestroy;
      9 
     10 import org.apache.commons.logging.Log;
     11 import org.apache.commons.logging.LogFactory;
     12 
     13 import org.springframework.beans.factory.annotation.Autowired;
     14 
     15 import com.mic.constants.Constants;
     16 import com.mic.constants.InvestType;
     17 
     18 /**
     19  * 分布式同步锁工具类
     20  * @author Administrator
     21  *
     22  */
     23 public class SynchrolockUtil {    
     24     
     25     private final Log logger = LogFactory.getLog(getClass());
     26     
     27     @Autowired
     28     private RedisClientTemplate redisClientTemplate;
     29 
     30     public final String RETRYTYPE_WAIT = "1";     //加锁方法当对象已加锁时,设置为等待并轮询
     31     public final String RETRYTYPE_NOWAIT = "0";     //加锁方法当对象已加锁时,设置为直接返回
     32     
     33     private String requestTimeOutName = "";     //投资同步锁请求超时时间
     34     private String retryIntervalName = "";         //投资同步锁轮询间隔
     35     private String keyTimeoutName = "";        //缓存中key的失效时间
     36     private String investProductSn = "";         //产品Sn
     37     private String uuid;                //对象唯一标识
     38     
     39     private Long startTime = System.currentTimeMillis(); //首次调用时间
     40     public Long getStartTime() {
     41         return startTime;
     42     }
     43 
     44     List<String> keyList = new ArrayList<String>();    //缓存key的保存集合
     45     public List<String> getKeyList() {
     46         return keyList;
     47     }
     48     public void setKeyList(List<String> keyList) {
     49         this.keyList = keyList;
     50     }
     51 
     52     @PostConstruct
     53     public void init() {
     54         uuid = UUID.randomUUID().toString();
     55     }
     56     
     57     @PreDestroy
     58     public void destroy() {
     59         this.unlock();
     60     }
     61     
     62 
     63     /**
     64      * 根据传入key值,判断缓存中是否存在该key
     65      * 存在-已上锁:判断retryType,轮询超时,或直接返回,返回ture
     66      * 不存在-未上锁:将该放入缓存,返回false
     67      * @param key
     68      * @param retryType 当遇到上锁情况时  1:轮询;0:直接返回
     69      * @return
     70      */
     71     public boolean islocked(String key,String retryType){
     72         boolean flag = true;
     73         logger.info("====投资同步锁设置轮询间隔、请求超时时长、缓存key失效时长====");
     74         //投资同步锁轮询间隔 毫秒
     75         Long retryInterval = Long.parseLong(Constants.getProperty(retryIntervalName));
     76         //投资同步锁请求超时时间 毫秒
     77         Long requestTimeOut = Long.parseLong(Constants.getProperty(requestTimeOutName));
     78         //缓存中key的失效时间 秒
     79         Integer keyTimeout =  Integer.parseInt(Constants.getProperty(keyTimeoutName));
     80         
     81         //调用缓存获取当前产品锁
     82         logger.info("====当前产品key为:"+key+"====");
     83         if(isLockedInRedis(key,keyTimeout)){
     84             if("1".equals(retryType)){
     85                 //采用轮询方式等待
     86                 while (true) {
     87                     logger.info("====产品已被占用,开始轮询====");
     88                     try {
     89                         Thread.sleep(retryInterval);
     90                     } catch (InterruptedException e) {
     91                         logger.error("线程睡眠异常:"+e.getMessage(), e);
     92                         return flag;
     93                     }
     94                     logger.info("====判断请求是否超时====");
     95                     Long currentTime = System.currentTimeMillis(); //当前调用时间
     96                     long Interval = currentTime - startTime;
     97                     if (Interval > requestTimeOut) {
     98                         logger.info("====请求超时====");
     99                         return flag;
    100                     }
    101                     if(!isLockedInRedis(key,keyTimeout)){
    102                         logger.info("====轮询结束,添加同步锁====");
    103                         flag = false;
    104                         keyList.add(key);
    105                         break;
    106                     }
    107                 }
    108             }else{
    109                 //不等待,直接返回
    110                 logger.info("====产品已被占用,直接返回====");
    111                 return flag;
    112             }
    113             
    114         }else{
    115             logger.info("====产品未被占用,添加同步锁====");
    116             flag = false;
    117             keyList.add(key);
    118         }
    119         return flag;
    120     }
    121     
    122     /**
    123      * 在缓存中查询key是否存在
    124      * 若存在则返回true;
    125      * 若不存在则将key放入缓存,设置过期时间,返回false
    126      * @param key
    127      * @param keyTimeout key超时时间单位是秒
    128      * @return
    129      */
    130     boolean isLockedInRedis(String key,int keyTimeout){
    131         logger.info("====在缓存中查询key是否存在====");
    132         boolean isExist = false; 
    133         //与redis交互,查询对象是否上锁
    134         Long result = this.redisClientTemplate.setnx(key, uuid);
    135         logger.info("====上锁 result = "+result+"====");
    136         if(null != result && 1 == Integer.parseInt(result.toString())){
    137             logger.info("====设置缓存失效时长 = "+keyTimeout+"秒====");
    138             this.redisClientTemplate.expire(key, keyTimeout);
    139             logger.info("====上锁成功====");
    140             isExist = false;
    141         }else{
    142             logger.info("====上锁失败====");
    143             isExist = true;
    144         }
    145         return isExist;
    146     }
    147     
    148     /**
    149      * 根据传入key,对该产品进行解锁
    150      * @param key
    151      * @return
    152      */
    153     public void unlock(){
    154         //与redis交互,对产品解锁
    155         if(keyList.size()>0){
    156             for(String key : this.keyList){
    157                 String value = this.redisClientTemplate.get(key);
    158                 if(null != value && !"".equals(value)){
    159                     if(uuid.equals(value)){
    160                         logger.info("====解锁key:"+key+" value="+value+"====");
    161                         this.redisClientTemplate.del(key);
    162                     }else{
    163                         logger.info("====待解锁集合中key:"+key+" value="+value+"与uuid不匹配====");
    164                     }
    165                 }else{
    166                     logger.info("====待解锁集合中key="+key+"的value为空====");
    167                 }
    168             }
    169         }else{
    170             logger.info("====待解锁集合为空====");
    171         }
    172     }
    173     
    174     
    175 }

    复制代码

     

    2.2、业务调用模拟样例

    复制代码

     1   //获取同步锁工具类
     2   SynchrolockUtil synchrolockUtil = SpringUtils.getBean("synchrolockUtil");
     3   //获取需上锁资源的KEY
     4   String key = "abc";
     5   //查询是否上锁,上锁轮询,未上锁加锁
     6   boolean isLocked = synchrolockUtil.islocked(key,synchrolockUtil.RETRYTYPE_WAIT);
     7   //判断上锁结果
     8   if(isLocked){
     9      logger.error("同步锁请求超时并返回 key ="+key);
    10   }else{
    11       logger.info("====同步锁加锁陈功====");
    12   }
    13 
    14   try {
    15 
    16       //执行业务处理
    17 
    18   } catch (Exception e) {
    19       logger.error("业务异常:"+e.getMessage(), e);
    20   }finally{
    21       //解锁
    22       synchrolockUtil.unlock();
    23   }

    复制代码

     

    2.3、如果业务处理内部,还有嵌套加锁需求,只需将对象传入方法内部,加锁成功后将key值追加到集合中即可

     

    ps:实际实现中还需要jedis工具类,需额外添加调用

    展开全文
  • 【Java多线程-6】synchronized同步锁

    千次阅读 2020-03-31 15:35:49
    synchronized同步锁 1 synchronized 原理概述 1.1 操作系统层面 1.2 JVM层面 2 synchronized 使用 2.1 同步代码块 2.2 同步方法 2.3 同步静态方法 2.4 同步类 前文描述了Java多线程编程,多线程的方式提高了系统资源...


    前文描述了Java多线程编程,多线程的方式提高了系统资源利用和程序效率,但多个线程同时处理共享的数据时,就将面临线程安全的问题。

    例如,下面模拟这样一个场景:一个售票处有3个售票员,出售20张票。

    public class SellTickets {
        public static void main(String[] args) {
            TicketSeller seller = new TicketSeller();
    
            Thread t1 = new Thread(seller, "窗口1");
            Thread t2 = new Thread(seller, "窗口2");
            Thread t3 = new Thread(seller, "窗口3");
    
            t1.start();
            t2.start();
            t3.start();
        }
    }
    
    class TicketSeller extends Thread {
        private static int tickets = 20;
    
        @Override
        public void run() {
            while (true) {
                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
    
                }
                if (tickets > 0) {
                    System.out.println(Thread.currentThread().getName() + "正在出售第" + (tickets--) + "张票");
                }
            }
        }
    }
    

    运行后,发现会出现多个售票员出售同一张票的现象:
    在这里插入图片描述
    为了解决线程安全的问题,Java提供了多种同步锁。

    1 synchronized 原理概述

    1.1 操作系统层面

    synchronized的底层是使用操作系统的mutex lock实现的。下面先了解一些相关的概念。

    • 内存可见性:同步块的可见性是由以下两个规则获得的:
      1. 如果对一个变量执行lock操作,将会清空工作内存中此变量的值,在执行引擎使用这个变量前需要重新执行load或assign操作初始化变量的值。
      2. 对一个变量执行unlock操作之前,必须先把此变量同步回主内存中(执行store和write操作)。
    • 操作原子性:持有同一个锁的两个同步块只能串行地进入

    锁的内存语义:

    • 当线程释放锁时,JMM会把该线程对应的本地内存中的共享变量刷新到主内存中。
    • 当线程获取锁时,JMM会把该线程对应的本地内存置为无效,从而使得被监视器保护的临界区代码必须从主内存中读取共享变量。

    锁释放和锁获取的内存语义:

    • 线程A释放一个锁,实质上是线程A向接下来将要获取这个锁的某个线程发出了(线程A对共享变量所做修改的)消息。
    • 线程B获取一个锁,实质上是线程B接收了之前某个线程发出的(在释放这个锁之前对共享变量所做修改的)消息。
    • 线程A释放锁,随后线程B获取这个锁,这个过程实质上是线程A通过主内存向线程B发送消息

    在这里插入图片描述

    Mutex Lock

    监视器锁(Monitor)本质是依赖于底层的操作系统的Mutex Lock(互斥锁)来实现的。每个对象都对应于一个可称为" 互斥锁" 的标记,这个标记用来保证在任一时刻,只能有一个线程访问该对象。

    互斥锁:用于保护临界区,确保同一时间只有一个线程访问数据。对共享资源的访问,先对互斥量进行加锁,如果互斥量已经上锁,调用线程会阻塞,直到互斥量被解锁。在完成了对共享资源的访问后,要对互斥量进行解锁。

    mutex的工作方式:
    在这里插入图片描述

    1. 申请mutex,如果成功,则持有该mutex,如果失败,则进行spin自旋. spin的过程就是在线等待mutex, 不断发起mutex gets, 直到获得mutex或者达到spin_count限制为止
    2. 依据工作模式的不同选择yiled还是sleep
    3. 若达到sleep限制或者被主动唤醒或者完成yield, 则重复1-2步,直到获得为止

    由于Java的线程是映射到操作系统的原生线程之上的,如果要阻塞或唤醒一条线程,都需要操作系统来帮忙完成,这就需要从用户态转换到内核态中,因此状态转换需要耗费很多的处理器时间。所以synchronized是Java语言中的一个重量级操作。在JDK1.6中,虚拟机进行了一些优化,譬如在通知操作系统阻塞线程之前加入一段自旋等待过程,避免频繁地切入到核心态中。

    synchronized与java.util.concurrent包中的ReentrantLock相比,由于JDK1.6中加入了针对锁的优化措施(见后面),使得synchronized与ReentrantLock的性能基本持平。ReentrantLock只是提供了synchronized更丰富的功能,而不一定有更优的性能,所以在synchronized能实现需求的情况下,优先考虑使用synchronized来进行同步。

    1.2 JVM层面

    synchronized用的锁是存在Java对象头里的,那么什么是Java对象头呢?

    Hotspot虚拟机的对象头主要包括两部分数据:Mark Word(标记字段)、Klass Pointer(类型指针)。其中Klass Point是是对象指向它的类元数据的指针,虚拟机通过这个指针来确定这个对象是哪个类的实例,Mark Word用于存储对象自身的运行时数据,它是实现轻量级锁和偏向锁的关键,所以下面将重点阐述。

    长度内容说明
    32/64bitMark Word存储对象的hashCode 或锁信息
    32/64bitClass Metadata Address存储对象类型数据的指针
    32/64bitArray length数组的长度(如果当前对象是数组)

    Mark Word

    Mark Word用于存储对象自身的运行时数据,如哈希码(HashCode)、GC分代年龄、锁状态标志、线程持有的锁、偏向线程 ID、偏向时间戳等等。Java对象头一般占有两个机器码(在32位虚拟机中,1个机器码等于4字节,也就是32bit),但是如果对象是数组类型,则需要三个机器码,因为JVM虚拟机可以通过Java对象的元数据信息确定Java对象的大小,但是无法从数组的元数据来确认数组的大小,所以用一块来记录数组长度。

    对象头信息是与对象自身定义的数据无关的额外存储成本,但是考虑到虚拟机的空间效率,Mark Word被设计成一个非固定的数据结构以便在极小的空间内存存储尽量多的数据,它会根据对象的状态复用自己的存储空间,也就是说,Mark Word会随着程序的运行发生变化,变化状态如下(32位虚拟机):
    在这里插入图片描述

    Monitor

    什么是Monitor?我们可以把它理解为一个同步工具,也可以描述为一种同步机制,它通常被描述为一个对象。

    与一切皆对象一样,所有的Java对象是天生的Monitor,每一个Java对象都有成为Monitor的潜质,因为在Java的设计中 ,每一个Java对象自打娘胎里出来就带了一把看不见的锁,它叫做内部锁或者Monitor锁。

    Monitor 是线程私有的数据结构,每一个线程都有一个可用monitor record列表,同时还有一个全局的可用列表。每一个被锁住的对象都会和一个monitor关联(对象头的MarkWord中的LockWord指向monitor的起始地址),同时monitor中有一个Owner字段存放拥有该锁的线程的唯一标识,表示该锁被这个线程占用。

    其结构如下:
    在这里插入图片描述

    • Owner:初始时为NULL表示当前没有任何线程拥有该monitor record,当线程成功拥有该锁后保存线程唯一标识,当锁被释放时又设置为NULL。
    • EntryQ:关联一个系统互斥锁(semaphore),阻塞所有试图锁住monitor record失败的线程。
    • RcThis:表示blocked或waiting在该monitor record上的所有线程的个数。
    • Nest:用来实现重入锁的计数。HashCode:保存从对象头拷贝过来的HashCode值(可能还包含GC age)。
    • Candidate:用来避免不必要的阻塞或等待线程唤醒,因为每一次只有一个线程能够成功拥有锁,如果每次前一个释放锁的线程唤醒所有正在阻塞或等待的线程,会引起不必要的上下文切换(从阻塞到就绪然后因为竞争锁失败又被阻塞)从而导致性能严重下降。Candidate只有两种可能的值,0表示没有需要唤醒的线程,1表示要唤醒一个继任线程来竞争锁。

    2 synchronized 使用

    synchronized是Java中的关键字,是一种同步锁,它修饰的对象有以下几种:

    序号类别作用范围作用对象
    1同步代码块被synchronized修饰的代码块调用这个代码块的单个对象
    2同步方法被synchronized修饰的方法调用该方法的单个对象
    3同步静态方法被synchronized修饰的静态方法静态方法所属类的所有对象
    4同步类被synchronized修饰的代码块该类的所有对象

    2.1 同步代码块

    同步代码块就是将需要的同步的代码使用同步锁包裹起来,这样能减少阻塞,提高程序效率。

    同步代码块格式如下:

        synchronized(对象){
        	同步代码;
        }
    

    同样对于文章开头卖票的例子,进行线程安全改造,代码如下:

    public class SellTickets {
        public static void main(String[] args) {
            TicketSeller seller = new TicketSeller();
    
            Thread t1 = new Thread(seller, "窗口1");
            Thread t2 = new Thread(seller, "窗口2");
            Thread t3 = new Thread(seller, "窗口3");
    
            t1.start();
            t2.start();
            t3.start();
        }
    }
    
    class TicketSeller implements Runnable {
        private static int tickets = 100;
    
        @Override
        public void run() {
            while (true) {
                synchronized (this) {
                    try {
                        Thread.sleep(10);
                        if (tickets > 0) {
                            System.out.println(Thread.currentThread().getName() + "正在出售第" + (tickets--) + "张票");
                        }
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }
    

    同步代码块的关键在于锁对象,多个线程必须持有同一把锁,才会实现互斥性。

    将上面代码中的 synchronized (this) 改为 synchronized (new Objcet()) 的话,线程安全将得不到保证,因为两个线程的持锁对象不再是同一个。

    又比如下面这个例子:

    public class SyncTest implements Runnable {
        // 共享资源变量
        int count = 0;
    
        @Override
        public void run() {
            synchronized (this) {
                for (int i = 0; i < 5; i++) {
                    System.out.println(Thread.currentThread().getName() + ":" + count++);
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    
        public static void main(String[] args) {
    //        test1();
            test2();
        }
    
        public static void test1() {
            SyncTest syncTest1 = new SyncTest();
            Thread thread1 = new Thread(syncTest1, "thread-1");
            Thread thread2 = new Thread(syncTest1, "thread-2");
            thread1.start();
            thread2.start();
        }
    
        public static void test2() {
            SyncTest syncTest1 = new SyncTest();
            SyncTest syncTest2 = new SyncTest();
    
            Thread thread1 = new Thread(syncTest1, "thread-1");
            Thread thread2 = new Thread(syncTest2, "thread-2");
            thread1.start();
            thread2.start();
        }
    }
    

    从输出结果可以看出,test2() 方法无法实现线程安全,原因在于我们指定锁为this,指的就是调用这个方法的实例对象,然而 test2() 实例化了两个不同的实例对象 syncTest1,syncTest2,所以会有两个锁,thread1与thread2分别进入自己传入的对象锁的线程执行 run() 方法,造成线程不安全。

    如果要使用这个经济实惠的锁并保证线程安全,那就不能创建出多个不同实例对象。如果非要想 new 两个不同对象出来,又想保证线程同步的话,那么 synchronized 后面的括号中可以填入SyncTest.class,表示这个类对象作为锁,自然就能保证线程同步了。

    synchronized(xxxx.class){
      //todo
    }
    

    一个线程访问一个对象的synchronized代码块时,别的线程可以访问该对象的非synchronized代码块而不受阻塞。

    例如下面的例子:

    public class SyncTest {
        public static void main(String[] args) {
            Counter counter = new Counter();
            Thread thread1 = new Thread(counter, "线程-1");
            Thread thread2 = new Thread(counter, "线程-2");
            thread1.start();
            thread2.start();
        }
    }
    
    class Counter implements Runnable {
        private int count = 0;
    
        public void countAdd() {
            synchronized (this) {
                for (int i = 0; i < 5; i++) {
                    try {
                        System.out.println(Thread.currentThread().getName() + " 同步计数:" + (count++));
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    
        public void printCount() {
            for (int i = 0; i < 5; i++) {
                try {
                    System.out.println(Thread.currentThread().getName() + " 非同步输出:" + count);
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    
        public void run() {
            String threadName = Thread.currentThread().getName();
            if (threadName.equals("线程-1")) {
                countAdd();
            } else if (threadName.equals("线程-2")) {
                printCount();
            }
        }
    }
    

    我们也可以用synchronized 给对象加锁。这时,当一个线程访问该对象时,其他试图访问此对象的线程将会阻塞,直到该线程访问对象结束。也就是说谁拿到那个锁谁就可以运行它所控制的那段代码,,例如下例:

    public class SyncTest {
        public static void main(String args[]) {
            Account account = new Account("zhang san", 10000.0f);
            AccountOperator accountOperator = new AccountOperator(account);
    
            final int THREAD_NUM = 5;
            Thread threads[] = new Thread[THREAD_NUM];
            for (int i = 0; i < THREAD_NUM; i++) {
                threads[i] = new Thread(accountOperator, "Thread-" + i);
                threads[i].start();
            }
        }
    }
    
    class Account {
        String name;
        double amount;
    
        public Account(String name, double amount) {
            this.name = name;
            this.amount = amount;
        }
    
        //存钱
        public void deposit(double amt) {
            amount += amt;
            try {
                Thread.sleep(0);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    
        //取钱
        public void withdraw(double amt) {
            amount -= amt;
            try {
                Thread.sleep(0);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    
        public double getBalance() {
            return amount;
        }
    }
    
    class AccountOperator implements Runnable {
        private Account account;
    
        public AccountOperator(Account account) {
            this.account = account;
        }
    
        public void run() {
            synchronized (account) {
                String name = Thread.currentThread().getName();
                account.deposit(500);
                System.out.println(name + "存入500,最新余额:" + account.getBalance());
                account.withdraw(400);
                System.out.println(name + "取出400,最新余额:" + account.getBalance());
                System.out.println(name + "最终余额:" + account.getBalance());
            }
        }
    }
    

    同步锁可以使用任意对象作为锁,当没有明确的对象作为锁,只是想让一段代码同步时,可以创建一个特殊的对象来充当锁:

    class Test implements Runnable {
       private byte[] lock = new byte[0];  // 特殊的instance变量
       public void method() {
          synchronized(lock) {
             // todo 同步代码块
          }
       }
     
       public void run() {
     
       }
    }
    

    2.2 同步方法

    Synchronized修饰一个方法很简单,就是在方法的前面加synchronized,synchronized修饰方法和修饰一个代码块类似,只是作用范围不一样,修饰代码块是大括号括起来的范围,而修饰方法范围是整个函数。

    public synchronized void method(){
       // todo
    }
    

    下面用同步函数的方式解决售票场景的线程安全问题,代码如下:

    public class SellTickets {
        public static void main(String[] args) {
            TicketSeller seller = new TicketSeller();
    
            Thread t1 = new Thread(seller, "窗口1");
            Thread t2 = new Thread(seller, "窗口2");
            Thread t3 = new Thread(seller, "窗口3");
    
            t1.start();
            t2.start();
            t3.start();
        }
    }
    
    class TicketSeller implements Runnable {
        private static int tickets = 100;
    
        @Override
        public void run() {
            while (true) {
                sellTickets();
            }
        }
    
        public synchronized void sellTickets() {
            try {
                Thread.sleep(10);
                if (tickets > 0) {
                    System.out.println(Thread.currentThread().getName() + "正在出售第" + (tickets--) + "张票");
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    

    同步方法有以下特征:

    1. synchronized关键字不能继承。 虽然可以使用synchronized来定义方法,但synchronized并不属于方法定义的一部分,因此,synchronized关键字不能被继承。如果在父类中的某个方法使用了synchronized关键字,而在子类中覆盖了这个方法,在子类中的这个方法默认情况下并不是同步的,而必须显式地在子类的这个方法中加上synchronized关键字才可以。当然,还可以在子类方法中调用父类中相应的方法,这样虽然子类中的方法不是同步的,但子类调用了父类的同步方法,因此,子类的方法也就相当于同步了。
    2. 在定义接口方法时不能使用synchronized关键字。
    3. 构造方法不能使用synchronized关键字,但可以使用synchronized代码块来进行同步。

    2.3 同步静态方法

    Synchronized也可修饰一个静态方法,静态方法是不属于当前实例的,而是属性类的,那么这个锁就是类的class对象锁。同步静态方法可以解决同步方法和同步代码块中的一个问题:new 两个对象的话,等于有两把锁,无法保证线程安全。

    public class SyncTest {
        public static void main(String args[]) {
            SyncThread syncThread1 = new SyncThread();
            SyncThread syncThread2 = new SyncThread();
            Thread thread1 = new Thread(syncThread1, "Thread-1");
            Thread thread2 = new Thread(syncThread2, "Thread-2");
            thread1.start();
            thread2.start();
        }
    }
    
    class SyncThread implements Runnable {
        private static int count = 0;
    
        public synchronized static void method() {
            for (int i = 0; i < 5; i++) {
                try {
                    System.out.println(Thread.currentThread().getName() + ":" + (count++));
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    
        public synchronized void run() {
            method();
        }
    }
    

    syncThread1 和 syncThread2 是 SyncThread 的两个对象,但在 thread1 和 thread2 并发执行时却保持了线程同步。这是因为run中调用了静态方法method,而静态方法是属于类的,所以syncThread1和syncThread2相当于用了同一把锁。

    2.4 同步类

    Synchronized还可作用于一个类,用法如下:

    class ClassName {
       public void method() {
          synchronized(ClassName.class) {
             // todo
          }
       }
    }
    

    同步类与同步静态方法有相同的效果,该类的所有对象都是持有同一把锁:

    public class SyncTest {
        public static void main(String args[]) {
            SyncThread syncThread1 = new SyncThread();
            SyncThread syncThread2 = new SyncThread();
            Thread thread1 = new Thread(syncThread1, "Thread-1");
            Thread thread2 = new Thread(syncThread2, "Thread-2");
            thread1.start();
            thread2.start();
        }
    }
    
    class SyncThread implements Runnable {
        private static int count = 0;
    
        public void method() {
            synchronized (SyncThread.class) {
                for (int i = 0; i < 5; i++) {
                    try {
                        System.out.println(Thread.currentThread().getName() + ":" + (count++));
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    
        public synchronized void run() {
            method();
        }
    }
    
    展开全文
  • 首先对java中同步锁与互斥锁进行区分,主要来源于知乎中的大佬总结如下: 1.锁的概念 锁的目的就是避免多个线程对同一个共享的数据并发修改带来的数据混乱。 锁的实现要处理的大概就只有这4个问题:(知乎大宽宽...

    栗子来源:https://blog.csdn.net/wenzhi20102321/article/details/52524545

    首先对java中同步锁与互斥锁进行区分,主要来源于知乎中的大佬总结如下:

    1.锁的概念

    锁的目的就是避免多个线程对同一个共享的数据并发修改带来的数据混乱。

    锁的实现要处理的大概就只有这4个问题:(知乎大宽宽的回答,非本人观点)

    • 谁拿到了锁“这个信息存哪里(可以是当前class,当前instance的markword,还可以是某个具体的Lock的实例)
    • 谁能抢到锁的规则(只能一个人抢到 - Mutex;能抢有限多个数量 - Semaphore;自己可以反复抢 - 重入锁;读可以反复抢到但是写独占 - 读写锁……)
    • 抢不到时怎么办(抢不到玩命抢;抢不到暂时睡着,等一段时间再试/等通知再试;或者二者的结合,先玩命抢几次,还没抢到就睡着)
    • 如果锁被释放了还有其他等待锁的怎么办(不管,让等的线程通过超时机制自己抢;按照一定规则通知某一个等待的线程;通知所有线程唤醒他们,让他们一起抢……)

    2.互斥与同步

    互斥锁与同步锁的区别:(知乎:chen Kingwen的回答

    互斥是通过竞争对资源的独占使用,彼此之间不需要知道对方的存在,执行顺序是一个乱序。
    同步是协调多个相互关联线程合作完成任务,彼此之间知道对方存在,执行顺序往往是有序的。

    3.java中synchronized实现同步锁

    synchronized为java中实现同步锁的关键字,可以以下几种对象、代码块、方法、类

    • 代码块:被修饰的代码块为同步代码块,调用该代码块的对象是同步锁的操作对象
    • 方法:被修饰的方法为同步方法,调用该方法的对象为同步锁的操作对象
    • 静态方法:被static关键字修饰的方法属于类,那同步锁修饰的静态方法自然被操作对象为该类的所有实例对象
    • 类:被修饰的类为同步类,该类的所有实例对象为同步锁的操作对象

    4.实例(该实例来源于https://blog.csdn.net/wenzhi20102321/article/details/52524545

    使用同步锁及多线程实现模拟三台售票机售出同批次票:

    /*
     * 三个售票窗口同时售卖20张票
     * 1.票数使用同一个静态值
     * 2.保证不出现卖出同一个票数,java多线程同步锁
     */
    public class Station extends Thread {
    	public Station(String name) {
    		super(name);//名字就以线程名进行命名
    	}
    	static int tick=20;//表示这个票数归类所有
    	static Object ob="aa";//静态钥匙,值是任意
    	public void run() {
    		while(tick>0) {
    			synchronized(ob) {//synchronized同步锁,以下钥匙aa为同步静态对象,这就解释了为什么定义为Object
    				if(tick>0) {
    					System.out.println(getName()+"卖出了第"+(21-tick)+"张票");
    					tick--;
    				}else {
    					System.out.println("票卖完了");
    				}
    			}
    			try {
    				sleep(1000);
    			}catch(InterruptedException e) {
    				e.printStackTrace();
    			}
    		}
    	}	
    }

    调用的主类:

    
    public class Main_class {
    	/**
    	 * java多线程同步锁的应用
    	 * @param args
    	 */
    	public static void main(String[] args) {
    		// TODO Auto-generated method stub
    		Station station1=new Station("窗口1");
    		Station station2=new Station("窗口2");
    		Station station3=new Station("窗口3");
    		station1.start();
    		station2.start();
    		station3.start();														
    	}
    
    }
    

    演示结果:

    展开全文
  • 同步锁-线程安全问题解决方法

    千次阅读 2021-10-03 20:23:46
    1 同步锁 1.1 前言 我们如何判断程序有没有可能出现线程安全问题,主要有以下三个条件: 在多线程程序中 + 有共享数据 + 多条语句操作共享数据 多线程的场景和共享数据的套接是改变不了的(就像4个窗口一起卖100...
  • Java同步锁synchronized的最全总结

    千次阅读 2019-10-18 11:34:35
    一、并发同步问题   线程安全是Java并发编程中的重点,而造成线程安全问题的主要原因有两点,一是存在共享数据(也称临界资源),二是存在多条线程共同操作共享数据。因此,当存在多个线程操作共享数据时,需要保证...
  • Synchronized同步锁是如何实现的

    千次阅读 多人点赞 2019-10-27 22:41:07
    要想用好Synchronized锁,首先得了解清楚其实现同步锁的原理 原理解析 首先,看下其修饰代码块时: //关键字在代码块上,锁为括号里面的对象 public void method2(){ Object o = new Object(); ...
  • Java同步锁

    千次阅读 2019-05-16 21:07:57
    从Java5开始,Java提供了一种功能更强大的线程同步机制——通过显式定义同步锁 对象来实现同步,在这种机制下,同步锁由Lock对象充当。 Lock提供了比synchronized方法和synchronized代码块更广泛的锁定操作,Lock...
  • 同步锁,乐观锁,悲观锁

    千次阅读 2018-02-27 11:25:29
    同步锁,乐观锁,悲观锁 Synchronized 所有对象都自动含有单一的锁(监视器),当在对象上调用其任意 synchronized 方法的时候,此对象都被加锁。对于某个特定对象来说,其所有synchronized方法共享同一个锁,这...
  • Java中synchronized同步锁用法及作用范围

    万次阅读 多人点赞 2018-01-18 13:33:52
    Java 中的 synchronized 关键字可以在多线程环境下用来作为线程安全的同步锁。本文主要对 synchronized 的作用,以及其有效范围进行讨论。 Java中的对象锁和类锁:java的对象锁和类锁在锁的概念上基本上和内置锁是...
  • synchronized同步锁的三种方式

    千次阅读 2019-08-13 19:42:59
    *解决:加锁---的范围:打印和++ * ---的对象:同一个对象 *的分类: *1.同步代码块2.同步方法3.对象互斥 * *问题二:票数超出范围 *解决:在内加判断 * *问题三:每个线程应该都有同一个出口...
  • 同步锁的三种实现与案例解析

    千次阅读 2017-12-17 17:29:43
    1、同步和异步的区别和联系 所谓同步,可以理解为在执行完一个函数或方法之后,一直等待系统返回值或消息,这时程序是出于阻塞的,只有接收到返回的值或消息后才往下执行其它的命令。  异步,执行完函数或方法后...
  • java中同步锁synchronized与Lock的区别

    万次阅读 多人点赞 2019-03-05 11:34:13
    同步锁: java的内置锁:每个java对象都可以用做一个实现同步的锁,这些锁成为内置锁。线程进入同步代码块或方法的时候会自动获得该锁,在退出同步代码块或方法时会释放该锁。获得内置锁的唯一途径就是进入这个锁的...
  • 目录 一,概述 二,CAS算法 三,Java对象的对象头,以及Mark Word 四,偏向 Baised Lock 五,轻量级 ...六,自旋 SpinLock ...七,重量级 ...1,java中使用synchronized关键字来实现同步功能...
  • go语言的同步锁

    万次阅读 2020-09-17 21:29:25
    当一个goroutine获得了Mutex后,其他goroutine就只能乖乖的等待,除非该goroutine释放这个Mutex ... RWMutex在写占用情况下,会阻止任何其他goroutine(无论读和写)进来,整个相当于由该goroutine独占 ...
  • synchronized关键字是java里面用来在多线程环境下保证线程安全的同步锁;java里面有对象锁和类锁,对象锁是用在对象实例的方法上或者一个对象实例上的,而类锁是用在一个类的静态方法上或者一个类的class对象上的。...
  • Java:同步锁概念

    千次阅读 2018-09-02 22:03:17
    同步锁介绍 对于非 static 方法,同步锁就是 this。 对于 static 方法,使用当前方法所在类的字节码对象(A.class)。 同步代码块: 语法 synchronized(同步锁) { 需要同步操作的代码 } 同步锁:  ...
  • 基于Redis RedLock的分布式同步锁

    千次阅读 2018-11-13 11:16:20
    本文采用Redis官网提供的RedLock来实现分布式同步锁,实现了单机模式和哨兵集群模式两种。 安全和可靠性保证 在描述我们的设计之前,我们想先提出三个属性,这三个属性在我们看来,是实现高效分布式锁的基础。 安全...
  • Java多线程:同步集合与同步锁

    千次阅读 2018-10-14 21:58:34
    Java多线程:同步集合与同步锁 同步集合 同步集合在多线程开发中扮演非常重要的角色,本文介绍些常用但被忽略的同步集合。 CopyOnWriteArrayList Copy-On-Write是一种用于程序设计中的优化策略,基本思路是多个线程...
  • 浅谈C#中的同步锁

    千次阅读 2018-07-16 15:09:59
    其中,lock是一种比较好用的简单的线程同步方式,它是通过为给定对象获取互斥来实现同步的。它可以保证当一个线程在关键代码段的时候,另一个线程不会进来,它只能等待,等到那个线程对象被释放,也就是说线程出了...
  • 线程同步锁和异步锁的几种方式

    万次阅读 2018-09-10 11:12:09
    同步锁:当在一个java虚拟机多个线程操作一个变量的时候就会出现线程安全问题,这个时候就会用到同步锁同步锁的解决方式: 先看下一个线程异常的售票 public class ThreadSafe { public static void main...
  • SpringBoot整合Redis使用同步锁

    千次阅读 2019-03-01 19:48:02
    在处理高并发,我们可以使用synchronized同步锁。 同步机制:synchronized是Java同步机制的一种实现,即互斥锁机制,它所获得的锁叫做互斥锁。 互斥锁:指的是每个对象的锁一次只能分配给一个线程,同一时间只能...
  • 同步锁的理解

    千次阅读 2019-01-16 11:56:08
    不添加同步锁,当两个(多个)线程共同使用一个共享资源时,会出现资源的抢占,对于每个进程而言,抢占某个特定的资源,所获得的结果是不确定的。如下: 保持代码不变,再次运行: 加上同步锁之后,某段时间内...
  • synchronized 同步锁(java)实例解析

    万次阅读 2017-07-09 22:02:53
     在多线程应用场景中,同步锁是一种非常重要的机制,例如:ID号的分配,多个客户端分别与服务端建立连接,客户端并发请求的情况下,为提升吞吐量,服务端一般采用多线程处理请求,若无同步锁机制,不同线程分配到...
  • 这里大部分的解释都在代码中。 demo1 package synchornizeds; public class Demo1 { private int count;... synchronized (o) { //任何线程执行一下代码都要申请o对应的。此处的o是堆内存中new...
  • js模拟java同步锁

    千次阅读 2018-03-15 11:12:00
     * js实现同步锁,缺省锁定10秒  * 示例  * if(jsynchronized("handIn")){  ...  * }  */ var locks = []; var LOCKTIME_DEFAULT = 1000 * 10; function jsynchronized(lockName,lock...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 489,240
精华内容 195,696
关键字:

同步锁