精华内容
下载资源
问答
  • golang并发知识点总结1. go申明 goroutine,也可以申明... go申明的并发访问方法,要考虑多个协程同时执行对公共资源的占用读写,如磁盘读写,网络访问,数据库访问等,下面例子体现并发访问导致的“事务不一致”...

    golang并发知识点总结

    1. go申明 goroutine,也可以申明匿名函数,就表示开启多协程并发访问

    2. go多个goroutine通信,使用channel,必须用 info:=make(chan string) 格式定义,即用make关键字

    3. go申明的并发访问方法,要考虑多个协程同时执行对公共资源的占用和读写,如磁盘读写,网络访问,数据库访问等,下面例子体现并发访问导致的“事务不一致”问题,以及解决方法

    4. golang 加锁机制非常轻量简洁 。

    go 共享锁定义 mutex := &sync.Mutex{} 传递锁的指针,要注意多个协程要用一个共享锁,传入不同的锁,不能达到加锁的功能。

    297dd4cafef9c727ea6df36709bfd890.png

    实例看懂并发和锁

    const (openLock = true)func main2() {gfile.PutContents("content.txt", "")concurrency()chuan()}func chuan() {gproc.StartTime()//串行访问十次 非并发,每次协程id不变for i := 0; i < 10; i++ {info := goMethod1("串行", nil)glog.Debug(info)}glog.Info("串行访问十次结束,耗时:", gproc.Uptime())}func concurrency() {//取锁地址mutex := &sync.Mutex{}gproc.StartTime()//并发访问十次//info:=make(chan string)for i := 0; i < 10; i++ {//传递共享锁指针go goMethod1("并行", mutex)}glog.Info("并发访问十次结束,耗时:", gproc.Uptime())}//这里的锁是来自上层主线程的共享锁func goMethod1(mtype string, mutex *sync.Mutex) string {if mutex != nil && openLock {mutex.Lock()defer mutex.Unlock()}info := mtype + "访问协程id:" + (strconv.FormatUint(GetGoroutineID(), 10)) + ""gutil.TryCatch(func() {//故意写入两次体验锁机制保证每一个协程事务一致,锁机制gfile.PutContentsAppend("content.txt", info)gfile.PutContentsAppend("content.txt", info)}, func(exception interface{}) {glog.Error(exception)})return info}func GetGoroutineID() uint64 {b := make([]byte, 64)runtime.Stack(b, false)b = bytes.TrimPrefix(b, []byte("goroutine "))b = b[:bytes.IndexByte(b, ' ')]n, _ := strconv.ParseUint(string(b), 10, 64)return n}
    99f2ba0dc9fa0f9509286eba9b34ea2d.png

    不加锁文件内容

    5784da3c80e7b9b20a60011d6ec53b6b.png

    加锁以后文件内容

    结果分析

    上面代码执行两种结果如图所示。

    不加锁文件内容:方法写入的两句话没法连续写入文件。

    加锁以后文件内容:方法写入的两句话连续写入文件。

    有锁,就可以保障方法对共享资源的“事务一致性”。

    展开全文
  • 我们在使用 Redis 时,不可避免地会遇到并发访问的问题,比如说如果多个用户同时下单,就会对缓存在 Redis 中的商品库存并发更新。一旦有了并发写操作,数据就会被修改,如果我们没有对并发写请求做好控制,就可能...

    我们在使用 Redis 时,不可避免地会遇到并发访问的问题,比如说如果多个用户同时下单,就会对缓存在 Redis 中的商品库存并发更新。一旦有了并发写操作,数据就会被修改,如果我们没有对并发写请求做好控制,就可能导致数据被改错,影响到业务的正常使用(例如库存数据错误,导致下单异常)。

    为了保证并发访问的正确性,Redis 提供了两种方法,分别是加锁和原子操作。

    看上去好像是一种很好的方案,但是,其实这里会有两个问题:一个是,如果加锁操作多,会降低系统的并发访问性能;第二个是,Redis 客户端要加锁时,需要用到分布式锁,而分布式锁实现复杂,需要用额外的存储系统来提供加解锁操作

    原子操作是另一种提供并发访问控制的方法。原子操作是指执行过程保持原子性的操作,而且原子操作执行时并不需要再加锁,实现了无锁操作。这样一来,既能保证并发控制,还能减少对系统并发性能的影响。

    并发访问中需要对什么进行控制?

    我们说的并发访问控制,是指对多个客户端访问操作同一份数据的过程进行控制,以保证任何一个客户端发送的操作在 Redis 实例上执行时具有互斥性。例如,客户端 A 的访问操作在执行时,客户端 B 的操作不能执行,需要等到 A 的操作结束后,才能执行。

    并发访问控制对应的操作主要是数据修改操作。当客户端需要修改数据时,基本流程分成两步:

    1. 客户端先把数据读取到本地,在本地进行修改;
    2. 客户端修改完数据后,再写回 Redis。

    我们把这个流程叫做“读取 - 修改 - 写回”操作(Read-Modify-Write,简称为 RMW 操作)。当有多个客户端对同一份数据执行 RMW 操作的话,我们就需要让 RMW 操作涉及的代码以原子性方式执行。访问同一份数据的 RMW 操作代码,就叫做临界区代码。

    我们先看下临界区代码。假设客户端要对商品库存执行扣减 1 的操作,伪代码如下所示:

    
    current = GET(id)
    current--
    SET(id, current)

    可以看到,客户端首先会根据商品 id,从 Redis 中读取商品当前的库存值 current(对应 Read),然后,客户端对库存值减 1(对应 Modify),再把库存值写回 Redis(对应 Write)。当有多个客户端执行这段代码时,这就是一份临界区代码。

    如果我们对临界区代码的执行没有控制机制,就会出现数据更新错误。在刚才的例子中,假设现在有两个客户端 A 和 B,同时执行刚才的临界区代码,就会出现错误,你可以看下下面这张图。

    e413fb4131ba6dca69e47d2605091ab8.png

    可以看到,客户端 A 在 t1 时读取库存值 10 并扣减 1,在 t2 时,客户端 A 还没有把扣减后的库存值 9 写回 Redis,而在此时,客户端 B 读到库存值 10,也扣减了 1,B 记录的库存值也为 9 了。等到 t3 时,A 往 Redis 写回了库存值 9,而到 t4 时,B 也写回了库存值 9。

    如果按正确的逻辑处理,客户端 A 和 B 对库存值各做了一次扣减,库存值应该为 8。所以,这里的库存值明显更新错了。

    出现这个现象的原因是,临界区代码中的客户端读取数据、更新数据、再写回数据涉及了三个操作,而这三个操作在执行时并不具有互斥性,多个客户端基于相同的初始值进行修改,而不是基于前一个客户端修改后的值再修改。

    为了保证数据并发修改的正确性,我们可以用锁把并行操作变成串行操作,串行操作就具有互斥性。一个客户端持有锁后,其他客户端只能等到锁释放,才能拿锁再进行修改。

    下面的伪代码显示了使用锁来控制临界区代码的执行情况,你可以看下。

    
    LOCK()
    current = GET(id)
    current--
    SET(id, current)
    UNLOCK()

    虽然加锁保证了互斥性,但是加锁也会导致系统并发性能降低。

    如下图所示,当客户端 A 加锁执行操作时,客户端 B、C 就需要等待。A 释放锁后,假设 B 拿到锁,那么 C 还需要继续等待,所以,t1 时段内只有 A 能访问共享数据,t2 时段内只有 B 能访问共享数据,系统的并发性能当然就下降了。

    400316909e187d08d7e175e0f284bd29.png

    和加锁类似,原子操作也能实现并发控制,但是原子操作对系统并发性能的影响较小,接下来,我们就来了解下 Redis 中的原子操作。

    Redis 的两种原子操作方法

    为了实现并发控制要求的临界区代码互斥执行,Redis 的原子操作采用了两种方法:

    1. 把多个操作在 Redis 中实现成一个操作,也就是单命令操作;
    2. 把多个操作写到一个 Lua 脚本中,以原子性方式执行单个 Lua 脚本。

    我们先来看下 Redis 本身的单命令操作。

    Redis 是使用单线程来串行处理客户端的请求操作命令的,所以,当 Redis 执行某个命令操作时,其他命令是无法执行的,这相当于命令操作是互斥执行的。当然,Redis 的快照生成、AOF 重写这些操作,可以使用后台线程或者是子进程执行,也就是和主线程的操作并行执行。不过,这些操作只是读取数据,不会修改数据,所以,我们并不需要对它们做并发控制。

    可能也注意到了,虽然 Redis 的单个命令操作可以原子性地执行,但是在实际应用中,数据修改时可能包含多个操作,至少包括读数据、数据增减、写回数据三个操作,这显然就不是单个命令操作了,那该怎么办呢?

    别担心,Redis 提供了 INCR/DECR 命令,把这三个操作转变为一个原子操作了。INCR/DECR 命令可以对数据进行增值 / 减值操作,而且它们本身就是单个命令操作,Redis 在执行它们时,本身就具有互斥性。

    比如说,在刚才的库存扣减例子中,客户端可以使用下面的代码,直接完成对商品 id 的库存值减 1 操作。即使有多个客户端执行下面的代码,也不用担心出现库存值扣减错误的问题。

    
    DECR id 

    所以,如果我们执行的 RMW 操作是对数据进行增减值的话,Redis 提供的原子操作 INCR 和 DECR 可以直接帮助我们进行并发控制。但是,如果我们要执行的操作不是简单地增减数据,而是有更加复杂的判断逻辑或者是其他操作,那么,Redis 的单命令操作已经无法保证多个操作的互斥执行了。

    所以,这个时候,我们需要使用第二个方法,也就是 Lua 脚本。Redis 会把整个 Lua 脚本作为一个整体执行,在执行的过程中不会被其他命令打断,从而保证了 Lua 脚本中操作的原子性。如果我们有多个操作要执行,但是又无法用 INCR/DECR 这种命令操作来实现,就可以把这些要执行的操作编写到一个 Lua 脚本中。然后,我们可以使用 Redis 的 EVAL 命令来执行脚本。这样一来,这些操作在执行时就具有了互斥性。

    举个例子,来具体解释下 Lua 的使用。

    当一个业务应用的访问用户增加时,我们有时需要限制某个客户端在一定时间范围内的访问次数,比如爆款商品的购买限流、社交网络中的每分钟点赞次数限制等。那该怎么限制呢?

    我们可以把客户端 IP 作为 key,把客户端的访问次数作为 value,保存到 Redis 中。客户端每访问一次后,我们就用 INCR 增加访问次数。

    不过,在这种场景下,客户端限流其实同时包含了对访问次数和时间范围的限制,例如每分钟的访问次数不能超过 20。所以,我们可以在客户端第一次访问时,给对应键值对设置过期时间,例如设置为 60s 后过期。同时,在客户端每次访问时,我们读取客户端当前的访问次数,如果次数超过阈值,就报错,限制客户端再次访问。你可以看下下面的这段代码,它实现了对客户端每分钟访问次数不超过 20 次的限制。

    
    //获取ip对应的访问次数
    current = GET(ip)
    //如果超过访问次数超过20次,则报错
    IF current != NULL AND current > 20 THEN
        ERROR "exceed 20 accesses per second"
    ELSE
        //如果访问次数不足20次,增加一次访问计数
        value = INCR(ip)
        //如果是第一次访问,将键值对的过期时间设置为60s后
        IF value == 1 THEN
            EXPIRE(ip,60)
        END
        //执行其他操作
        DO THINGS
    END

    可以看到,在这个例子中,我们已经使用了 INCR 来原子性地增加计数。但是,客户端限流的逻辑不只有计数,还包括访问次数判断和过期时间设置。

    对于这些操作,我们同样需要保证它们的原子性。否则,如果客户端使用多线程访问,访问次数初始值为 0,第一个线程执行了 INCR(ip) 操作后,第二个线程紧接着也执行了 INCR(ip),此时,ip 对应的访问次数就被增加到了 2,我们就无法再对这个 ip 设置过期时间了。这样就会导致,这个 ip 对应的客户端访问次数达到 20 次之后,就无法再进行访问了。即使过了 60s,也不能再继续访问,显然不符合业务要求。

    所以,这个例子中的操作无法用 Redis 单个命令来实现,此时,我们就可以使用 Lua 脚本来保证并发控制。我们可以把访问次数加 1、判断访问次数是否为 1,以及设置过期时间这三个操作写入一个 Lua 脚本,如下所示:

    
    local current
    current = redis.call("incr",KEYS[1])
    if tonumber(current) == 1 then
        redis.call("expire",KEYS[1],60)
    end

    假设我们编写的脚本名称为 lua.script,我们接着就可以使用 Redis 客户端,带上 eval 选项,来执行该脚本。脚本所需的参数将通过以下命令中的 keys 和 args 进行传递。

    
    redis-cli  --eval lua.script  keys , args

    这样一来,访问次数加 1、判断访问次数是否为 1,以及设置过期时间这三个操作就可以原子性地执行了。即使客户端有多个线程同时执行这个脚本,Redis 也会依次串行执行脚本代码,避免了并发操作带来的数据错误。

    小结

    在并发访问时,并发的 RMW 操作会导致数据错误,所以需要进行并发控制。

    所谓并发控制,就是要保证临界区代码的互斥执行。Redis 提供了两种原子操作的方法来实现并发控制,分别是单命令操作和 Lua 脚本

    因为原子操作本身不会对太多的资源限制访问,可以维持较高的系统并发性能。但是,单命令原子操作的适用范围较小,并不是所有的 RMW 操作都能转变成单命令的原子操作(例如 INCR/DECR 命令只能在读取数据后做原子增减),当我们需要对读取的数据做更多判断,或者是我们对数据的修改不是简单的增减时,单命令操作就不适用了。

    而 Redis 的 Lua 脚本可以包含多个操作,这些操作都会以原子性的方式执行,绕开了单命令操作的限制。不过,如果把很多操作都放在 Lua 脚本中原子执行,会导致 Redis 执行脚本的时间增加,同样也会降低 Redis 的并发性能。

    所以,一个小建议:在编写 Lua 脚本时,你要避免把不需要做并发控制的操作写入脚本中。当然,加锁也能实现临界区代码的互斥执行,只是如果有多个客户端加锁时,就需要分布式锁的支持了。

    展开全文
  • 并发模型分布式系统很相似并发模型其实分布式系统模型非常相似,在并发模型中是线程彼此进行通信,而在分布式系统模型中是 进程 彼此进行通信。然而本质上,进程线程也非常相似。这也就是为什么并发模型...

    本篇文章我们来探讨一下并发设计模型。

    可以使用不同的并发模型来实现并发系统,并发模型说的是系统中的线程如何协作完成并发任务。不同的并发模型以不同的方式拆分任务,线程可以以不同的方式进行通信和协作。

    并发模型和分布式系统很相似

    并发模型其实和分布式系统模型非常相似,在并发模型中是线程彼此进行通信,而在分布式系统模型中是 进程 彼此进行通信。然而本质上,进程和线程也非常相似。这也就是为什么并发模型和分布式模型非常相似的原因。

    分布式系统通常要比并发系统面临更多的挑战和问题比如进程通信、网络可能出现异常,或者远程机器挂掉等等。但是一个并发模型同样面临着比如 CPU 故障、网卡出现问题、硬盘出现问题等。

    因为并发模型和分布式模型很相似,因此他们可以相互借鉴,例如用于线程分配的模型就类似于分布式系统环境中的负载均衡模型。

    其实说白了,分布式模型的思想就是借鉴并发模型的基础上推演发展来的。

    认识两个状态

    并发模型的一个重要的方面是,线程是否应该共享状态,是具有共享状态还是独立状态。共享状态也就意味着在不同线程之间共享某些状态

    状态其实就是数据,比如一个或者多个对象。当线程要共享数据时,就会造成 竞态条件 或者 死锁 等问题。当然,这些问题只是可能会出现,具体实现方式取决于你是否安全的使用和访问共享对象。

    c803a7b0b438081cc0f97eb2170d8488.png

    独立的状态表明状态不会在多个线程之间共享,如果线程之间需要通信的话,他们可以访问不可变的对象来实现,这是一种最有效的避免并发问题的一种方式,如下图所示

    9f57c21fb806ced3101177bcd6aa108f.png

    使用独立状态让我们的设计更加简单,因为只有一个线程能够访问对象,即使交换对象,也是不可变的对象。

    并发模型

    并行 Worker

    第一个并发模型是并行 worker 模型,客户端会把任务交给 代理人(Delegator),然后由代理人把工作分配给不同的 工人(worker)。如下图所示

    6529d601d5e83f0f9a980bd7f95fa53a.png

    并行 worker 的核心思想是,它主要有两个进程即代理人和工人,Delegator 负责接收来自客户端的任务并把任务下发,交给具体的 Worker 进行处理,Worker 处理完成后把结果返回给 Delegator,在 Delegator 接收到 Worker 处理的结果后对其进行汇总,然后交给客户端。

    并行 Worker 模型是 Java 并发模型中非常常见的一种模型。许多 java.util.concurrent 包下的并发工具都使用了这种模型。

    并行 Worker 的优点

    并行 Worker 模型的一个非常明显的特点就是很容易理解,为了提高系统的并行度你可以增加多个 Worker 完成任务。

    并行 Worker 模型的另外一个好处就是,它会将一个任务拆分成多个小任务,并发执行,Delegator 在接受到 Worker 的处理结果后就会返回给 Client,整个 Worker -> Delegator -> Client 的过程是异步的。

    并行 Worker 的缺点

    同样的,并行 Worker 模式同样会有一些隐藏的缺点

    共享状态会变得很复杂

    实际的并行 Worker 要比我们图中画出的更复杂,主要是并行 Worker 通常会访问内存或共享数据库中的某些共享数据。

    b6a6e5867628ad78f1d029d8dba241ed.png

    这些共享状态可能会使用一些工作队列来保存业务数据、数据缓存、数据库的连接池等。在线程通信中,线程需要确保共享状态是否能够让其他线程共享,而不是仅仅停留在 CPU 缓存中让自己可用,当然这些都是程序员在设计时就需要考虑的问题。线程需要避免 竞态条件,死锁 和许多其他共享状态造成的并发问题。

    多线程在访问共享数据时,会丢失并发性,因为操作系统要保证只有一个线程能够访问数据,这会导致共享数据的争用和抢占。未抢占到资源的线程会 阻塞。

    现代的非阻塞并发算法可以减少争用提高性能,但是非阻塞算法比较难以实现。

    可持久化的数据结构(Persistent data structures) 是另外一个选择。可持久化的数据结构在修改后始终会保留先前版本。因此,如果多个线程同时修改一个可持久化的数据结构,并且一个线程对其进行了修改,则修改的线程会获得对新数据结构的引用。

    虽然可持久化的数据结构是一个新的解决方法,但是这种方法实行起来却有一些问题,比如,一个持久列表会将新元素添加到列表的开头,并返回所添加的新元素的引用,但是其他线程仍然只持有列表中先前的第一个元素的引用,他们看不到新添加的元素。

    持久化的数据结构比如 链表(LinkedList) 在硬件性能上表现不佳。列表中的每个元素都是一个对象,这些对象散布在计算机内存中。现代 CPU 的顺序访问往往要快的多,因此使用数组等顺序访问的数据结构则能够获得更高的性能。CPU 高速缓存可以将一个大的矩阵块加载到高速缓存中,并让 CPU 在加载后直接访问 CPU 高速缓存中的数据。对于链表,将元素分散在整个 RAM 上,这实际上是不可能的。

    无状态的 worker

    共享状态可以由其他线程所修改,因此,worker 必须在每次操作共享状态时重新读取,以确保在副本上能够正确工作。不在线程内部保持状态的 worker 成为无状态的 worker。

    作业顺序是不确定的

    并行工作模型的另一个缺点是作业的顺序不确定,无法保证首先执行或最后执行哪些作业。任务 A 在任务 B 之前分配给 worker,但是任务 B 可能在任务 A 之前执行。

    流水线

    第二种并发模型就是我们经常在生产车间遇到的 流水线并发模型,下面是流水线设计模型的流程图

    7f9b312ee0c48408a46b82c6a16f7369.png

    这种组织架构就像是工厂中装配线中的 worker,每个 worker 只完成全部工作的一部分,完成一部分后,worker 会将工作转发给下一个 worker。

    每道程序都在自己的线程中运行,彼此之间不会共享状态,这种模型也被称为无共享并发模型。

    使用流水线并发模型通常被设计为非阻塞I/O,也就是说,当没有给 worker 分配任务时,worker 会做其他工作。非阻塞I/O 意味着当 worker 开始 I/O 操作,例如从网络中读取文件,worker 不会等待 I/O 调用完成。因为 I/O 操作很慢,所以等待 I/O 非常耗费时间。在等待 I/O 的同时,CPU 可以做其他事情,I/O 操作完成后的结果将传递给下一个 worker。下面是非阻塞 I/O 的流程图

    e10156b73a4545f2629918d358863c8e.png

    在实际情况中,任务通常不会按着一条装配线流动,由于大多数程序需要做很多事情,因此需要根据完成的不同工作在不同的 worker 之间流动,如下图所示

    392f9af71517ac51fb69570abe77cb34.png

    任务还可能需要多个 worker 共同参与完成

    a5b04eef6982b49fe82ef0ce96090cee.png

    响应式 - 事件驱动系统

    使用流水线模型的系统有时也被称为 响应式 或者 事件驱动系统,这种模型会根据外部的事件作出响应,事件可能是某个 HTTP 请求或者某个文件完成加载到内存中。

    Actor 模型

    在 Actor 模型中,每一个 Actor 其实就是一个 Worker, 每一个 Actor 都能够处理任务。

    简单来说,Actor 模型是一个并发模型,它定义了一系列系统组件应该如何动作和交互的通用规则,最著名的使用这套规则的编程语言是 Erlang。一个参与者Actor对接收到的消息做出响应,然后可以创建出更多的 Actor 或发送更多的消息,同时准备接收下一条消息。

    dcd1abdd0dd8eefb4def55b87ca1668c.png

    Channels 模型

    在 Channel 模型中,worker 通常不会直接通信,与此相对的,他们通常将事件发送到不同的 通道(Channel)上,然后其他 worker 可以在这些通道上获取消息,下面是 Channel 的模型图

    3b7ddaf38bbd69fc718f65c1fabe7d69.png

    有的时候 worker 不需要明确知道接下来的 worker 是谁,他们只需要将作者写入通道中,监听 Channel 的 worker 可以订阅或者取消订阅,这种方式降低了 worker 和 worker 之间的耦合性。

    流水线设计的优点

    与并行设计模型相比,流水线模型具有一些优势,具体优势如下

    不会存在共享状态

    因为流水线设计能够保证 worker 在处理完成后再传递给下一个 worker,所以 worker 与 worker 之间不需要共享任何状态,也就不用无需考虑以为并发而引起的并发问题。你甚至可以在实现上把每个 worker 看成是单线程的一种。

    有状态 worker

    因为 worker 知道没有其他线程修改自身的数据,所以流水线设计中的 worker 是有状态的,有状态的意思是他们可以将需要操作的数据保留在内存中,有状态通常比无状态更快。

    更好的硬件整合

    因为你可以把流水线看成是单线程的,而单线程的工作优势在于它能够和硬件的工作方式相同。因为有状态的 worker 通常在 CPU 中缓存数据,这样可以更快地访问缓存的数据。

    使任务更加有效的进行

    可以对流水线并发模型中的任务进行排序,一般用来日志的写入和恢复。

    流水线设计的缺点

    流水线并发模型的缺点是任务会涉及多个 worker,因此可能会分散在项目代码的多个类中。因此很难确定每个 worker 都在执行哪个任务。流水线的代码编写也比较困难,设计许多嵌套回调处理程序的代码通常被称为 回调地狱。回调地狱很难追踪 debug。

    函数性并行

    函数性并行模型是最近才提出的一种并发模型,它的基本思路是使用函数调用来实现。消息的传递就相当于是函数的调用。传递给函数的参数都会被拷贝,因此在函数之外的任何实体都无法操纵函数内的数据。这使得函数执行类似于原子操作。每个函数调用都可以独立于任何其他函数调用执行。

    当每个函数调用独立执行时,每个函数都可以在单独的 CPU 上执行。这也就是说,函数式并行并行相当于是各个 CPU 单独执行各自的任务。

    JDK 1.7 中的 ForkAndJoinPool 类就实现了函数性并行的功能。Java 8 提出了 stream 的概念,使用并行流也能够实现大量集合的迭代。

    函数性并行的难点是要知道函数的调用流程以及哪些 CPU 执行了哪些函数,跨 CPU 函数调用会带来额外的开销。

    你好,我是 cxuan,我自己手写了四本 PDF,分别是 Java基础总结、HTTP 核心总结、计算机基础知识,操作系统核心总结,我已经整理成为 PDF,可以关注公众号 Java建设者 回复 PDF 领取优质资料。

    200c9b12efeebee0306ff4bf6579177b.png
    展开全文
  • java 并发包提供的加锁模式分为独占锁共享锁。 独占锁 独占锁模式下,每次只能有一个线程能持有锁,ReentrantLock 就是以独占方式实现的... 共享锁 共享锁则允许多个线程同时获取锁,并发访问 共享资源,如:Re...
    60e7a702e303f8718c5d06c98a5a6355.png

    java 并发包提供的加锁模式分为独占锁和共享锁。

    独占锁

    独占锁模式下,每次只能有一个线程能持有锁,ReentrantLock 就是以独占方式实现的互斥锁。 独占锁是一种悲观保守的加锁策略,它避免了读/读冲突,如果某个只读线程获取锁,则其他读线 程都只能等待,这种情况下就限制了不必要的并发性,因为读操作并不会影响数据的一致性。

    共享锁

    共享锁则允许多个线程同时获取锁,并发访问 共享资源,如:ReadWriteLock。共享锁则是一种 乐观锁,它放宽了加锁策略,允许多个执行读操作的线程同时访问共享资源。

    1. AQS 的内部类 Node 定义了两个常量 SHARED 和 EXCLUSIVE,他们分别标识 AQS 队列中等 待线程的锁获取模式。

    2. java 的并发包中提供了 ReadWriteLock,读-写锁。它允许一个资源可以被多个读操作访问, 或者被一个 写操作访问,但两者不能同时进行。

    展开全文
  • 并发模拟多个用户同时访问所写接口,用于测试接口所需要的的并发接口的最多承受用户量,适用于一些简单的压力测试。
  • Fayson在前面的文章《0553-6.1.0-如何使用Java代码同时访问安全非安全CDH集群》《0554-6.1.0-同一java进程中同时访问认证非认证集群的问题(续)》,本篇文档主要介绍如何使用Python并发访问认证的集群非...
  • 但是很多时候,多线程程序是需要同时访问同一个对象,或者变量的。这样,一个对象同时被多个线程访问,会出现处理的结果预期不一致的可能。因此,需要了解如何对对象及变量并发访问,写出线程安全的程序,所谓线程...
  • web并发访问执行

    2013-12-09 10:52:43
    并发访问就是同时有多个请求请求同一服务。比如我你现在都同时在请求百度的服务器提供搜索。大量的并发访问如果超出了服务器的承受能力的话,轻则导致服务器抛弃一部分请求,重则导致服务器资源耗尽,当机。有一种...
  • java多线程(对象变量的并发访问)

    万次阅读 2016-07-11 09:37:42
    在现实开发中,我们写的线程肯定会有不同的实例在执行,此时就可能会出现”非线程安全问题”,非线程安全就是:多个线程对同一个对象中的实例变量进行并发访问时候,有可能AB线程同时读取到数据,先后进行更改,...
  • 数据库连接数:某个数据库,同时接受最大的访问数,直接受制于技术版权; eg:以MySql为例,“max_connections”表示数据库服务器可接受的同时最大访问量;“max_used_connections”表示数据库服务器分配给每个...
  • 6.1.0-如何使用Java代码同时访问安全非安全CDH集群》《0554-6.1.0-同一java进程中同时访问认证非认证集群的问题(续)》,本篇文档主要介绍如何使用Python并发访问认证的集群非认证的集群。测试环境:CDH6.1....
  • 10.3.10 限制资源的并发访问 有时可能需要允许多个工作线程同时访问一个资源,但要限制总数。例如,连接池支持同时连接,但数目可能是固定的,或者一个网络应用可能支持固定数目的并发下载。这些连接就可以使用...
  • 当大量“客户”同时访问一个“服务器”时,“服务器”面临的是大并发访问的技术架构问题。大并发访问的等级可以分为“超出连接”、“超出时限”“超出应用负载”三种。   2.1超出连接 当客户的连接超出...
  • 对象及变量并发访问

    2017-05-09 16:23:56
    但是很多时候,多线程程序是需要同时访问同一个对象,或者变量的。这样,一个对象同时被多个线程访问,会出现处理的结果预期不一致的可能。因此,需要了解如何对对象及变量并发访问,写出线程安全的程序,所谓线程...
  • 云服务器吧本文关于服务器1M带宽的测试,忽略了云服务器的计算处理时间,仅考虑网页从服务器下载到用户本地的速度。 服务器1M带宽的下载速度是128KB/S(参考:云服务器1M带宽下载速度),不是1M/秒。网站类型不同...
  • ab-n100-c10http://www.xxx.com参数说明:-n:表示发送的请求数总量-c:表示并发请求数可以调整-c参数来模拟不同的并发量,可以得到不同的测试结果哦。可以看一下得到的结果的例子,重点关注 Requests pe...
  • 并发访问(一)

    2014-06-16 15:20:39
    HubbleDotNet 推出一年多来,得到了大量用户的支持使用,其中有些用户的搜索访问量较大,在使用过程中有时会因为同时并发访问过大,出现 Too many connects on server 这个错误,为了缓解这个问题,HubbleDotNet ...
  • Nginx 是我们常用的负载均衡反向代理服务器,并发性能非常优秀。但是在并发量极大的情况下,必要限流措施还是需要的,Nginx 的有对应的模块插件可通过简单配置来完成这个功能。限制并发限制ip并发数,也是说限制同...
  • 2.变量的并发访问

    2020-06-07 23:44:55
    在上一篇文章中我们已经讨论了线程具有异步运行的特性,因此当多线程同时访问同一个实例变量时就会引发脏读的问题。而这显然不是我们愿意看到的,解决办法也很简单,就是给访问该变量的程序部分加锁。多线程并发在...
  • 悲观锁解决高并发访问问题

    千次阅读 热门讨论 2016-08-14 21:49:24
    如果程序存在并发访问问题,我们就要考虑通过加锁对一些资源的访问进行控制,加锁的两种方式为悲观锁乐观锁,学习drp的时候,老师为我们展示的悲观锁的例子,这篇博客就来看一下这种锁的机制。  在分销管理...
  • 10.4.14 控制资源的并发访问 有时可能需要允许多个工作进程同时访问一个资源,但要限制总数。例如,连接池支持同时连接,但数目可能是固定的,或者一个网络应用可能支持固定数目的并发下载。这些连接就可以使用...
  • Android多线程并发访问网络

    千次阅读 2016-10-19 13:57:30
    先说说新的测试要求吧,要同时有不同的多个用户一起访问网络把心率传入到服务器端,那么Android怎么利用多线程来并发访问网络呢?这个时候我们应该想到多线程的两种方法,继承Thread实现Runnable接口。这里我用...
  • 解决数据库高并发访问瓶颈问题 一、缓存式的Web应用程序架构:  在Web层db层之间加一层cache层,主要目的:减少数据库读取负担,提高数据读取速度。cache存取的媒介是内存,可以考虑采用分布式的cache层,这样...
  • 但是很多时候,多线程程序是需要同时访问同一个对象,或者变量的。这样,一个对象同时被多个线程访问,会出现处理的结果预期不一致的可能。因此,需要了解如何对对象及变量并发访问,写出线程安全的程序,所谓线程...
  • 在打开关闭数据时通过计数的方式防止重复打开重复关闭,保证多个线程同时访问时只打开关闭数据库一次。 代码 private synchronized SQLiteDatabase getWritableDatabase() { if (dbCounter.incrementAndGe...
  • 方式一:多线程组指定TPS同时并发示例:接口1、接口2、接口3……1、新建线程组1) 右键测试计划,依次选择 添加-Threads-线程组2) 点击线程组,输入并发数3) 勾选循环次数:永远4) 勾选调度器:输入执行时间2、新建...
  • Nginx 是我们常用的负载均衡反向代理服务器,并发性能非常优秀。但是在并发量极大的情况下,必要限流措施还是需要的,Nginx 的有对应的模块插件可通过简单配置来完成这个功能。 限制并发 限制ip并发数,也是...
  • PHP高并发大流量的解决方案一 高并发的概念在互联网时代,并发,高并发通常是指并发访问。也就是在某个时间点,有多少个访问同时到来。二 高并发架构相关概念1、QPS (每秒查询率) : 每秒钟请求或者查询的数量,在...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 5,070
精华内容 2,028
关键字:

并发访问和同时访问