当我们查看JDK API的时候,总会发现一些类说明写着,线程安全或者线程不安全,比如说到StringBuilder中,有这么一句,“将StringBuilder 的实例用于多个线程是不安全的。如果需要这样的同步,则建议使用StringBuffer。”,提到StringBuffer时,说到“StringBuffer是线程安全的可变字符序列,一个类似于String的字符串缓冲区,虽然在任意时间点上它都包含某种特定的字符序列,但通过某些方法调用可以改变该序列的长度和内容。可将字符串缓冲区安全地用于多个线程。可以在必要时对这些方法进行同步,因此任意特定实例上的所有操作就好像是以串行顺序发生的,该顺序与所涉及的每个线程进行的方法调用顺序一致”。StringBuilder是一个可变的字符序列,此类提供一个与StringBuffe兼容的API,但不保证同步。该类被设计用作StringBuffer的一个简易替换,用在字符串缓冲区被单个线程使用的时候(这种情况很普遍)。如果可能,建议优先采用该类,因为在大多数实现中,它比StringBuffer要快。将StringBuilder的实例用于多个线程是不安全的,如果需要这样的同步,则建议使用StringBuffer。

   根据以上JDK文档中对StringBuffer和StringBuilder的描述,得到对String、StringBuilder与StringBuffer三者使用情况的总结:

   1、如果要操作少量的数据用String

   2、单线程操作字符串缓冲区下操作大量数据StringBuilder

   3、多线程操作字符串缓冲区下操作大量数据StringBuffer

   那么下面手动创建一个线程不安全的类,然后在多线程中使用这个类,看看有什么效果。

public class Count {  

private int num;

public void count() {

for(int i = 1; i <= 10; i++) {

num += i;

}

System.out.println(Thread.currentThread().getName() + "-" + num);

}

}

   在这个类中的count方法计算1一直加到10的和,并输出当前线程名和总和,我们期望的是每个线程都会输出55。

public class ThreadTest {  

public static void main(String[] args) {

Runnable runnable = new Runnable() {

Count count = new Count();

public void run() {

count.count();

}

};

for(int i = 0; i < 10; i++) {

new Thread(runnable).start();

}

}

}

   这里启动了10个线程,看一下输出结果:

Thread-0-55  

Thread-1-110

Thread-2-165

Thread-4-220

Thread-5-275

Thread-6-330

Thread-3-385

Thread-7-440

Thread-8-495

Thread-9-550

   只有Thread-0线程输出的结果是我们期望的,而输出的是每次都累加的,要想得到我们期望的结果,有几种解决方案:

   1、将Count类中的成员变量num变成count方法的局部变量;

public class Count {  

public void count() {

int num = 0;

for(int i = 1; i <= 10; i++) {

num += i;

}

System.out.println(Thread.currentThread().getName() + ”-“ + num);

}

}

   2、将线程类成员变量拿到run方法中,这时count引用是线程内的局部变量;

public class ThreadTest4 {  

public static void main(String[] args) {

Runnable runnable = new Runnable() {

public void run() {

Count count = new Count();

count.count();

}

};

for(int i = 0; i < 10; i++) {

new Thread(runnable).start();

}

}

}

   3、每次启动一个线程使用不同的线程类,不推荐。

   通过上述测试,我们发现,存在成员变量的类用于多线程时是不安全的,不安全体现在这个成员变量可能发生非原子性的操作,而变量定义在方法内也就是局部变量是线程安全的。想想在使用struts1时,不推荐创建成员变量,因为action是单例的,如果创建了成员变量,就会存在线程不安全的隐患,而struts2是每一次请求都会创建一个action,就不用考虑线程安全的问题。所以,日常开发中,通常需要考虑成员变量或者说全局变量在多线程环境下,是否会引发一些问题。

   要说明线程同步问题首先要说明Java线程的两个特性,可见性和有序性。

   多个线程之间是不能直接传递数据进行交互的,它们之间的交互只能通过共享变量来实现。拿上面的例子来说明,在多个线程之间共享了Count类的一个实例,这个对象是被创建在主内存(堆内存)中,每个线程都有自己的工作内存(线程栈),工作内存存储了主内存count对象的一个副本,当线程操作count对象时,首先从主内存复制count对象到工作内存中,然后执行代码count.count(),改变了num值,最后用工作内存中的count刷新主内存的 count。当一个对象在多个工作内存中都存在副本时,如果一个工作内存刷新了主内存中的共享变量,其它线程也应该能够看到被修改后的值,此为可见性。

   多个线程执行时,CPU对线程的调度是随机的,我们不知道当前程序被执行到哪步就切换到了下一个线程,一个最经典的例子就是银行汇款问题,一个银行账户存款100,这时一个人从该账户取10元,同时另一个人向该账户汇10元,那么余额应该还是100。那么此时可能发生这种情况,A线程负责取款,B线程负责汇款,A从主内存读到100,B从主内存读到100,A执行减10操作,并将数据刷新到主内存,这时主内存数据100-10=90,而B内存执行加10操作,并将数据刷新到主内存,最后主内存数据100+10=110,显然这是一个严重的问题,我们要保证A线程和B线程有序执行,先取款后汇款或者先汇款后取款,此为有序性。

对Java线程安全与不安全的理解的更多相关文章

  1. java线程安全与不安全的理解

    存在成员变量(全局变量)的类用于多线程时是不安全的,不安全体现在这个成员变量可能发生非原子性的操作,而变量定义在方法内也就是局部变量是线程安全的. 想想在使用struts1时,不推荐创建成员变量,因为 ...

  2. Java线程并发:知识点

    Java线程并发:知识点   发布:一个对象是使它能够被当前范围之外的代码所引用: 常见形式:将对象的的引用存储到公共静态域:非私有方法中返回引用:发布内部类实例,包含引用.   逃逸:在对象尚未准备 ...

  3. Java线程的概念

    1.      计算机系统 使用高速缓存来作为内存与处理器之间的缓冲,将运算需要用到的数据复制到缓存中,让计算能快速进行:当运算结束后再从缓存同步回内存之中,这样处理器就无需等待缓慢的内存读写了. 缓 ...

  4. Java 线程池框架核心代码分析--转

    原文地址:http://www.codeceo.com/article/java-thread-pool-kernal.html 前言 多线程编程中,为每个任务分配一个线程是不现实的,线程创建的开销和 ...

  5. 细说进程五种状态的生老病死——双胞胎兄弟Java线程

    java线程的五种状态其实要真正高清,只需要明白计算机操作系统中进程的知识,原理都是相同的. 系统根据PCB结构中的状态值控制进程. 单CPU系统中,任一时刻处于执行状态的进程只有一个. 进程的五种状 ...

  6. 【转载】 Java线程面试题 Top 50

    Java线程面试题 Top 50 不管你是新程序员还是老手,你一定在面试中遇到过有关线程的问题.Java语言一个重要的特点就是内置了对并发的支持,让Java大受企业和程序员 的欢迎.大多数待遇丰厚的J ...

  7. 第24章 java线程(3)-线程的生命周期

    java线程(3)-线程的生命周期 1.两种生命周期流转图 ** 生命周期:**一个事物冲从出生的那一刻开始到最终死亡中间的过程 在事物的漫长的生命周期过程中,总会经历不同的状态(婴儿状态/青少年状态 ...

  8. 第23章 java线程通信——生产者/消费者模型案例

    第23章 java线程通信--生产者/消费者模型案例 1.案例: package com.rocco; /** * 生产者消费者问题,涉及到几个类 * 第一,这个问题本身就是一个类,即主类 * 第二, ...

  9. 第22章 java线程(2)-线程同步

    java线程(2)-线程同步 本节主要是在前面吃苹果的基础上发现问题,然后提出三种解决方式 1.线程不安全问题 什么叫线程不安全呢 即当多线程并发访问同一个资源对象的时候,可能出现不安全的问题 对于前 ...

随机推荐

  1. 浅谈搜狐云景PAAS平台

    前言: 搜狐云景作为搜狐的paas平台,在2014年5月22日的云计算大会上正式公布了公測.初測,注冊用户必须先申请邀请码參与公測会赠送用户100元电子券,经过实名认证之后会再赠送100电子券.目測能 ...

  2. hdoj--2063--过山车(最大匹配)

    过山车 Time Limit: 1000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submis ...

  3. c# xml操作总结

    一前言 先来了解下操作XML所涉及到的几个类及之间的关系  如果大家发现少写了一些常用的方法,麻烦在评论中指出,我一定会补上的!谢谢大家 * 1 XMLElement 主要是针对节点的一些属性进行操作 ...

  4. if,elif,else的关系 input print int的用法

    qian=input("找劳保网是什么网站?:")if qian=="zhaolaobaowang.com": print("正确")els ...

  5. codeforces 708ALetter Cyclic Shift

    2019-05-18 09:51:19 加油,加油,fightting !!! https://www.cnblogs.com/ECJTUACM-873284962/p/6375011.html 全为 ...

  6. vmware笔试题目

    http://discuss.acmcoder.com/topic/58db8e2ebb0f44ba0e94e670 上面是完整的题目,下面一下我自己的想法. http://discuss.acmco ...

  7. 利用jquery的ajaxPrefilter阻止重复发送请求

    利用jquery的ajaxPrefilter阻止重复发送请求 (function ($) { var pendingRequests = {}; // 所有ajax请求的通用前置filter $.aj ...

  8. 集成Bmob推送

    Write By lz:  转发请注明原文地址: http://www.cnblogs.com/lizhilin2016/p/6952217.html Lz 寄语: Bmob 奇葩推送, 大坑, 想要 ...

  9. 在YII2中使用redis

    一.安装YII2的redis扩展 composer require --prefer-dist yiisoft/yii2-redis 二. 配置basic/config/web.php 在compon ...

  10. Ext未定义问题解决

    做的项目用到EXT.NET,调试时候没问题,发布到IIS上出现EXT未定义,把项目的应用程序池改为Classic 模式就可以了.