引言:线程同步(高并发)情景下,怎样撰写线程安全(Thread-Safety)的程序流程,针对程序流程的恰当和平稳运作有关键的实际意义。下边将融合实例,谈一谈怎样在Java语言中,完成线程安全的程序流程。

文中共享自华为云服务小区《Java如何实现多线程场景下的线程安全》,创作者: jackwangcumt 。

1 前言

当今伴随着计算机系统的迅速发展趋势,个人计算机上的CPU也是多核的,如今广泛的CUP核数全是4核或是8核的。因而,在程序编写时,必须为了更好地提高工作效率,充分运用硬件配置的工作能力,则必须撰写并行处理的程序流程。Java语言做为互联网技术运用的关键语言表达,广泛运用于公司应用软件的开发设计中,它也是适用线程同步(Multithreading)的,但线程同步虽好,却对程序流程的撰写有较高的规定。

单核能够恰当运作的程序流程不意味着在线程同步情景下可以恰当运作,这儿的准确性通常不易被发觉,它会在并发数做到一定量的情况下才很有可能发生。这也是在检测阶段不易再现的缘故。因而,线程同步(高并发)情景下,怎样撰写线程安全(Thread-Safety)的程序流程,针对程序流程的恰当和平稳运作有关键的实际意义。下边将融合实例,谈一谈怎样在Java语言中,完成线程安全的程序流程。

为了更好地得出理性的了解,下边得出一个进程不安全的实例,实际以下:

package com.example.learn;
public class Counter {
    private static int counter = 0;
    public static int getCount(){
        return counter;
    }
    public static  void add(){
        counter = counter   1;
    }
}

这一类有一个静态数据的特性counter,用以记数。在其中能够根据静态方法add()对counter开展加1实际操作,还可以根据getCount()方式 获得到当今的记数counter值。如果是单核状况下,这一程序流程是没有问题的,例如循环系统10次,那麼最终获得的记数counter值为10。但线程同步状况下,那麼这一結果就不一定可以恰当获得,很有可能相当于10,也很有可能低于10,例如9。下边得出一个线程同步检测的实例:

package com.example.learn;
public class MyThread extends Thread{
    private String name ;
    public MyThread(String name){
        this.name = name ;
    }
    public void run(){
        Counter.add();
        System.out.println("Thead[" this.name "] Count is "   Counter.getCount());
    }
}
///////////////////////////////////////////////////////////
package com.example.learn;
public class Test01 {
    public static void main(String[] args) {
        for(int i=0;i<5000;i  ){
            MyThread mt1 = new MyThread("TCount" i);
            mt1.start();
        }
    }
}

这儿为了更好地再现记数的难题,线程数调至较为大,这儿是5000。运作此实例,则輸出很有可能結果以下:

Thead[TCount5] Count is 4
Thead[TCount2] Count is 9
Thead[TCount4] Count is 4
Thead[TCount14] Count is 10
..................................
Thead[TCount4911] Count is 4997
Thead[TCount4835] Count is 4998
Thead[TCount4962] Count is 4999

留意:线程同步情景下,进程不安全的程序流程輸出結果具备可变性。

2 synchronized方式

根据以上的实例,让其变为线程安全的程序流程,最立即的便是在相匹配的方式 上加上synchronized关键词,让其变成 同歩的方式 。它能够装饰一个类,一个方式 和一个代码块。对以上记数程序流程开展改动,编码以下:

package com.example.learn;
public class Counter {
    private static int counter = 0;
    public static int getCount(){
        return counter;
    }
    public static synchronized void add(){
        counter = counter   1;
    }
}

再度运作程序流程,则輸出結果以下:

......
Thead[TCount1953] Count is 4998
Thead[TCount3087] Count is 4999
Thead[TCount2425] Count is 5000

3 上锁体制

此外一种普遍的同歩方式 便是上锁,例如Java中有一种再入锁ReentrantLock,它是一种递归算法无堵塞的同歩体制,相对性于synchronized而言,它能够给予更为强劲和灵便的锁体制,与此同时能够降低死锁产生的几率。实例编码以下:

package com.example.learn;
import java.util.concurrent.locks.ReentrantLock;
public class Counter {
    private  static int counter = 0;
    private static final ReentrantLock lock = new ReentrantLock(true);
    public static int getCount(){
        return counter;
    }
    public static  void add(){
        lock.lock();
        try {
            counter = counter   1;
        } finally {
            lock.unlock();
        }
    }
}

再度运作程序流程,则輸出結果以下:

......
Thead[TCount1953] Count is 4998
Thead[TCount3087] Count is 4999
Thead[TCount2425] Count is 5000

留意:Java中还给予了读写锁ReentrantReadWriteLock,那样能够开展读写分离,高效率高些。

4 应用Atomic目标

因为锁体制会危害一定的特性,而有一些情景下,能够根据无锁方法开展完成。Java内嵌了Atomic有关原子操作类,例如AtomicInteger, AtomicLong, AtomicBoolean和AtomicReference,能够依据不一样的情景开展挑选。下边得出实例编码:

package com.example.learn;
import java.util.concurrent.atomic.AtomicInteger;
public class Counter {
    private static final AtomicInteger counter = new AtomicInteger();
    public static int getCount(){
        return counter.get();
    }
    public static void add(){
        counter.incrementAndGet();
    }
}

再度运作程序流程,则輸出結果以下:

......
Thead[TCount1953] Count is 4998
Thead[TCount3087] Count is 4999
Thead[TCount2425] Count is 5000

5 无状态目标

前边提及,进程不安全的一个缘故便是好几个进程与此同时浏览某一目标中的数据信息,数据信息存有共享资源的状况,因而,假如将数据信息变为私有的,即无状态(stateless)得话,那麼当然便是线程安全的。而说白了的无状态的方式 ,便是给一样的键入,就能回到一致的結果。下边得出实例编码:

package com.example.learn;
public class Counter {
    public static int sum (int n) {
        int ret = 0;
        for (int i = 1; i <= n; i  ) {
            ret  = i;
        }
        return ret;
    }
}

6 不能变目标

前边提及,假如必须在线程同步中共享资源一个数据信息,而这一数据信息给出值,就不可以更改,那麼也是线程安全的,等同于写保护的特性。在Java中能够根据final关键词开展特性装饰。下边得出实例编码:

package com.example.learn;
public class Counter {
    public final int count ;
    public Counter (int n) {
        count = n;
    }
}

7 汇总

前边提及了几类线程安全的方式 ,整体的观念要不便是根据锁体制完成同歩,要不便是避免信息共享,避免在好几个进程中对数据信息开展存取数据。此外,有一些文章内容上说到,能够在自变量前应用volatile装饰,来完成同歩体制,但这一历经检测是不一定的,有一些情景下,volatile依然不可以确保线程安全。尽管以上是线程安全的经验交流,可是或是必须根据严苛的检测开展认证,实践是检验真知的唯一标准。

 

加关注,第一时间掌握华为云服务新鮮技术性~

评论(0条)

刀客源码 游客评论