使用多线程就可能会存在线程安全的问题。很多 java 程序员对写多线程都很挣扎,或者仅仅理解那些是线程安全的代码,那些不是。这篇文章我并不是详述线程安全,详述同步机制的文章,相反我只是用一个简单的非线程安全的代码例子引领大家,然后重点是去理解什么是线程安全,以及怎样使代码变得线程安全。

好了,下面我们先来看一个非线程安全的代码,可以尝试找找为什么非线程安全?

/*
* 非线程安全类
*/  
public class Counter {
private int count;
 
public int getCount(){
       return count++;
   }
}

大家应该能看得出来,上面代码是非线程安全的,因为 ++ 操作不是原子操作,读取、更新、写回的时候可能会出问题。

比如现在有多个线程同时访问 getCount() 方法,那么这些线程可能同时操作 count ,部分结果出现重叠。例如,Thread 1 正在更新 count,Thread 2 读取 count,但是仍然是获取到旧的值,那么最后Thread 2 就会覆盖掉 Thead 1 的更新,所以并发环境下我们要考虑 ++ 操作的原子性。

在 Java 编程中,有许多方式去编写线程安全的代码:

1)、使用 synchronized 关键字,synchonized 可以 lock  getCount() 方法,某个时刻只有一个线程可以执行它,也就实现了线程安全

2)、使用原子的 Integer,原子的 Integer 可以保证 ++ 操作的原子性

好了,我们了看看线程安全版本的 Counter

public class Counter {
    private int count;
    AtomicInteger atomicCount = new AtomicInteger( 0 );
  
   /*
*1、synchronized 所以线程安全
*/
    public synchronized int getCount(){
        return count++;
    }
  
    /*
     * 2、原子增长的操作,所以线程安全
     */
    public int getCountAtomically(){
        return atomicCount.incrementAndGet();
    }
}
 
java 线程安全比较重要的点:
 
在 java 编程中,记住这些关键点可以帮你避免一些严重的并发问题,比如条件竞争或死锁。
1)、不可变对象默认是线程安全的,因为他们一旦被创建就不会被修改。比如 String 是不可变对象,是线程安全的。
 
2)、只读、final 类型的变量也是线程安全的
 
3)、锁也是一种线程安全的方式
 
4) 、static 变量,如果没有被恰当的使用同步,也会引发线程安全问题
 
5)、使用线程安全的类: Vector, Hashtable, ConcurrentHashMap, String etc.
 
6)、原子操作是线程安全的 reading a 32 bit int from memory because its an atomic operation it can't interleave with other thread.
 
7) 、本地变量也是线程安全的,因为每个线程都有自己的变量 copy.使用本地变量是一种保证代码线程安全的好方法。(ThreadLocal)
 
8)、 多线程之间的共享对象尽可能的少,也就尽可能的避免线程安全的问题
 
9) 、Volatile 关键字
 
 
好了,以上就是所有的关于编写线程安全类或代码和避免并发问题的要点。老实说,线程安全是有点难掌握的概念,你需要去考虑并发,进而看代码是否线程安全。
当然 jvm 也可以重排序代码,实现自己调优。但是相同的代码在开发环境正常不一定能保证在线上也正常。因为 jvm 会去自我调优,重排序等操作去优化代码,这些也可能会生成线程安全的问题。
 

怎样去写线程安全的代码(Java)的更多相关文章

  1. 如何写出无法维护的代码(JAVA版)

    程序命名(针对那些不能混淆的代码) 容易输入的名字.比如:Fred,asdf 单字母的变量名.比如:a,b,c, x,y,z,或者干脆上中文比如(阿隆索肯德基) 有创意地拼写错误.比如:SetPint ...

  2. Java线程运行轨迹-代码追踪与定位

    今天在写程序时,想到一个问题,当我的程序出异常的时候,控制台输出信息中,明确指出了出现异常的位置,并详细列举了函数的调用层次关系,它是怎么做到的. 竟然想到了这个问题,就去查看了源代码,不过没点几下, ...

  3. Java认证:JavaRunnable线程编写接口代码

    Java认证:JavaRunnable线程编写接口代码.JavaRunnable线程如何才能更好的适应目前的编程环境呢?下面我们就看看如何才能更好的进行相关环境.希望下面的文章对大家有所帮助.Java ...

  4. 手写代码 - java.util.Arrays 相关

    1-拷贝一个范围内的数组 Arrays.copyOfRange( array, startIndex, endIndex); include startIndex... exclude endInde ...

  5. 【读书笔记】《写给大忙人看的Java SE 8》——Java8新特性总结

    虽然看过一些Java 8新特性的资料,但是平时很少用到,时间长了就忘了,正好借着Java 9的发布,来总结下一些Java 8中的新特性. 接口中的默认方法和静态方法 先考虑一个问题,如何向Java中的 ...

  6. 《写给大忙人看的Java SE 8》——Java8新特性总结

    阅读目录 接口中的默认方法和静态方法 函数式接口和Lambda表达式 Stream API 新的日期和时间 API 杂项改进 参考资料 回到顶部 接口中的默认方法和静态方法 先考虑一个问题,如何向Ja ...

  7. 学习 Doug Lea 大神写的——Scalable IO in Java

    学习 Doug Lea 大神写的--Scalable IO in Java 网络服务 Web services.分布式对象等等都具有相同的处理结构 Read request Decode reques ...

  8. 自己动手写线程池——向JDK线程池进发

    自己动手写线程池--向JDK线程池进发 前言 在前面的文章自己动手写乞丐版线程池中,我们写了一个非常简单的线程池实现,这个只是一个非常简单的实现,在本篇文章当中我们将要实现一个和JDK内部实现的线程池 ...

  9. 分页 工具类 前后台代码 Java JavaScript (ajax) 实现 讲解

    [博客园cnblogs笔者m-yb原创, 转载请加本文博客链接,笔者github: https://github.com/mayangbo666,公众号aandb7,QQ群927113708]http ...

随机推荐

  1. Liunx Mkdir

    linux mkdir命令: 创建目录 介绍:该命令创建指定的目录名,要求创建目录的用户在当前目录中具有写权限,并且指定的目录名不能是当前目录中已有的目录1语法: mkdir [-m] [-p] 目录 ...

  2. Java 8.9 游戏:井字游戏(C++&Java)

    思路框架同8.20 : C++: #include<iostream> #include<string> using namespace std; class Chess { ...

  3. kali安全工具

    http://www.kali.org.cn/ Kali linux下载安装 (27) kali linux是backtrack的最新代号,或者叫新版本的backtrack,欢迎下载使用. 908 / ...

  4. 说说为什么会有ssl.CertificateError报错

    有一些网站没有获取浏览器的颁发的安全证书 当你在请求这个网站时浏览器会当做不安全网站处理 因此会报ssl.CertificateError 解决办法是 :将默认的证书验证模式修改为不需要验证 代码如下 ...

  5. ark是什么

    https://github.com/QuadHex/ARK/releases 0.8.0版本网络使用evpp 调用 https://www.jianshu.com/p/9f09928829ba

  6. rapidjson 的封装学习

    #pragma once #include "Util.h" #include "rapidjson/writer.h" #include "rapi ...

  7. DNA计算机及DNA存储

    傅里叶变换到量子水平,可编程元素到原子分子核能,都可以极大的改变有机体(高级有机体都是有寿命的,例如人类),如果可以出现机械体,核能提供能量:并结合类似高级生物大脑的有机体大脑,不断学习进化,甚至优化 ...

  8. 验证签名机制——java示例

    简单的验证公钥私钥签名认证: 公钥是对外公开的部分,私钥是不公开的部分,一般在项目开发中公钥是给用户,私钥是存于服务器上,二者中有一个加密,则需要另外一个来解密. 下面是java实现的一个比较简单的示 ...

  9. rpm安装jdk7

    原文:http://www.centoscn.com/image-text/config/2015/0208/4658.html 系统环境:centos-6.5 安装方式:rpm安装 软件:jdk-7 ...

  10. 再读c++primer plus 003

    1.如果函数返回一个结构而不是一个指向结构的引用,将把整个结构复制到一个临时位置,再将这个拷贝复制给dup.但在返回值为引用时,将直接复制给变量,其效率更高. 2.返回引用时最重要的一点是,应避免返回 ...