前言:进程同歩(分布式系统)场景下,如何撰写进程安全性(Thread-Safety)的程序流程步骤,对于程序流程的正确和稳定运行有重要的现实意义。下面将结合实例,谈一谈如何在Java语言表达中,进行线程安全的程序流程。

原文中共享资源华为公司云服务器住宅小区《Java怎样完成线程同步情景下的线程安全》,原创者: jackwangcumt 。

1 序言

现如今随着着电子计算机系统软件的快速发展趋向,pc机上的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仍然不能保证 线程安全。虽然之上是线程安全的经验总结,但是或者务必依据苛刻的检验进行验证,实践是检验真谛的唯一标准。

 

加关心,第一時间把握华为云服务新鲜专业性~