java高并发锁机制

java并发常识

1.java并发编程是什么

1, 保证线程安全的三种方法: a, 不要跨线程访问共享变量b, 使共享变量是final类型的c, 将共享变量的操作加上同步 2, 一开始就将类设计成线程安全的, 比在后期重新修复它,更容易。

3, 编写多线程程序, 首先保证它是正确的, 其次再考虑性能。 4, 无状态或只读对象永远是线程安全的。

5, 不要将一个共享变量 *** 在多线程环境下(无同步或不可变性保护) 6, 多线程环境下的延迟加载需要同步的保护, 因为延迟加载会造成对象重复实例化 7, 对于volatile声明的数值类型变量进行运算, 往往是不安全的(volatile只能保证可见性,不能保证原子性)。 详见volatile原理与技巧中, 脏数据问题讨论。

8, 当一个线程请求获得它自己占有的锁时(同一把锁的嵌套使用), 我们称该锁为可重入锁。在jdk1。

5并发包中, 提供了可重入锁的java实现-ReentrantLock。 9, 每个共享变量,都应该由一个唯一确定的锁保护。

创建与变量相同数目的ReentrantLock, 使他们负责每个变量的线程安全。 10,虽然缩小同步块的范围, 可以提升系统性能。

但在保证原子性的情况下, 不可将原子操作分解成多个synchronized块。 11, 在没有同步的情况下, 编译器与处理器运行时的指令执行顺序可能完全出乎意料。

原因是, 编译器或处理器为了优化自身执行效率, 而对指令进行了的重排序(reordering)。 12, 当一个线程在没有同步的情况下读取变量, 它可能会得到一个过期值, 但是至少它可以看到那个线程在当时设定的一个真实数值。

而不是凭空而来的值。 这种安全保证, 称之为最低限的安全性(out-of-thin-air safety) 在开发并发应用程序时, 有时为了大幅度提高系统的吞吐量与性能, 会采用这种无保障的做法。

但是针对, 数值的运算, 仍旧是被否决的。 13, volatile变量,只能保证可见性, 无法保证原子性。

14, 某些耗时较长的网络操作或IO, 确保执行时, 不要占有锁。 15, 发布(publish)对象, 指的是使它能够被当前范围之外的代码所使用。

(引用传递)对象逸出(escape), 指的是一个对象在尚未准备好时将它发布。 原则: 为防止逸出, 对象必须要被完全构造完后, 才可以被发布(最好的解决方式是采用同步) this关键字引用对象逸出 例子: 在构造函数中, 开启线程, 并将自身对象this传入线程, 造成引用传递。

而此时, 构造函数尚未执行完, 就会发生对象逸出了。 16, 必要时, 使用ThreadLocal变量确保线程封闭性(封闭线程往往是比较安全的, 但一定程度上会造成性能损耗)封闭对象的例子在实际使用过程中, 比较常见, 例如 hibernate openSessionInView机制, jdbc的connection机制。

17, 单一不可变对象往往是线程安全的(复杂不可变对象需要保证其内部成员变量也是不可变的)良好的多线程编程习惯是: 将所有的域都声明为final, 除非它们是可变的。

2.Java线程并发协作是什么

线程发生死锁可能性很小,即使看似可能发生死锁的代码,在运行时发生死锁的可能性也是小之又小。

发生死锁的原因一般是两个对象的锁相互等待造成的。 在《Java线程:线程的同步与锁》一文中,简述死锁的概念与简单例子,但是所给的例子是不完整的,这里给出一个完整的例子。

/** * Java线程:并发协作-死锁 * * @author Administrator 2009-11-4 22:06:13 */ public class Test { public static void main(String[] args) { DeadlockRisk dead = new DeadlockRisk(); MyThread t1 = new MyThread(dead, 1, 2); MyThread t2 = new MyThread(dead, 3, 4); MyThread t3 = new MyThread(dead, 5, 6); MyThread t4 = new MyThread(dead, 7, 8); t1。 start(); t2。

start(); t3。start(); t4。

start(); } } class MyThread extends Thread { private DeadlockRisk dead; private int a, b; MyThread(DeadlockRisk dead, int a, int b) { this。 dead = dead; this。

a = a; this。b = b; } @Override public void run() { dead。

read(); dead。write(a, b); } } class DeadlockRisk { private static class Resource { public int value; }。

3.如何学习Java高并发

1.学习 *** 并发框架的使用,如ConcurrentHashMAP,CopyOnWriteArrayList/Set等2.几种并发锁的使用以及线程同步与互斥,如ReentainLock,synchronized,Lock,CountDownLatch,Semaphore等3.线程池如Executors,ThreadPoolExecutor等4.Runable,Callable,RescureTask,Future,FutureTask等5.Fork-Join框架以上基本包含完了,如有缺漏请原谅。

4.并发编程的Java抽象有哪些呢

一、机器和OS级别抽象 (1)冯诺伊曼模型 经典的顺序化计算模型,貌似可以保证顺序化一致性,但是没有哪个现代的多处理架构会提供顺序一致性,冯氏模型只是现代多处理器行为的模糊近似。

这个计算模型,指令或者命令列表改变内存变量直接契合命令编程泛型,它以显式的算法为中心,这和声明式编程泛型有区别。 就并发编程来说,会显著的引入时间概念和状态依赖 所以所谓的函数式编程可以解决其中的部分问题。

(2)进程和线程 进程抽象运行的程序,是操作系统资源分配的基本单位,是资源cpu,内存,IO的综合抽象。 线程是进程控制流的多重分支,它存在于进程里,是操作系统调度的基本单位,线程之间同步或者异步执行,共享进程的内存地址空间。

(3)并发与并行 并发,英文单词是concurrent,是指逻辑上同时发生,有人做过比喻,要完成吃完三个馒头的任务,一个人可以这个馒头咬一口,那个馒头咬一口,这样交替进行,最后吃完三个馒头,这就是并发,因为在三个馒头上同时发生了吃的行为,如果只是吃完一个接着吃另一个,这就不是并发了,是排队,三个馒头如果分给三个人吃,这样的任务完成形式叫并行,英文单词是parallel。 回到计算机概念,并发应该是单CPU时代或者单核时代的说法,这个时候CPU要同时完成多任务,只能用时间片轮转,在逻辑上同时发生,但在物理上是串行的。

现在大多数计算机都是多核或者多CPU,那么现在的多任务执行方式就是物理上并行的。 为了从物理上支持并发编程,CPU提供了相应的特殊指令,比如原子化的读改写,比较并交换。

(4)平台内存模型 在可共享内存的多处理器体系结构中,每个处理器都有它自己的缓存,并且周期性的与主存同步,为什么呢?因为处理器通过降低一致性来换取性能,这和CAP原理通过降低一致性来获取伸缩性有点类似,所以大量的数据在CPU的寄存器中被计算,另外CPU和编译器为了性能还会乱序执行,但是CPU会提供存储关卡指令来保证存储的同步,各种平台的内存模型或者同步指令可能不同,所以这里必须介入对内存模型的抽象,JMM就是其中之一。 二、编程模型抽象 (1)基于线程模型 (2)基于Actor模型 (3)基于STM软件事务内存 …… Java体系是一个基于线程模型的本质编程平台,所以我们主要讨论线程模型。

三、并发单元抽象 大多数并发应用程序都是围绕执行任务进行管理的,任务是抽象,离散的工作单元,所以编写并发程序,首要工作就是提取和分解并行任务。 一旦任务被抽象出来,他们就可以交给并发编程平台去执行,同时在任务抽象还有另一个重要抽象,那就是生命周期,一个任务的开始,结束,返回结果,都是生命周期中重要的阶段。

那么编程平台必须提供有效安全的管理任务生命周期的API。 四、线程模型 线程模型是Java的本质模型,它无所不在,所以Java开发必须搞清楚底层线程调度细节,不搞清楚当然就会有struts1,struts2的原理搞不清楚的基本灾难(比如在struts2的action中塞入状态,把struts2的action配成单例)。

用线程来抽象并发编程,是比较低级别的抽象,所以难度就大一些,难度级别会根据我们的任务特点有以下几个类别 (1)任务非常独立,不共享,这是最理想的情况,编程压力为0。 (2)共享数据,压力开始增大,必须引入锁,Volatile变量,问题有活跃度和性能危险。

(3)状态依赖,压力再度增大,这时候我们基本上都是求助jdk 提供的同步工具。 五、任务执行 任务是一个抽象体,如果被抽象了出来,下一步就是交给编程平台去执行,在Java中,描述任务的一个基本接口是Runnable,可是这个抽象太有限了,它不能返回值和抛受检查异常,所以Jdk5。

0有另外一个高级抽象Callable。 任务的执行在Jdk中也是一个底级别的Thread,线程有好处,但是大量线程就有大大的坏处,所以如果任务量很多我们并不能就创建大量的线程去服务这些任务,那么Jdk5。

0在任务执行上做了抽象,将任务和任务执行隔离在接口背后,这样我们就可以引入比如线程池的技术来优化执行,优化线程的创建。 任务是有生命周期的,所以Jdk5。

0提供了Future这个对象来描述对象的生命周期,通过这个future可以取到任务的结果甚至取消任务。 六、锁 当然任务之间共享了数据,那么要保证数据的安全,必须提供一个锁机制来协调状态,锁让数据访问原子,但是引入了串行化,降低了并发度,锁是降低程序伸缩性的原罪,锁是引入上下文切换的主要原罪,锁是引入死锁,活锁,优先级倒置的绝对原罪,但是又不能没有锁,在Java中,锁是一个对象,锁提供原子和内存可见性,Volatile变量提供内存可见性不提供原子,原子变量提供可见性和原子,通过原子变量可以构建无锁算法和无锁数据结构,但是这需要高高手才可以办到。

5.Java高并发入门要怎么学习

1、如果不使用框架,纯原生Java编写,是需要了解Java并发编程的,主要就是学习Doug Lea开发的那个java.util.concurrent包下面的API;2、如果使用框架,那么我的理解,在代码层面确实不会需要太多的去关注并发问题,反而是由于高并发会给系统造成很大压力,要在缓存、数据库操作上要多加考虑。

3、但是即使是使用框架,在工作中还是会用到多线程,就拿常见的CRUD接口来说,比如一个非常耗时的save接口,有多耗时呢?我们假设整个save执行完要10分钟,所以,在save的时候,就需要采用异步的方式,也就是单独用一个线程去save,然后直接给前端返回200。

6.Java如何进行并发多连接socket编程呢

Java多个客户端同时连接服务端,在现实生活中用得比较多。

同时执行多项任务,第一想到的当然是多线程了。下面用多线程来实现并发多连接。

import java。。

*; import java。io。

*; public class ThreadServer extends Thread { private Socket client; public ThreadServer(Socket c) { this。 client=c; } public void run() { try { BufferedReader in=new BufferedReader(new InputStreamReader(client。

getInputStream())); PrintWriter out=new PrintWriter(client。 getOutputStream()); Mutil User but can’t parallel while (true) { String str=in。

readLine(); System。out。

println(str); out。 println(“has receive。

“); out。

flush(); if (str。equals(“end”)) break; } client。

close(); } catch (IOException ex) { } finally { } } public static void main(String[] args)throws IOException { ServerSocket server=new ServerSocket(8000); while (true) { transfer location change Single User or Multi User ThreadServer mu=new ThreadServer(server。 accept()); mu。

start(); } } }J。

7.如何掌握java多线程,高并发,大数据方面的技能

线程:同一类线程共享代码和数据空间,每个线程有独立的运行栈和程序计数器(PC),线程切换开销小。

(线程是cpu调度的最小单位)线程和进程一样分为五个阶段:创建、就绪、运行、阻塞、终止。多进程是指操作系统能同时运行多个任务(程序)。

多线程是指在同一程序中有多个顺序流在执行。在java中要想实现多线程,有两种手段,一种是继续Thread类,另外一种是实现Runable接口.(其实准确来讲,应该有三种,还有一种是实现Callable接口,并与Future、线程池结合使用。

8.java工程师需要掌握哪些知识

1.Core Java,就是Java基础、JDK的类库,很多童鞋都会说,JDK我懂,但是懂还不足够,知其然还要知其所以然,JDK的源代码写的非常好,要经常查看,对使用频繁的类,比如String, *** 类(List,Map,Set)等数据结构要知道它们的实现,不同的 *** 类有什么区别,然后才能知道在一个具体的场合下使用哪个 *** 类更适合、更高效,这些内容直接看源代码就OK了2.多线程并发编程,现在并发几乎是写服务端程序必须的技术,那对Java中的多线程就要有足够的熟悉,包括对象锁机制、synchronized关键字,concurrent包都要非常熟悉,这部分推荐你看看《Java并发编程实践》这本书,讲解的很详细3.I/O,Socket编程,首先要熟悉Java中Socket编程,以及I/O包,再深入下去就是Java NIO,再深入下去是操作系统底层的Socket实现,了解Windows和Linux中是怎么实现socket的4.JVM的一些知识,不需要熟悉,但是需要了解,这是Java的本质,可以说是Java的母体, 了解之后眼界会更宽阔,比如Java内存模型(会对理解Java锁、多线程有帮助)、字节码、JVM的模型、各种垃圾收集器以及选择、JVM的执行参数(优化JVM)等等,这些知识在《深入Java虚拟机》这本书中都有详尽的解释,或者去oracle网站上查看具体版本的JVM规范.5.一些常用的设计模式,比如单例、模板方法、代理、适配器等等,以及在Core Java和一些Java框架里的具体场景的实现,这个可能需要慢慢积累,先了解有哪些使用场景,见得多了,自己就自然而然会去用。

6.常用数据库(Oracle、MySQL等)、SQL语句以及一般的优化7.JavaWeb开发的框架,比如Spring、iBatis等框架,同样他们的原理才是最重要的,至少要知道他们的大致原理。8.其他一些有名的用的比较多的开源框架和包,ty网络框架,Apache mon的N多包,Google的Guava等等,也可以经常去Github上找一些代码看看。

暂时想到的就这么多吧,1-4条是Java基础,全部的这些知识没有一定的时间积累是很难搞懂的,但是了解了之后会对Java有个彻底的了解,5和6是需要学习的额外技术,7-8是都是基于1-4条的,正所谓万变不离其宗,前4条就是Java的灵魂所在,希望能对你有所帮助9.(补充)学会使用Git。如果你还在用SVN的话,赶紧投入Git的怀抱吧。

9.java 多线程的并发到底是什么意思

一、多线程1、操作系统有两个容易混淆的概念,进程和线程。

进程:一个计算机程序的运行实例,包含了需要执行的指令;有自己的独立地址空间,包含程序内容和数据;不同进程的地址空间是互相隔离的;进程拥有各种资源和状态信息,包括打开的文件、子进程和信号处理。线程:表示程序的执行流程,是CPU调度执行的基本单位;线程有自己的程序计数器、寄存器、堆栈和帧。

同一进程中的线程共用相同的地址空间,同时共享进进程锁拥有的内存和其他资源。2、Java标准库提供了进程和线程相关的API,进程主要包括表示进程的java.lang.Process类和创建进程的java.lang.ProcessBuilder类;表示线程的是java.lang.Thread类,在虚拟机启动之后,通常只有Java类的main方法这个普通线程运行,运行时可以创建和启动新的线程;还有一类守护线程(damon thread),守护线程在后台运行,提供程序运行时所需的服务。

当虚拟机中运行的所有线程都是守护线程时,虚拟机终止运行。3、线程间的可见性:一个线程对进程 *** 享的数据的修改,是否对另一个线程可见可见性问题:a、CPU采用时间片轮转等不同算法来对线程进行调度[java] view plaincopypublic class IdGenerator{ private int value = 0; public int getNext(){ return value++; } } 对于IdGenerator的getNext()方法,在多线程下不能保证返回值是不重复的:各个线程之间相互竞争CPU时间来获取运行机会,CPU切换可能发生在执行间隙。

以上代码getNext()的指令序列:CPU切换可能发生在7条指令之间,多个getNext的指令交织在一起。

如何处理高并发

问题一:java程序员面试时被问到:如何在j2ee项目中处理高并发量访问? 该怎么回答? 请仔细看题干再回答 blog.csdn/y_h_t/article/details/6322823

你是一名java程序员,这些应该知道些吧

问题二:如何处理高并发带来的系统性能问题 那必须了解linux中的基本使用,比如如何找到某个路径,如何打开一个文件,如何编辑修改一个文件等等,那就是linux中命令的使用;还有就是必须知道linux服务器中所用的什么服务器(有weblogic、websphere等等);精通相关服务器的重要属性配置等等。

问题三:JAVA中高访问量高并发的问题怎么解决? 你指的高并发量大概有多少?

几点需要注意:

尽量使用缓存,包括用户缓存,信息缓存等,多花点内存来做缓存,可以大量减少与数据库的交互,提高性能。

用jprofiler等工具找出性能瓶颈,减少额外的开销。

优化数据库查询语句,减少直接使用hibernate等工具的直接生成语句(仅耗时较长的查询做优化)。

优化数据库结构,多做索引,提高查询效率。

统计的功能尽量做缓存,或按每天一统计或定时统计相关报表,避免需要时进行统计的功能。

能使用静态页面的地方尽量使用,减少容器的解析(尽量将动态内容生成静态html来显示)。

解决以上问题后,使用服务器集群来解决单台的瓶颈问题。

基本上以上述问题解决后,达到系统最优。

至于楼上有人提到别用JAVA来做,除非是低层的连接数过大(如大量的端口占用需求),这种情况下考虑直接C来写,其他的可以用JAVA来做。

问题四:项目中怎么控制多线程高并发访问 synchronized关键字主要解决多线程共享数据同步问题。

ThreadLocal使用场合主要解决多线程中数据因并发产生不一致问题。

ThreadLocal和Synchonized都用于解决多线程并发访问。但是ThreadLocal与synchronized有本质的区别:

synchronized是利用锁的机制,使变量或代码块在某一时该只能被一个线程访问。而ThreadLocal为每一个线程都提供了变量的副本,使 得每个线程在某一时间访问到的并不是同一个对象,这样就隔离了多个线程对数据的数据共享。而Synchronized却正好相反,它用于在多个线程间通信 时能够获得数据共享。

Synchronized用于线程间的数据共享,而ThreadLocal则用于线程间的数据隔离。当然ThreadLocal并不能替代synchronized,它们处理不同的问题域。Synchronized用于实现同步机制,比ThreadLocal更加复杂。

1、Java中synchronized用法

使用了synchronized关键字可以轻松地解决多线程共享数据同步问题。

synchronized关键字可以作为函数的修饰符,也可作为函数内的语句,也就是平时说的同步方法和同步语句块。如果再细的分 类,synchronized可作用于instance变量、object reference(对象引用)、static函数和class literals(类名称字面常量)身上。

synchronized取得的锁都是对象;每个对象只有一个锁(lock)与之相关联;实现同步是要很大的系统开销作为代价的,甚至可能造成死锁,所以尽量避免无谓的同步控制。

问题五:如何处理高并发或列举处理高并发的业务逻辑 1、提高系统的并发能力2、减轻数据库的负担这两种用途其实非常容易理解。由于memcached高性能,所以可以同时服务于更多的连接,大大提高了系统的并发处理的能力。另外,memcached 通常部署在业务逻辑层(前台应用)和存储层(主指数据库)之间,作为数据库和前台应用的数据缓冲,因此可以快速的响应前端的请求,减少对数据库的访问。

问题六:数据库怎样处理高并发 1.用一个标识,在选择那张票的时候先用(Update 表 set 票flag=‘占用了!’ where 票flag=‘未占用’ and ……..)这样是保险的,不可能存在并发问题,这就牵扯到sql锁机制问题了,你可以测试一下,其实sql中update是先查询出然后删除再添加,但由于使用了update,过程中就自动加锁了,很方便吧2.加锁。Microsoft® SQL Server™ 2000 使用锁定确保事务完整性和数据库一致性。锁定可以防止用户读取正在由其他用户更改的数据,并可以防止多个用户同时更改相同数据。如果不使用锁定,则数据库中的数据可能在逻辑上不正确,并且对数据的查询可能会产生意想不到的结果。虽然 SQL Server 自动强制锁定,但可以通过了解锁定并在应用程序中自定义锁定来设计更有效的应用程序。

问题七:数据库怎样处理高并发 理论上不限制并发连接数的.就是服务器受硬件的限制.过高的并发是会使服务器无法完成并发任务,而造成服务器死机或者假死机.不过数据库软件可以优化并发连接,使并发持续的时间更短,以减起服务器的负担,但是一台服务器不能完成几十万的并发.

问题八:如何处理大量数据并发操作 如何处理大量数据并发操作

文件缓存,数据库缓存,优化sql,数据分流,数据库表的横向和纵向划分,优化代码结构!

锁述的概

一. 为什么要引入锁

多个用户同时对数据库的并发操作时会带来以下数据不一致的问题:

丢失更新

A,B两个用户读同一数据并进行修改,其中一个用户的修改结果破坏了另一个修改的结果,比如订票系统

脏读

A用户修改了数据,随后B用户又读出该数据,但A用户因为某些原因取消了对数据的修改,数据恢复原值,此时B得到的数据就与数据库内的数据产生了不一致

不可重复读

A用户读取数据,随后B用户读出该数据并修改,此时A用户再读取数据时发现前后两次的值不一致

并发控制的主要方法是封锁,锁就是在一段时间内禁止用户做某些操作以避免产生数据不一致

二 锁的分类

锁的类别有两种分法:

1. 从数据库系统的角度来看:分为独占锁(即排它锁),共享锁和更新锁

MS-SQL Server 使用以下资源锁模式。

锁模式 描述

共享 (S) 用于不更改或不更新数据的操作(只读操作),如 SELECT 语句。

更新 (U) 用于可更新的资源中。防止当多个会话在读取、锁定以及随后可能进行的资源更新时发生常见形式的死锁。

排它 (X) 用于数据修改操作,例如 INSERT、UPDATE 或 DELETE。确保不会同时同一资源进行多重更新。

意向锁 用于建立锁的层次结构。意向锁的类型为:意向共享 (IS)、意向排它 (IX) 以及与意向排它共享 (SIX)。

架构锁 在执行依赖于表架构的操作时使用。架构锁的类型为:架构修改 (Sch-M) 和架构稳定性 (Sch-S)。

大容量更新 (BU) 向表中大容量复制数据并指定了 TABLOCK 提示时使用。

共享锁

共享 (S) 锁允许并发事务读取 (SELECT) 一个资源。资源上存在共享 (S) 锁时,任何其它事务都不能修改数据。一旦已经读取数据,便立即释放资源上的共享 (S) 锁,除非将事务隔离级别设置为可重复读或更高级别,或者在事务生存周期内用锁定提示保留共享 (S) 锁。

更新锁

更新 (U) 锁可以防止通常形式的死锁。一般更新模式由一个事务组成,此事务读取记录,获取资源(页或行)的共享 (S) 锁,然后修改行,此操作要求锁转换为排它 (X) 锁。如果两个事务获得了资源上的共享模式锁,然后试图同时更新数据,则一个事务尝试将锁转换为排它 (X) 锁。共享模式到排它锁的转换必须等待一段时间,因为一个事务的排它锁与其它事务的共享模式锁不兼容;发生锁等待。第二个事务试图获取排它 (X) 锁以进行更新。由于两个事务都要转换为排它 (X) 锁,并且每个事务都等待另一个事务释放共享模式锁,因此发生死锁。

若要避免这种潜在的死锁问题,请使用更新 (U) 锁。一次只有一个事务可以获得资源的更新 (U) 锁。如果事务修改资源,则更新 (U) 锁转换为排它 (X) 锁。否则,锁转换为共享锁。

排它锁

排它 (X) 锁可以防止并发事务对资源进行访问。其它事务不能读取或修改排它 (X) 锁锁定的数据。

意向锁

意向锁表示 SQL Server 需要在层次结构中的某些底层资源上获取共享 (S) 锁或排它 (X) 锁。例如,放置在表级的共享意向锁表示事务打算在表中的页或行上放置共享 (S) 锁。在表级设置意向锁可防止另一个事务随后在包含那一页的表上获取排它 (X) 锁。意向锁可以提高性能,因为 SQL Server 仅在表级检查意向锁来确定事务是否可以安全地获取该表上的锁。而无须检查表中的每行或每页上的锁……

问题九:高并发是什么和如何解决 数据库建立多表关联,关键业务数据字段和查询字段建立索引,对唯一性建立好,同时多任务并发时程序设计时注意数据的合理性检验和用户处理数据有问题时的友好提示见面,建立好的结构文档说明,同时对关键字段的关系型作好记录,有效地设计多表的结构安排,尽量减少数据的冗余,同时又要避免对历史数据的影响,保持良好的数据管理

问题十:如何处理高并发量的HTTP请求 尽量减少页面的HTTP请求,可以提高页面载入速度。减少页面中的元素网页中的的图片、form、flash等等元素都会发出HTTP请求,尽可能的减少页面中非必要的元素,可以减少HTTP请求的次数。

java高并发锁机制

java、oracle 高并发,编程问题

你这里要考虑并发与业务逻辑的一个平衡。但是事实上只能先保证业务正确,那么就需要对同一条记录(即同一个单子)进行线性操作了。

最简单的,利用oracle存储过程的事务原子性和行锁机制就可以解决。

比如:

create or replace procedure aaa( …)

as

v_count number;

begin

….

select count(*) into v_count from tbl where id=123 for update;

— 利用for update锁住对应这个单号的这条记录。这样,在这个存储过程没有commit之前,别的存储过程对id号(你的单号)为123的记录的for update查询只能等待。

….. 处理你的业务逻辑;

update tbl set amout=amout+1 where id=123; 这里把跟单人数加1.

… 其他逻辑

end;

/

高并发没锁可不行,三种分布式锁详解

Java中的锁主要包括synchronized锁和JUC包中的锁,这些锁都是针对单个JVM实例上的锁,对于分布式环境如果我们需要加锁就显得无能为力。在单个JVM实例上,锁的竞争者通常是一些不同的线程,而在分布式环境中,锁的竞争者通常是一些不同的线程或者进程。如何实现在分布式环境中对一个对象进行加锁呢?答案就是分布式锁。

目前分布式锁的实现方案主要包括三种:

基于数据库实现分布式锁主要是利用数据库的唯一索引来实现,唯一索引天然具有排他性,这刚好符合我们对锁的要求:同一时刻只能允许一个竞争者获取锁。加锁时我们在数据库中插入一条锁记录,利用业务id进行防重。当第一个竞争者加锁成功后,第二个竞争者再来加锁就会抛出唯一索引冲突,如果抛出这个异常,我们就判定当前竞争者加锁失败。防重业务id需要我们自己来定义,例如我们的锁对象是一个方法,则我们的业务防重id就是这个方法的名字,如果锁定的对象是一个类,则业务防重id就是这个类名。

基于缓存实现分布式锁:理论上来说使用缓存来实现分布式锁的效率最高,加锁速度最快,因为Redis几乎都是纯内存操作,而基于数据库的方案和基于Zookeeper的方案都会涉及到磁盘文件IO,效率相对低下。一般使用Redis来实现分布式锁都是利用Redis的 SETNX key value 这个命令,只有当key不存在时才会执行成功,如果key已经存在则命令执行失败。

基于Zookeeper:Zookeeper一般用作配置中心,其实现分布式锁的原理和Redis类似,我们在Zookeeper中创建瞬时节点,利用节点不能重复创建的特性来保证排他性。

在实现分布式锁的时候我们需要考虑一些问题,例如:分布式锁是否可重入,分布式锁的释放时机,分布式锁服务端是否有单点问题等。

上面已经分析了基于数据库实现分布式锁的基本原理:通过唯一索引保持排他性,加锁时插入一条记录,解锁是删除这条记录。下面我们就简要实现一下基于数据库的分布式锁。

id字段是数据库的自增id,unique_mutex字段就是我们的防重id,也就是加锁的对象,此对象唯一。在这张表上我们加了一个唯一索引,保证unique_mutex唯一性。holder_id代表竞争到锁的持有者id。

如果当前sql执行成功代表加锁成功,如果抛出唯一索引异常(DuplicatedKeyException)则代表加锁失败,当前锁已经被其他竞争者获取。

解锁很简单,直接删除此条记录即可。

是否可重入 :就以上的方案来说,我们实现的分布式锁是不可重入的,即是是同一个竞争者,在获取锁后未释放锁之前再来加锁,一样会加锁失败,因此是不可重入的。解决不可重入问题也很简单:加锁时判断记录中是否存在unique_mutex的记录,如果存在且holder_id和当前竞争者id相同,则加锁成功。这样就可以解决不可重入问题。

锁释放时机 :设想如果一个竞争者获取锁时候,进程挂了,此时distributed_lock表中的这条记录就会一直存在,其他竞争者无法加锁。为了解决这个问题,每次加锁之前我们先判断已经存在的记录的创建时间和当前系统时间之间的差是否已经超过超时时间,如果已经超过则先删除这条记录,再插入新的记录。另外在解锁时,必须是锁的持有者来解锁,其他竞争者无法解锁。这点可以通过holder_id字段来判定。

数据库单点问题 :单个数据库容易产生单点问题:如果数据库挂了,我们的锁服务就挂了。对于这个问题,可以考虑实现数据库的高可用方案,例如MySQL的MHA高可用解决方案。

使用Jedis来和Redis通信。

可以看到,我们加锁就一行代码:

jedis.set(String key, String value, String nxxx, String expx, int time);

这个set()方法一共五个形参:

第一个为key,我们使用key来当锁,因为key是唯一的。

第二个为value,这里写的是锁竞争者的id,在解锁时,我们需要判断当前解锁的竞争者id是否为锁持有者。

第三个为nxxx,这个参数我们填的是NX,意思是SET IF NOT EXIST,即当key不存在时,我们进行set操作;若key已经存在,则不做任何操作。

第四个为expx,这个参数我们传的是PX,意思是我们要给这个key加一个过期时间的设置,具体时间由第五个参数决定;

第五个参数为time,与第四个参数相呼应,代表key的过期时间。

总的来说,执行上面的set()方法就只会导致两种结果:1.当前没有锁(key不存在),那么久进行加锁操作,并对锁设置一个有效期,同时value表示加锁的客户端。2.已经有锁存在,不做任何操作。

上述解锁请求中, SET_IF_NOT_EXIST (不存在则执行)保证了加锁请求的排他性,缓存超时机制保证了即使一个竞争者加锁之后挂了,也不会产生死锁问题:超时之后其他竞争者依然可以获取锁。通过设置value为竞争者的id,保证了只有锁的持有者才能来解锁,否则任何竞争者都能解锁,那岂不是乱套了。

解锁的步骤:

注意到这里解锁其实是分为2个步骤,涉及到解锁操作的一个原子性操作问题。这也是为什么我们解锁的时候用Lua脚本来实现,因为Lua脚本可以保证操作的原子性。那么这里为什么需要保证这两个步骤的操作是原子操作呢?

设想:假设当前锁的持有者是竞争者1,竞争者1来解锁,成功执行第1步,判断自己就是锁持有者,这是还未执行第2步。这是锁过期了,然后竞争者2对这个key进行了加锁。加锁完成后,竞争者1又来执行第2步,此时错误产生了:竞争者1解锁了不属于自己持有的锁。可能会有人问为什么竞争者1执行完第1步之后突然停止了呢?这个问题其实很好回答,例如竞争者1所在的JVM发生了GC停顿,导致竞争者1的线程停顿。这样的情况发生的概率很低,但是请记住即使只有万分之一的概率,在线上环境中完全可能发生。因此必须保证这两个步骤的操作是原子操作。

是否可重入 :以上实现的锁是不可重入的,如果需要实现可重入,在 SET_IF_NOT_EXIST 之后,再判断key对应的value是否为当前竞争者id,如果是返回加锁成功,否则失败。

锁释放时机 :加锁时我们设置了key的超时,当超时后,如果还未解锁,则自动删除key达到解锁的目的。如果一个竞争者获取锁之后挂了,我们的锁服务最多也就在超时时间的这段时间之内不可用。

Redis单点问题 :如果需要保证锁服务的高可用,可以对Redis做高可用方案:Redis集群+主从切换。目前都有比较成熟的解决方案。

利用Zookeeper创建临时有序节点来实现分布式锁:

其基本思想类似于AQS中的等待队列,将请求排队处理。其流程图如下:

解决不可重入 :客户端加锁时将主机和线程信息写入锁中,下一次再来加锁时直接和序列最小的节点对比,如果相同,则加锁成功,锁重入。

锁释放时机 :由于我们创建的节点是顺序临时节点,当客户端获取锁成功之后突然session会话断开,ZK会自动删除这个临时节点。

单点问题 :ZK是集群部署的,主要一半以上的机器存活,就可以保证服务可用性。

Zookeeper第三方客户端curator中已经实现了基于Zookeeper的分布式锁。利用curator加锁和解锁的代码如下:

多线程高并发之Synchronized锁及其膨胀

在并发编程中,synchronized锁因其使用简单,在线程间同步被广泛应用。下面对其原理及锁升级过程进行探究。

当实例方法被synchronized修饰时,通过当前实例调用此方法的所有线程共用一把锁,不同对象调用此方法线程间互不影响。

当使用synchronized锁修饰实例方法,锁添加在当前类的实例上,有多少个实例可添加多少把锁。

修饰代码块比修饰方法颗粒度更小。当实例方法代码块被synchronized修饰时,通过当前实例调用此方法的所有线程共用一把锁,不同对象调用此方法线程间互不影响。

当使用synchronized锁修饰代码块,锁添加在当前类的实例上,有多少个实例可添加多少把锁。

当静态方法被synchronized修饰时,整个JVM所有调用此方法的线程均受同一个锁的约束。

当使用synchronized锁修饰静态方法,锁添加在当前类的类对象上,最多添加一把锁。

Java 8所使用的synchronized锁是经过优化后的,存在偏向锁、轻量级锁、重量级锁等状态。

线程间不存在锁的竞争行为,至多只有一个线程有获取锁的需求,常见场景为单线程程序。

判断是不是偏向锁的标识是查看调用此方法的线程是否有且仅有一个。

在多线程编程里,被锁修饰的方法仅被单一线程调用几乎不存在,因此偏向锁比较鸡肋:如果能够明确单一线程调用目标方法,使用无锁编程更为合适。

无锁与偏向锁的性能差异非常接近,几乎可以忽略不计。

线程间存在锁的伪竞争行为,即同一时刻绝对不会存在两个线程申请获取锁,各线程尽管都有使用锁的需求,但是是交替使用锁。

当有两个及以上线程调用被锁修饰的方法时,那么至少能确定是轻量级锁。

轻量级锁由于同一时刻不存在两个线程互相竞争锁,因此不存在线程阻塞-唤醒的上下文切换,因此性能相对重量级锁要高很多。

线程间存在锁的实质性竞争行为,线程间都有获取锁的需求,但是时间不可交错,互斥锁的阻塞等待。

当能够肯定至少有两个及以上线程调用被锁修饰的方法时,线程调用方法是随机的,那么大概率是重量级锁。

重量级锁由于涉及到线程阻塞-唤醒的上下文切换,造成相比较与无锁状态,效率低很多。

synchronized锁是非公平锁,没有FIFO队列机制保障竞争锁的线程一定有几率获得锁。

synchronized锁是可重入锁,可重入意味着嵌套调用不会产生死锁问题。

synchronized锁是一种悲观锁,通过加锁实现线程间同步。

在多线程环境下,如果使用synchronized锁,那么大概率会升级到重量级锁。偏向锁和轻量级锁非刻意为之,很难存在,更大的意义是对比帮助理解重量级锁的性能。

重量级锁尽管会对性能产生很大影响,但是依旧是解决线程间同步的有效手段。

当被锁修饰的方法或者代码块执行时间较长时,选用基于线程阻塞-唤醒切换上下文的方式进行线程同步效率相对较高。

当被锁修饰的方法或者代码块执行时间较短时,应选用其它替代锁,比如自旋锁等。

在实际多线程场景开发中,synchronized锁大概率会升级到重量级锁,因其单向升级的特点,重量级状态的synchronized锁可能会对实际业务的并发产生不利影响,手动选用其它锁可能会更合适。

synchronized锁仅可用于解决同一进程内不同线程间同步,对于分布式项目跨进城线程同步依赖于分布式锁,synchronized锁更多的意义是理解锁的过程。

本文来自投稿,不代表【】观点,发布者:【

本文地址: ,如若转载,请注明出处!

举报投诉邮箱:253000106@qq.com

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2024年3月24日 08:58:11
下一篇 2024年3月24日 09:07:54

相关推荐

  • 深入java虚拟机pdf,深入java虚拟机 中村成洋 pdf

    在linux环境下,java怎么实现从word格式转换为pdf格式 //设置当前使用的打印机,我的Adobe Distiller打印机名字为 Adobe PDF wordCom.setProperty( ActivePrinter , new Variant( Adobe PDF ));//设置printout的参数,将word文档打印为postscript…

    2024年5月23日
    4600
  • java截取指定长度字符串,java截取指定字符串之后的

    java中如何截取字符串中的指定一部分 第一个参数是开始截取的字符位置。(从0开始)第二个参数是结束字符的位置+1。(从0开始)indexof函数的作用是查找该字符串中的某个字的位置,并且返回。 int end);截取s中从begin开始至end结束时的字符串,并将其赋值给s;split讲解:java.lang.string.split split 方法 将…

    2024年5月23日
    4400
  • java绑定一个端口,java使用端口

    java如何多个service共用一个端口 你如果有多个项目的话,你可以把多个项目放到一个tomcat里面,这样端口相同使用项目名称来进行区分项目。你如果非要使用同一个,你也可以配置不同的域名导向不同的项目。就是访问的域名不同转接到的项目不同。 如果需要同时启动多个程序,要么修改tomcat的配置文件中的监听端口。要么修改jar包程序的监听端口。不能在一台服…

    2024年5月23日
    3600
  • java多线程并发编程基础,Java多线程并发执行返回

    电脑培训分享Java并发编程:核心理论 电脑培训发现本系列会从线程间协调的方式(wait、notify、notifyAll)、Synchronized及Volatile的本质入手,详细解释JDK为我们提供的每种并发工具和底层实现机制。 人们开始意识到了继承的众多缺点,开始努力用聚合代替继承。软件工程解决扩展性的重要原则就是抽象描述,直接使用的工具就是接口。接…

    2024年5月23日
    4800
  • 自学java找工作,自学java找工作需要包装简历吗

    自学java学多久可以找到工作 1、自学Java至少需要一年以上的时间才能达到找工作的水平。报班培训四到六个月的时间就可以找到一份不错的工作。 2、自学Java至少需要一年以上的时间才能达到找工作的水平。 3、如果要想找到一份Java相关的工作,需要至少学习5-6个月时间才能就业。Java开发需要掌握一些基础的编程语言知识,比如掌握面向对象的编程思想、基本的…

    2024年5月23日
    4400
  • java左移右移,java 左移

    java位移问题 1、思路:直接用Integer类的bit运算操作。 2、移位操作:左移:向左移位,符号后面的数字是移了多少位,移的位用0补齐,例如2进制数01111111左移一位后变为11111110,移位是字节操作。 3、Java 位运算 Java 位运算[转]一,Java 位运算表示方法: 在Java语言中,二进制数使用补码表示,最高位为符号位,正数的…

    2024年5月23日
    4300
  • java技术规范,java规范性要求

    现在主流的JAVA技术是什么? java最流行开发技术程序员必看 1 、Git Git一直是世界上最受欢迎的Java工具之一,也是Java开发人员最杰出的工具之一。Git是一个开源工具,是-种出色的分布式版本控制解决方案。 (1).Java基础语法、数组、类与对象、继承与多态、异常、范型、集合、流与文件、反射、枚举、自动装箱和注解。(2).Java面向对象编…

    2024年5月23日
    4100
  • javasocket编程,Java socket编程中,禁用nagle算法的参数

    Java进行并发多连接socket编程 1、Java可利用ServerSocket类对外部客户端提供多个socket接口。基本的做法是先创建一个ServerSocket实例,并绑定一个指定的端口,然后在这个实例上调用accept()方法等待客户端的连接请求。 2、Socket socket=server.accept(0;Thread handleThrea…

    2024年5月23日
    4700
  • java死亡,java死代码是什么意思

    我的世界传送回死亡点指令是什么? 1、下面就让我们一起来了解一下吧:我的世界回到死的地方的指令是输入/back,就可以回到死亡地点了,当然也可以看信标,因为死亡后会有一道光集中在死亡点,只要循着光就可以找到目的地了。 2、在服务器中的指令 首先打开指令台,在指令行输入“/back”就可以回到自己的死亡地点了。在单人游戏中的指令 在单人游戏中,您无法直接返回到…

    2024年5月23日
    4900
  • myeclipse能部署java工程么,myeclipse支持jdk18

    myeclipse如何建java文件 1、点击【File】—【New】–【Class】在如下界面,输入Class的名字,如Test,点击【Finish】。Test.java文件创建成功。 2、点击【File】—【New】–【Class】 在如下界面,输入Class的名字,如Test,点击【Finish】。 Te…

    2024年5月23日
    4100

发表回复

登录后才能评论



关注微信