首页 文章 精选 留言 我的

精选列表

搜索[并发原语],共10000篇文章
优秀的个人博客,低调大师

Java并发编程-队列同步器(AbstractQueuedSynchronizer)

章节目录 Lock接口与Synchronized的区别及特性 队列同步器的接口与自定义锁示例 队列同步器的实现分析 1.Lock接口与Synchronized的区别及特性 特性 描述 尝试非阻塞性的获取锁 当前线程尝试获取锁(自旋获取锁),如果这一时刻锁没有被其他线程获取到,则成功获取并持有锁 能被中断的获取锁 已获取锁的线程可以响应中断,当获取到锁的线程被中断时,可以抛出中断异常,同时锁会被释放 超时获取锁 在指定的截止时间之前获取锁,如果截止时间到了仍然没有获取到锁,则返回 注意:Lock接口的实现基本上都是通过聚合了一个同步器的子类来完成线程访问控制的 队里同步器的接口与定义锁示例 队列同步器定义: 队列同步器,是用来构建锁与其它同步组件的基础框架,基本数据结构与内容是: 1、int state -> state 标示同步状态; 2、内置的FIFO来完成获取同步状态的线程的排队工作。 队列同步器使用方式 1、子类通过继承同步器并实现它的抽象方法来管理同步状态; 2、实现过程中对同步状态的更改,通过 setState()、 setState(int newState)、 compareAndSetState(int expect,int newUpdateValue) 来进行操作,保证状态改变时原子性的、安全的; 3、实现同步器的子类被推荐为自定义同步组件的静态内部类; 4、同步器可以支持独占式的获取同步状态(ReentrantLock)、也可以支持共享 式的获取同步状态(ReentrantReadWriteLock) 对于同步器与锁的关系可以这样理解: 在锁的实现中聚合同步器,利用同步器实现锁的语义。 锁面向使用者,它定义了使用者与锁的交互接口,隐藏了实现细节。 同步器面向的是锁的实现者,它简化了锁的实现方式,屏蔽了同步器状态管理、线程排队、等待与唤醒等底层操作。 2.队列同步器的接口与自定义锁示例 2.1 模板方法模式 同步器的设置是基于**模版方法模式**,使用者需要继承同步器并重写指定的方 法,随后将同步器组合在自定义同步组件的实现中,并调用同步器提供的模板 方法,而这些模板方法将会调用使用者重写的方法。 2.2 重写同步器指定的方法 getState():获取当前同步状态 setState(int newState):设置当前同步状态 compareAndSetState(int expect,int update): 使用CAS设置当前的状态,该方 法保证状态设置的原子性 2.3 同步器可重写的方法 方法名称 描述 protected boolean tryAcquire(int arg) 独占式获取同步状态,实现该方法需要查询当前状态并判断同步状态是否符合预期,然后再进行CAS设置同步状态 protected boolean tryRelease(int arg) 独占式释放同步状态,等待获取同步状态的线程将有机会获取同步状态(公平性获取锁) protected int tryAcquireShared(int arg) 共享式获取同步状态,返回>=0的值,标示获取成功,反之获取失败 protected boolean tryReleaseShared(int arg) 共享式释放同步状态 protected boolean isHeldExclusively() 当前同步器是否在独占模式下被线程占用,一般该方法表示是否被当前线程所独占 2.4 独占锁示例 package org.seckill.lock; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.AbstractQueuedSynchronizer; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; /** * 利用了模板方法模式 */ public class Mutex implements Lock { private static class Sync extends AbstractQueuedSynchronizer { //是否处于占用状态 @Override protected boolean isHeldExclusively() { return getState() == 1; } //当状态为0时获取锁 @Override protected boolean tryAcquire(int arg) { if (compareAndSetState(0, 1)) { setExclusiveOwnerThread(Thread.currentThread()); return true; } return false; } //释放锁,将当前状态设置为0 @Override protected boolean tryRelease(int arg) { if (getState() == 0) { throw new IllegalMonitorStateException(); } setExclusiveOwnerThread(null); setState(0); return true; } //返回一个condition,每个condition中都包含了一个condition队列 Condition newCondition() { return new ConditionObject(); } } //仅需要将操作代理到Sync上即可 private Sync sync = new Sync(); public void lock() { sync.acquire(1);//调用tryAccquire } //当前已获取锁的线程响应中断,释放锁,抛出异常,并返回 public void lockInterruptibly() throws InterruptedException { sync.acquireInterruptibly(1); } public boolean tryLock() { return sync.tryAcquire(1);//尝试立即获取锁 } public boolean tryLock(long timeout, TimeUnit unit) throws InterruptedException { return sync.tryAcquireNanos(1, unit.toNanos(timeout));//尝试超时获取锁 } public void unlock() { sync.release(1);//释放锁 } public Condition newCondition() { return sync.newCondition(); } } 总结-实现同步组件的方法 1. 独占锁Mutex 是一个自定义同步组件,它允许同一时刻只允许同一个线程占有锁。 2.Mutex中定义了一个私有静态内部类,该类继承了同步器并实现了独占式获取和释放同步状态。 3.在tryAcquire(int acquires)方法中,经过CAS设置成功(同步状态设置为1),则 代表获取了同步状态,而在tryRelease(int releases) 方法中只是将同步状态重 置为0。 3 队列同步器的实现分析 3.1 同步队列数据结构 同步器依赖内部的同步队列,即一个FIFO的队列,这个队列由双向链表实现。节点数据从 队列尾部插入,头部删除。 node 数据结构 struct node { node prev; //节点前驱节点 node next; //节点后继节点 Thread thread; //获取同步状态的线程 int waitStatus; //等待状态 Node nextWaiter; //等待队列中的后继节点 } 等待队列 后续篇章介绍到condition会有相关记录。 同步队列基本结构 3.2 无法获取到同步状态的线程节点被加入到同步队列的尾部 本质上是采用 compareAndSetTail(Node expect,Node update),当一个线程成功的获取了同步状态 (或者锁),其他线程将无法获取到同步状态,转而被构造成为节点并加入到同步队列中,而这个加入队列的过程 必须要保证线程安全。所以采用了基于CAS的方式来设置尾节点的方法。 ,需要传递当前节点认为的尾节点和当前节点,只有设置成功后,当前节点才正式与之前的尾节点建立关联。 3.3 成功获取同步状态 同步队列遵循FIFO,首节点是获取同步状态成功的节点,首节点的线程在释放 同步状态时,会唤醒后继节点,而后继节点将会在获取同步状态成功时,将自己设置为首节点。 3.4 独占式同步状态获取与释放 前驱节点为头节点且能够获取同步状态的判断条件和线程进入同步队列 来获 取同步状态是自旋的过程。 设置首节点是通过获取同步状态成功的线程来完成的acquireQueued(node,args)完成的 独占式获取同步状态的流程图 独占式同步状态(锁)获取流程

优秀的个人博客,低调大师

Java并发编程的艺术(十二)——线程安全

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_34173549/article/details/80289238 1. 什么是『线程安全』? 如果一个对象构造完成后,调用者无需额外的操作,就可以在多线程环境下随意地使用,并且不发生错误,那么这个对象就是线程安全的。 2. 线程安全的几种程度 线程安全性的前提:对『线程安全性』的讨论必须建立在对象内部存在共享变量这一前提,若对象在多条线程间没有共享数据,那这个对象一定是线程安全的! 2.1. 绝对的线程安全 上述线程安全性的定义即为绝对线程安全的情况,即:一个对象在构造完之后,调用者无需任何额外的操作,就可以在多线程环境下随意使用。绝对的线程安全是一种理想的状态,若要达到这一状态,往往需要付出巨大的代价。通常并不需要达到绝对的线程安全。 2.2. 相对的线程安全 我们通常所说的『线程安全』即为『相对的线程安全』,JDK中标注为线程安全的类通常就是『相对的线程安全』,如:Vector、HashTable、Collections.synchronizedXXX。对于相对线程安全的类,使用它们时一般不需要使用额外的保障措施,但对于一些特定的使用场景,仍然需要额外的操作来保证线程安全,如: // 读线程 Thread t1 = new Thread( new Runnable(){ public void run(){ for(int i=0; i<vector.size(); i++){ System.out.println( vector.get(i) ); } } }).start(); // 写线程 Thread t2 = new Thread( new Runnable(){ public void run(){ for(int i=0; i<vector.size(); i++){ vector.remove(i); } } }).start(); 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 vector是一个线程安全的容器,它所提供的方法均为同步方法,但上述代码仍然会出现线程安全性问题:若线程1读了一半的元素后暂停,线程2开始执行,并删除了所有的元素,然后线程1继续执行,此时发生角标越界异常!修改方案:加上额外的同步 // 读线程 Thread t1 = new Thread( new Runnable(){ public void run(){ synchronized( vector ){ for(int i=0; i<vector.size(); i++){ System.out.println( vector.get(i) ); } } } }).start(); // 写线程 Thread t2 = new Thread( new Runnable(){ public void run(){ synchronized( vector ){ for(int i=0; i<vector.size(); i++){ vector.remove(i); } } } }).start(); 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 2.3. 线程对立 线程对立指的是:不论调用者采用何种同步措施,都无法达到线程安全的目的。如Thread类的suspend、resume方法就是线程对立的方法。suspend方法会暂停线程,但它不会释放资源,若resume需要请求到该资源才会被运行的话,系统就会进入死锁状态。 3. 实现线程安全的方法 3.1. 互斥同步 同步指的是同一时刻,只有一条线程操作『共享变量』。 实现同步的方式有很多:互斥访问、CAS操作。互斥会引起阻塞,当一条线程请求一个已经被另一线程使用的锁时,就会进入阻塞态;而进入阻塞态会涉及上下文切换。因此,使用互斥来实现同步的开销是很大的。 互斥同步(阻塞式同步)是一种『悲观锁』,即它认为总是存在多条线程竞争资源的情况,因此它不管当前是不是真的有多条线程在竞争共享资源,它总是先上锁,然后再处理。 Java中有两种实现互斥同步的方式:synchronized和ReentrantLock。 synchronized 编译器会在synchronized同步块的开始和结束位置加上monitorenter和monitorexit指令; 这两个指令需要一个reference类型的参数来指名要锁定和解锁的对象; 若同步块没有明确指定锁对象,那么就使用当前对象或当前类的Class对象; 它是一把可重入的锁,即:当前线程在已经获得锁的情况下,可以再次获取该锁,因此不会出现当前线程把自己锁死的情况; ReentrantLock它也是一把可重入的锁,但比synchronized多如下功能: 等待可中断:若一条线程长时间占用锁不释放,那被阻塞的线程可以选择放弃等待,而去做别的事;这对于要处理长时间的同步块时是很有帮助的。 可实现公平锁:synchronized是一种非公平锁,即:被阻塞的线程竞争锁是随机的;而公平锁是根据被阻塞线程先来后到的顺序给予锁。ReentrantLock默认是非公平锁,可以通过构造函数构造公平锁。 可以绑定多个条件:synchronized可使用wait/notify来实现等待/通知机制,但一个synchronized同步块只能使用一次,若要使用多次,就需要嵌套同步块;但ReentrantLock可以通过newCondition创建多个条件。 synchronized和ReentrantLock如何选择?优先选择synchronized!JDK1.6已经对synchronized做了很多优化,性能与ReentrantLock相差不大。在条件允许的请况下应优先选择synchronized。 3.2. 非阻塞同步 它是一种『乐观锁』,即它总是认为当前没有线程使用共享资源,因此它不管当前的状态,直接操作共享资源,若发现产生了冲突,那么再采取补偿措施(如:CAS的补偿措施就是不断尝试,直到不发生冲突为止),这种方式线程无需进入阻塞态(挂起态),因此称为『非阻塞同步』。 JUC中各种整形原子类的自增、自减等操作就使用了CAS。 CAS操作过程:CAS操作存在3个值:共享变量V、预期的旧值A、新值B,若V与A相同,则将V更新成B,否则就不更新,继续循环比较,直到更新完成为止。 CAS操作可能引发的问题:ABA问题。若V一开始的值为A,但在准备赋新值的过程中A变成了B,又变成了A,而CAS操作误认为V没有被改过。 无同步方案 『阻塞式同步』和『非阻塞式同步』都是同一时刻只让一条线程处理共享数据,而下面的方案使得多条线程之间不存在共享数据,从而无需同步。 可重入代码如果一块代码段只要输入的值一样其结果就一样的话,这段代码就叫『可重入代码』。这一类代码天生具有线程安全性,线程随意切换结果都一样。 线程封闭线程封闭:把所有涉及共享变量操作的任务都放在一个线程中运行。这样就不存在多条线程同时处理共享变量了,从而达到了线程安全目的。 WEB服务器采用的就是这种方式,它把每个请求封装在一条线程中处理,从而不存在线程安全性问题。 不可变对象如果是共享的基本数据类型变量,只要被final修饰,它就是不可变的;如果是共享的对象,那就要确保它内部的共享成员变量不会被它的行为所改变。PS:保证对象内部共享变量不会被改变的方法有很多,最简单粗暴的方式就是将所有共享变量用final修饰。 不可变对象一定是线程安全的。

优秀的个人博客,低调大师

Java高并发秒杀Api-web 层

章节目录 前端交互设计 Restful Spring MVC理论 整合配置MVC框架 bootstrap+jquery 福利彩蛋 前端交互设计 前端交互设计 详情页流程逻辑 Restful接口 什么是Restful Restful 是接口设计规范 一种优雅的URI表达方式 资源的状态和状态转移 获取的是资源的状态,删除的时候,其实是更改资源的状态 put、post、delete Restful示例 image.png image.png 不同的请求方法代表着对资源的不同操作类型。操作类型应与资源的接下来发生的状态状态转移对应起来 GET 请求是幂等性的 URL设计 url设计 秒杀Api设计 Spring MVC理论 理论部分 围绕handler开发 image.png Spring mvc 的运行流程 image.png 解释: 用户请求->前端控制器->寻找对应处理请求的handler,默认为DefaultAnnotationHandlerMapping->DefaultAnnotationHandlerAdapter->寻找处理请求的Controller->请求生成ModelAndView至中央控制器->中央控制器将数据转发给InternalResourceViewResolver去解析->解析完成的数据及页面结构,组合后,被生成资源,返回给用户。 HTTP请求映射的原理 image.png 默认使用DefaultAnnotationHandlerMapping->handler方法 注解映射技巧 @RequestMapping 注解 (1) 支持标准标准的URL (2) Ant风格URL (3) 带{xxx}占位符的URL 注解映射技巧 请求方法细节处理 (1) 请求参数绑定 (2) 请求方式限制 (3) 请求转发与重定向 (4) 数据模型赋值 (5) 返回json数据 (6) cookie的访问 请求参数绑定、重定向与转发、model数据绑定 返回json数据 cookie 数据 整合配置Spring MVC配置文件 <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" version="3.1" metadata-complete="true"> <!-- 修改servlet版本为3.1 --> <!-- 配置DispatcherServlet--> <servlet> <servlet-name>seckill-dispatcher</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <!-- 配置SpringMVC需要加载的配置文件 Spring-dao.xml, spring-service.xml, spring-web.xml MyBatis -> Spring -> SpringMVC --> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:spring/spring-*.xml</param-value> </init-param> </servlet> <servlet-mapping> <servlet-name>seckill-dispatcher</servlet-name> <!-- 默认匹配所有的请求--> <url-pattern>/</url-pattern> </servlet-mapping> </web-app> Spring MVC 配置 spring-web.xml <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:mvc="http://www.springframework.org/schema/mvc" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd"> <!--配置Spring MVC--> <!--1:开启Spring MVC注解模式--> <!--简化配置 (1):自动注册DefaultAnnotationHandlerMapping、AnnotationMethodHandlerAdapter url->handler 映射 (2):默认提供一系列功能、数据绑定、数字与日期format @NumberFormat @DateTimeFormat xml json 默认读写支持 --> <mvc:annotation-driven/> <!--servlet-mapping 映射路径 /--> <!--静态资源配置,默认servlet配置--> <!-- 1:加入对静态资源的处理:js、png 2:允许使用"/"作为映射 --> <mvc:default-servlet-handler/> <!--配置jsp 显示 view Resolver--> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/> <property name="prefix" value="/WEB-INF/jsp/"/> <property name="suffix" value=".jsp"/> </bean> <!--扫描web相关的bean--> <context:component-scan base-package="org.seckill.web"/> </beans> **SecKillController ** package org.seckill.web; import org.seckill.domain.SecKill; import org.seckill.dto.Exposer; import org.seckill.dto.SecKillResult; import org.seckill.service.SecKillService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.ResponseBody; import java.util.List; @Controller @RequestMapping("/seckill") //url:/模块/资源/{id}/细分 public class SecKillController { private final Logger logger = LoggerFactory.getLogger(this.getClass()); @Autowired private SecKillService secKillService; /** * 获取秒杀商品list * * @param model * @return */ @RequestMapping(value = "/list", method = RequestMethod.GET) public String list(Model model) { //1.获取列表页面 List<SecKill> secKillList = secKillService.getSecKillList(); model.addAttribute("list", secKillList); //2.返回页面并携带model 数据list.jsp + model = ModelAndView return "list"; } /** * 接收参数、参数验证、业务逻辑调用并处理 * 获取秒杀商品详情 * * @param model * @param secKillId * @return */ @RequestMapping(value = "/{secKillId}/detial", method = RequestMethod.GET) public String detial(Model model, @PathVariable("secKillId") Long secKillId) { if (secKillId == null) {//请求重定向到list页面 return "redirect:/seckill/list"; } SecKill secKill = secKillService.getSecKillById(secKillId); if (secKill == null) { return "forward:/seckill/list"; } model.addAttribute("seckill", secKill); return "detial"; } /** * ajax接口-返回类型 json * 获取秒杀曝露地址 * * @param secKillId */ @RequestMapping(value = "/{secKillId}/exposer", method = RequestMethod.POST, produces = {"application/json;charset=UTF-8"}//告诉浏览器返回数据类型是json ) @ResponseBody public SecKillResult<Exposer> /*TODO*/ exposer(@PathVariable("secKillId") Long secKillId) { SecKillResult<Exposer> result; try { Exposer exposer = secKillService.exportSecKillUrl(secKillId);//返回秒杀地址 result = new SecKillResult<Exposer>(true, exposer); } catch (Exception e) { logger.error(e.getMessage()); result = new SecKillResult<Exposer>(false, e.getMessage()); } return result; } } 福利彩蛋 职位:腾讯OMG 广告后台高级开发工程师; Base:深圳; 场景:海量数据,To B,To C,场景极具挑战性。 基础要求: 熟悉常用数据结构与算法; 熟悉常用网络协议,熟悉网络编程; 熟悉操作系统,有线上排查问题经验; 熟悉MySQL,oracle; 熟悉JAVA,GoLang,c++其中一种语言均可; 可内推,欢迎各位优秀开发道友私信[微笑] 期待关注我的开发小哥哥,小姐姐们私信我,机会很好,平台对标抖音,广告生态平台,类似Facebook 广告平台,希望你们用简历砸我~ 联系方式 微信 13609184526

优秀的个人博客,低调大师

Java并发编程的艺术(二)——重排序

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_34173549/article/details/79612475 当我们写一个单线程程序时,总以为计算机会一行行地运行代码,然而事实并非如此。 什么是重排序? 重排序指的是编译器、处理器在不改变程序执行结果的前提下,重新排列指令的执行顺序,以达到最佳的运行效率。 重排序分类 重排序分为:编译器重排序 和 处理器重排序。 数据依赖 编译器和处理器并不会随意的改变指令的执行顺序,因为有些指令之间是有依赖关系的,若改变了他们的执行顺序,就会出现错误的结果。因此,编译器和处理器只会对没有依赖关系的指令进行重排序。 数据依赖:若相邻的两条指令访问同一个变量,并且其中有一条指令执行写操作,那么这样的两条指令之间存在数据依赖。对于有数据依赖关系的指令,不会发生重排序。 数据依赖关系总结一下为以下三种情况: 指令 示例 读后写 a=b;b=1; 写后写 a=1;a=2; 写后读 a=1;b=a; as-if-serial 在单线程开发中,程序员不需要知道指令是如何重排序的,只要简单地认为指令是按照顺序依次执行的即可。这就是as-if-serial的语义,即:貌似是串行的。

优秀的个人博客,低调大师

java面试-Java并发编程(二)——重排序

当我们写一个单线程程序时,总以为计算机会一行行地运行代码,然而事实并非如此。 什么是重排序? 重排序指的是编译器、处理器在不改变程序执行结果的前提下,重新排列指令的执行顺序,以达到最佳的运行效率。 重排序分类 重排序分为:编译器重排序 和 处理器重排序。 数据依赖 编译器和处理器并不会随意的改变指令的执行顺序,因为有些指令之间是有依赖关系的,若改变了他们的执行顺序,就会出现错误的结果。因此,编译器和处理器只会对没有依赖关系的指令进行重排序。 数据依赖:若相邻的两条指令访问同一个变量,并且其中有一条指令执行写操作,那么这样的两条指令之间存在数据依赖。对于有数据依赖关系的指令,不会发生重排序。 数据依赖关系总结一下为以下三种情况: 指令 示例 读后写 a=b;b=1; 写后写 a=1;a=2; 写后读 a=1;b=a; as-if-serial 在单线程开发中,程序员不需要知道指令是如何重排序的,只要简单地认为指令是按照顺序依次执行的即可。这就是as-if-serial的语义,即:貌似是串行的。

优秀的个人博客,低调大师

Java并发编程 -- 手动实现可重入Lock

Lock就像synchronized块一样是一个线程同步机制。 然而,Lock定比synchronized更灵活、更复杂。 Lock和synchronized块 的区别 同步块不保证等待输入它的线程被授予访问权限的顺序。 不能将任何参数传递给同步块的条目。 同步块必须完全包含在单个方法中。 一个Lock可以在不同的方法中调用lock()和unlock()。 简单例子 Lock lock = new ReentrantLock(); lock.lock(); //要保证线程安全的代码 lock.unlock(); 其中,你应该能够猜到,lock() 方法是加锁,unlock()方法是解锁。 Lock接口含有的方法 lock() lockInterruptibly() tryLock() tryLock(long timeout, TimeUnit timeUnit) unlock() lock()方法锁定Lock实例。 如果锁定实例已被锁定,则线程调用锁定()将被锁定,直到解锁锁定。 lockInterruptibly()方法锁定Lock,除非调用该方法的线程已被中断。如果一个线程被阻塞,等待通过此方法锁定Lock,该线程将被中断,并退出此方法调用。(获取锁的时候可以被中断) tryLock()方法立即尝试锁定Lock实例。 如果锁定成功则返回true;如果Lock已经被锁定,则返回false。 这个方法永远不会阻塞 tryLock(long timeout,TimeUnit timeUnit)的工作方式与tryLock()方法相似,只是它对超时时间有所规定。 unlock()方法解锁Lock实例。 通常,Lock实现将只允许已锁定Lock的线程调用此方法。 调用此方法的其他线程可能会导致未经检查的异常(RuntimeException)。 ReentrantLock实例 ReentrantLock 可重入锁,是Lock的一个子类。我们这里来使用它实现线程安全编程。 package com.lock; import com.thread.security.Task; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; /** * 重入锁 * Created by Fant.J. * 2018/3/6 20:09 */ public class ReentrantLockTest { public int value = 0; //实例化重入锁锁 Lock lock = new ReentrantLock(); public int getValue() { //加锁 lock.lock(); int a = value++; //消除锁 lock.unlock(); return a; } public static void main(String[] args) { ReentrantLockTest task = new ReentrantLockTest(); new Thread(){ @Override public void run() { while (true) { System.out.println(Thread.currentThread().getName() + " " + task.getValue()); try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } } } }.start(); new Thread(){ @Override public void run() { while (true) { System.out.println(Thread.currentThread().getName() + " " + task.getValue()); try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } } } }.start(); } } 手写自己的Lock实现类 如果有特殊业务需求,我们也可以重写Lock接口,来打造一个自己的lock锁。 package com.lock; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; /** * Created by Fant.J. * 2018/3/6 20:12 */ public class MyLock implements Lock { //声明一个判断锁的布尔值 private boolean isLocked = false; /** * 必须声明 synchronized 原自行操作,不然jvm不会识别是哪个线程的wait方法,notify也一样 */ @Override public synchronized void lock() { while (isLocked){ try { wait(); } catch (InterruptedException e) { e.printStackTrace(); } } isLocked = true; } @Override public synchronized void unlock() { isLocked = false; notify(); } @Override public void lockInterruptibly() throws InterruptedException { } @Override public boolean tryLock() { return false; } @Override public boolean tryLock(long time, TimeUnit unit) throws InterruptedException { return false; } @Override public Condition newCondition() { return null; } } 然后我们做测试 package com.lock; /** * Created by Fant.J. * 2018/3/6 20:24 */ public class MyLockTest { public int value = 0; MyLock myLock = new MyLock(); public int getValue(){ myLock.lock(); value++; myLock.unlock(); return value; } public static void main(String[] args) { MyLockTest task = new MyLockTest(); new Thread(){ @Override public void run() { while (true) { System.out.println(Thread.currentThread().getName() + " " + task.getValue()); try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } } } }.start(); new Thread(){ @Override public void run() { while (true) { System.out.println(Thread.currentThread().getName() + " " + task.getValue()); try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } } } }.start(); } } 结果没有出现线程安全问题,这里不做截图了,自己可以试试。但是我们写的方法还有一定的问题,就是MyLock这个类不支持 可重入锁,意思就是如果有两个锁嵌套,如果相同的线程先调用a方法,再调用带锁的b方法,则就会进入自旋锁。 测试方法源码 package com.lock; /** * Created by Fant.J. * 2018/3/6 20:24 */ public class MyLockTest2 { public int value = 0; MyLock myLock = new MyLock(); public void a(){ myLock.lock(); System.out.println("a"); b(); myLock.unlock(); } public void b(){ myLock.lock(); System.out.println("b"); myLock.unlock(); } public static void main(String[] args) { MyLockTest2 task = new MyLockTest2(); new Thread(){ @Override public void run() { task.a(); } }.start(); new Thread(){ @Override public void run() { task.a(); } }.start(); } } 执行该方法后,我们会发现,线程停止在打印出"a"后,一直在等待。这就是因为该锁不是可重入锁。 可重入锁的设计 我在这里只贴和上面代码不同的部分。 public class MyLock implements Lock { //声明一个判断锁的布尔值 private boolean isLocked = false; Thread lockBy = null; int lockCount = 0; @Override public synchronized void lock() { Thread currentThread = Thread.currentThread(); //获取到当前线程 while (isLocked && currentThread != lockBy){ try { wait(); } catch (InterruptedException e) { e.printStackTrace(); } } isLocked = true; lockBy = currentThread; //将currentThread线程指向 lockBy线程 lockCount++;//计数器自增 } @Override public synchronized void unlock() { if (lockBy == Thread.currentThread()){ lockCount--; if (lockCount ==0 ){ notify(); isLocked = false; } } } } 第一个线程执行a()方法,得到了锁,使lockedBy等于当前线程,也就是说,执行的这个方法的线程获得了这个锁,执行add()方法时,同样要先获得锁,因不满足while循环的条件,也就是不等待,继续进行,将此时的lockedCount变量,也就是当前获得锁的数量加一,当释放了所有的锁,才执行notify()。如果在执行这个方法时,有第二个线程想要执行这个方法,因为lockedBy不等于第二个线程,导致这个线程进入了循环,也就是等待,不断执行wait()方法。只有当第一个线程释放了所有的锁,执行了notify()方法,第二个线程才得以跳出循环,继续执行。

优秀的个人博客,低调大师

【总结】Spark优化(1)-多Job并发执行

Spark程序中一个Job的触发是通过一个Action算子,比如count(), saveAsTextFile()等 在这次Spark优化测试中,从Hive中读取数据,将其另外保存四份,其中两个Job采用串行方式,另外两个Job采用并行方式。将任务提交到Yarn中执行。能够明显看出串行与兵线处理的性能。 每个Job执行时间: JobID 开始时间 结束时间 耗时 Job 0 16:59:45 17:00:34 49s Job 1 17:00:34 17:01:13 39s Job 2 17:01:15 17:01:55 40s Job 3 17:01:16 17:02:12 56s 四个Job都是自执行相同操作,Job0,Job1一组采用串行方式,Job2,Job3采用并行方式。 Job0,Job1串行方式耗时等于两个Job耗时之和 49s+39s=88s Job2,Job3并行方式耗时等于最先开始和最后结束时间只差17:02:12-17:01:15=57s 代码: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 package com.cn.ctripotb; import org.apache.spark.SparkConf; import org.apache.spark.api.java.JavaSparkContext; import org.apache.spark.sql.DataFrame; import org.apache.spark.sql.hive.HiveContext; import java.util.*; import java.util.concurrent.Callable; import java.util.concurrent.Executors; /** *CreatedbyAdministratoron2016/9/12. */ public class HotelTest{ static ResourceBundlerb=ResourceBundle.getBundle( "filepath" ); public static void main(String[]args){ SparkConfconf= new SparkConf() .setAppName( "MultiJobWithThread" ) .set( "spark.serializer" , "org.apache.spark.serializer.KryoSerializer" ); JavaSparkContextsc= new JavaSparkContext(conf); HiveContexthiveContext= new HiveContext(sc.sc()); //测试真实数据时要把这里放开 final DataFramedf=getHotelInfo(hiveContext); //没有多线程处理的情况,连续执行两个Action操作,生成两个Job df.rdd().saveAsTextFile(rb.getString( "hdfspath" )+ "/file1" ,com.hadoop.compression.lzo.LzopCodec. class ); df.rdd().saveAsTextFile(rb.getString( "hdfspath" )+ "/file2" ,com.hadoop.compression.lzo.LzopCodec. class ); //用Executor实现多线程方式处理Job java.util.concurrent.ExecutorServiceexecutorService=Executors.newFixedThreadPool( 2 ); executorService.submit( new Callable<Void>(){ @Override public Voidcall(){ df.rdd().saveAsTextFile(rb.getString( "hdfspath" )+ "/file3" ,com.hadoop.compression.lzo.LzopCodec. class ); return null ; } }); executorService.submit( new Callable<Void>(){ @Override public Voidcall(){ df.rdd().saveAsTextFile(rb.getString( "hdfspath" )+ "/file4" ,com.hadoop.compression.lzo.LzopCodec. class ); return null ; } }); executorService.shutdown(); } public static DataFramegetHotelInfo(HiveContexthiveContext){ Stringsql= "select*fromcommon.dict_hotel_ol" ; return hiveContext.sql(sql); } } 本文转自巧克力黒 51CTO博客,原文链接:http://blog.51cto.com/10120275/1961130 ,如需转载请自行联系原作者

优秀的个人博客,低调大师

iOS: 并发编程的几个知识点

iOS 多线程问题 查阅的大部分资料都是英文的,整理完毕之后,想翻译成中文,却发现很多名字翻译成中文很难表述清楚。 所以直接把整理好的资料发出来,大家就当顺便学习学习英语。 1. Thread Safe Vs Main Thread Safe Main Thread Safe means only safe execute on main thread; Thread Safe means you can modify on any thread simultaneously; 2. ConditionLock Vs Condition NSCondition A condition variable whose semantics follow those used for POSIX-style conditions. A condition is another type of semaphore that allows threads to signal each other when a certain condition is true.Conditions are typically used to indicate the availability of a resource or to ensure that tasks are performed in a specific order.When a thread tests a condition, it blocks unless that condition is already true. It remains blocked until some other thread explicitly changes and signals the condition. The difference between acondition and a mutex lock is that multiple threads may be permitted access to the condition at the same time.The condition is more of a gatekeeper that lets different threads through the gate depending on some specified criteria. Due to the subtleties involved in implementing operating systems, condition locks are permitted to return with spurious success even if they were not actually signaled by your code.To avoid problems caused by these spurious signals, you should always use a predicate in conjunction with your condition lock. When a thread waits on a condition, the condition object unlocks its lock and blocks the thread. When the condition is signaled, the system wakes up the thread. The condition object then reacquires its lock before returning from thewaitorwaitUntilDate:method. Thus, from the point of view of the thread, it is as if it always held the lock. A boolean predicate is an important part of the semantics of using conditions because of the way signaling works. Signaling a condition does not guarantee that the condition itself is true.Using a predicate ensures that these spurious signals do not cause you to perform work before it is safe to do so. The predicate itself is simply a flag or other variable in your code that you test in order to acquire a Boolean result. Thesemanticsfor using anNSConditionobject is as follows: Lock the condition object. Test a boolean predicate. (This predicate is a boolean flag or other variable in your code that indicates whether it is safe to perform the task protected by the condition.) If the boolean predicate is false, call the condition object’swaitorwaitUntilDate:method to block the thread. Upon returning from these methods, go to step 2 to retest your boolean predicate. (Continue waiting and retesting the predicate until it is true.) If the boolean predicate is true, perform the task. Optionally update any predicates (or signal any conditions) affected by your task. When your task is done, unlock the condition object. lock the condition while (!(boolean_predicate)) { wait on condition } do protected work (optionally, signal or broadcast the condition again or change a predicate value) unlock the condition NSCondition的底层是通过pthread_mutex + pthread_cond_t来实现的。 NSConditionLock A lock that can be associated with specific, user-defined conditions. Using anNSConditionLockobject, you can ensure that a thread can acquire a lockonly if a certain condition is met. AnNSConditionLockobject defines a mutex lock that can be locked and unlocked with specific values. NSConditionLock just support condition with a int, if you want support a custom condition value, you should use NSCondition. 用互斥所能不能实现生产者,消费者模型???答案是: YES 参考资料: https://web.stanford.edu/class/cs140/cgi-bin/lecture.php?topic=locks http://blog.ibireme.com/2016/01/16/spinlock_is_unsafe_in_ios/ https://bestswifter.com/ios-lock/ 3. @synchronized Directive The object passed to the @synchronized directive is aunique identifierused to distinguish the protected block. If you execute the preceding method in two different threads, passing a different object for theanObjparameter on each thread, each would take its lock and continue processing without being blocked by the other. If you pass the same object in both cases, however, one of the threads would acquire the lock first and the other would block until the first thread completed the critical section. Several Common ways to use @synchronized wrong @synchronized(nil) @synchronized(][NSObject all] init]) Exceptions With @synchronized As a precautionary measure, the@synchronizedblock implicitly adds an exception handler to the protected code.This handler automatically releases the mutex in the event that an exception is thrown. This means that in order to use the@synchronizeddirective, you must also enable Objective-C exception handling in your code. If you do not want the additional overhead caused by the implicit exception handler, you should consider using the lock classes. 原理 OBJC_EXPORT int objc_sync_enter(id obj) OBJC_AVAILABLE(10.3, 2.0, 9.0, 1.0); OBJC_EXPORT int objc_sync_exit(id obj) OBJC_AVAILABLE(10.3, 2.0, 9.0, 1.0); @synchronized(obj) { // do work } 会被编译器转换为: @try { objc_sync_enter(obj); // do work } @finally { objc_sync_exit(obj); } Example 结论: 你调用sychronized的每个对象,Objective-C runtime 都会为其分配一个递归锁并存储在哈希表中。 如果在sychronized内部对象被释放或被设为nil看起来都 OK。 注意不要向你的sychronizedblock 传入nil!这将会从代码中移走线程安全。 参考资料 http://rykap.com/objective-c/2015/05/09/synchronized/ http://yulingtianxia.com/blog/2015/11/01/More-than-you-want-to-know-about-synchronized/ https://opensource.apple.com/source/objc4/objc4-646/runtime/objc-sync.mm 4. Runloop Perform selector on a thread 当目标线程runloop未启动时是没有效果的。 启动 Runloop If no input sources or timers are attached to the run loop, this method exits immediately; Manually removing all known input sources and timers from the run loop is not a guarantee that the run loop will exit.macOS can install and remove additional input sources as needed to process requests targeted at the receiver’s thread. Those sources could therefore prevent the run loop from exiting. The Run Loop Sequence of Events Each time you run it, your thread’s run loop processes pending events and generates notifications for any attached observers. The order in which it does this is very specific and is as follows: Notify observers that the run loop has been entered. Notify observers that any ready timers are about to fire. Notify observers that any input sources that are not port based are about to fire. Fire any non-port-based input sources that are ready to fire. If a port-based input source is ready and waiting to fire, process the event immediately. Go to step 9. Notify observers that the thread is about to sleep. Put the thread to sleep until one of the following events occurs: An event arrives for a port-based input source. A timer fires. The timeout value set for the run loop expires. The run loop is explicitly woken up. Notify observers that the thread just woke up. Process the pending event. If a user-defined timer fired, process the timer event and restart the loop. Go to step 2. If an input source fired, deliver the event. If the run loop was explicitly woken up but has not yet timed out, restart the loop. Go to step 2. Notify observers that the run loop has exited. Example: Detect Main Runloop lag with RunloopObserver 参考资料: https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/Multithreading/RunLoopManagement/RunLoopManagement.html http://www.tanhao.me/code/151113.html/ 6. Queue Vs Thread Thread != Queue A queue doesn't own a thread and a thread is not bound to a queue. There are threads and there are queues. Whenever a queue wants to run a block, it needs a thread but that won't always be the same thread. It just needs any thread for it (this may be a different one each time) and when it's done running blocks (for the moment), the same thread can now be used by a different queue. There's also no guarantee that a given serial queue will always use the same thread. The only exception is the main queue: dispatch_get_main_queue will must run on main thread. While, main thread may run task at any more than one queue. 7. Dispatch Sync Vs Dispatch Async dispatch_sync dispatch_sync └──dispatch_sync_f └──_dispatch_sync_f2 └──_dispatch_sync_f_slow static void _dispatch_sync_f_slow(dispatch_queue_t dq, void *ctxt, dispatch_function_t func) { _dispatch_thread_semaphore_t sema = _dispatch_get_thread_semaphore(); struct dispatch_sync_slow_s { DISPATCH_CONTINUATION_HEADER(sync_slow); } dss = { .do_vtable = (void*)DISPATCH_OBJ_SYNC_SLOW_BIT, .dc_ctxt = (void*)sema, }; _dispatch_queue_push(dq, (void *)&dss); _dispatch_thread_semaphore_wait(sema); _dispatch_put_thread_semaphore(sema); // ... } Submits a block to a dispatch queue for synchronous execution. Unlike dispatch_async,this function does not return until the block has finished. Calling this function and targeting the current queue results in deadlock. Unlike withdispatch_async,no retain is performed on the target queue.Because calls to this function are synchronous, it "borrows" the reference of the caller. Moreover, noBlock_copyis performed on the block. As an optimization, this function invokes the block on the current thread when possible. dispatch_syncdoes two things: queue a block blocks the current thread until the block has finished running dispatch_async dispatch_async(dispatch_queue_t queue, dispatch_block_t block) { dispatch_async_f(dq, _dispatch_Block_copy(work), _dispatch_call_block_and_release); } dispatch_async_f(dispatch_queue_t queue, void *context, dispatch_function_t work); Dead Locks dispatch_sync(queueA, ^{ dispatch_sync(queueB, ^{ dispatch_sync(queueA, ^{ // DEAD LOCK // some task }); }); }); Example: dispatch_async(QueueA, ^{ someFunctionA(...); dispatch_sync(QueueB, ^{ someFunctionB(...); }); }); WhenQueueAruns the block, it will temporarily own a thread, any thread.someFunctionA(...)will execute on that thread. Now while doing the synchronous dispatch,QueueAcannot do anything else, it has to wait for the dispatch to finish.QueueBon the other hand, will also need a thread to run its block and executesomeFunctionB(...). So eitherQueueAtemporarily suspends its thread andQueueBuses some other thread to run the block orQueueAhands its thread over toQueueB(after all it won't need it anyway until the synchronous dispatch has finished) andQueueBdirectly uses the current thread ofQueueA. Needless to say that the last option is much faster as no thread switch is required. Andthisis the optimization the sentence talks about. So adispatch_sync()to a different queue may not always cause a thread switch (different queue, maybe same thread). But adispatch_sync()still cannot happen to the same queue (same thread, yes, same queue, no). That's because a queue will execute block after block and when it currently executes a block, it won't execute another one until this one is done. So it executesBlockAandBlockAdoes adispatch_sync()ofBlockBon the same queue. The queue won't runBlockBas long as it still runsBlockA, but runningBlockAwon't continue untilBlockBhas run. Important:You should never call thedispatch_syncordispatch_sync_ffunction from a task that is executing in the same queue that you are planning to pass to the function. This is particularly important for serial queues, which are guaranteed to deadlock, butshould also be avoided for concurrent queues. 8. Dispatch set target The misunderstanding here is thatdispatch_get_specificdoesn't traverse thestackof nested queues, it traverses thequeue targeting lineage. Modifying the target queue of some objects changes their behavior: Dispatch queues: A dispatch queue's priority is inherited from its target queue. If you submit a block to a serial queue, and the serial queue’s target queue is a different serial queue, that block is not invoked concurrently with blocks submitted to the target queue or to any other queue with that same target queue. Dispatch sources: A dispatch source's target queue specifies where its event handler and cancellation handler blocks are submitted. Dispatch I/O channels: A dispatch I/O channel's target queue specifies where its I/O operations are executed. By default, a newly created queue forwards into the default priority global queue. 参考资料: https://bestswifter.com/deep-gcd/?spm=5176.100239.0.0.vCv2rL https://stackoverflow.com/questions/20860997/dispatch-queue-set-specific-vs-getting-the-current-queue https://stackoverflow.com/questions/23955948/why-did-apple-deprecate-dispatch-get-current-queue https://stackoverflow.com/questions/7346929/why-do-we-use-builtin-expect-when-a-straightforward-way-is-to-use-if-else https://www.objc.io/issues/2-concurrency/concurrency-apis-and-pitfalls/?spm=5176.100239.blogcont17709.5.71pknM libdispatch 源码:https://opensource.apple.com/tarballs/libdispatch/ 9. Read-write Lock in GCD Use dispatch_barrier_async(). When the barrier block reaches the front of a private concurrent queue, it is not executed immediately. Instead, the queue waits until its currently executing blocks finish executing. At that point, the barrier block executes by itself.Any blocks submitted after the barrier block are not executed until the barrier block completes. The queue you specify should be a concurrent queue that you create yourself using thedispatch_queue_create function.If the queue you pass to this function is a serial queue or one of the global concurrent queues, this function behaves like thedispatch_asyncfunction. 附录: 测试Demo:http://files.cnblogs.com/files/smileEvday/iOSMultiThreadSample.zip 部门招人: 高级iOS、Android、前端开发,有意私聊,博主请你喝️ 如果觉得本文帮到了你,记得点赞哦,当然也可以请博主喝一杯豆浆 微信二维码 QQ二维码

资源下载

更多资源
Mario

Mario

马里奥是站在游戏界顶峰的超人气多面角色。马里奥靠吃蘑菇成长,特征是大鼻子、头戴帽子、身穿背带裤,还留着胡子。与他的双胞胎兄弟路易基一起,长年担任任天堂的招牌角色。

Nacos

Nacos

Nacos /nɑ:kəʊs/ 是 Dynamic Naming and Configuration Service 的首字母简称,一个易于构建 AI Agent 应用的动态服务发现、配置管理和AI智能体管理平台。Nacos 致力于帮助您发现、配置和管理微服务及AI智能体应用。Nacos 提供了一组简单易用的特性集,帮助您快速实现动态服务发现、服务配置、服务元数据、流量管理。Nacos 帮助您更敏捷和容易地构建、交付和管理微服务平台。

Spring

Spring

Spring框架(Spring Framework)是由Rod Johnson于2002年提出的开源Java企业级应用框架,旨在通过使用JavaBean替代传统EJB实现方式降低企业级编程开发的复杂性。该框架基于简单性、可测试性和松耦合性设计理念,提供核心容器、应用上下文、数据访问集成等模块,支持整合Hibernate、Struts等第三方框架,其适用范围不仅限于服务器端开发,绝大多数Java应用均可从中受益。

WebStorm

WebStorm

WebStorm 是jetbrains公司旗下一款JavaScript 开发工具。目前已经被广大中国JS开发者誉为“Web前端开发神器”、“最强大的HTML5编辑器”、“最智能的JavaScript IDE”等。与IntelliJ IDEA同源,继承了IntelliJ IDEA强大的JS部分的功能。

用户登录
用户注册