Java线程之程安全与不安全代码示例

作为一个Java web开发人员,很少也不需要去处理线程,因为服务器已经帮我们处理好了。记得大一刚学Java的时候,老师带着我们做了一个局域网聊天室,用到了AWT、Socket、多线程、I/O,编写的客户端和服务器,当时做出来很兴奋,回学校给同学们演示,感觉自己好NB,呵呵,扯远了。上次在百度开发者大会上看到一个提示语,自己写的代码,6个月不看也是别人的代码,自己学的知识也同样如此,学完的知识如果不使用或者不常常回顾,那么还不是自己的知识。大学零零散散搞了不到四年的Java,我相信很多人都跟我一样,JavaSE基础没打牢,就急忙忙、兴冲冲的搞JavaEE了,然后学习一下前台开发(html、css、javascript),有可能还搞搞jquery、extjs,再然后是Struts、hibernate、spring,然后听说找工作得会linux、oracle,又去学,在这个过程中,是否迷失了,虽然学习面很广,但就像《神雕侠侣》中黄药师评价杨过,博而不精、杂而不纯,这一串下来,感觉做Java开发好难,并不是学着难,而是知识面太广了,又要精通这个,又要精通那个,这只是我迷茫时候的想法,现在我已经找到方向了。

回归正题,当我们查看JDK API的时候,总会发现一些类说明写着,线程安全或者线程不安全,比如说StringBuilder中,有这么一句,“将StringBuilder 的实例用于多个线程是不安全的。如果需要这样的同步,则建议使用StringBuffer。 ”,那么下面手动创建一个线程不安全的类,然后在多线程中使用这个类,看看有什么效果。

Count.java:

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。

ThreadTest.java:

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线程之程安全与不安全代码示例的全部内容,希望对大家有所帮助。感兴趣的朋友可以继续参阅本站:java多线程中断代码详解、创建并运行一个java线程方法介绍、Java多线程编程实现socket通信示例代码等,有什么问题可以随时留言,小编会及时回复大家的。感谢朋友们对本站的支持!

(0)

相关推荐

  • Java实现转跳不同系统使用枚举加switch的方式示例

    因有个判断需要处理不同系统类型跳转不同系统.考虑用switch + 枚举的方式. 具体使用案例如下: package com.b2b.common.constant; import com.base.utils.base.StringUtils; /** * 系统类型枚举 * @author shijing */ public enum SystemType { ERP(0,"ERP"), ORDER_PLATFORM(1,"订货平台"), PERSONAL(2,&

  • 扒一扒 Java 中的枚举类型

    前言 在 Java 中, 枚举, 也称为枚举类型, 其是一种特殊的数据类型, 它使得变量能够称为一组预定义的常量. 其目的是强制编译时类型安全. 枚举类更加直观,类型安全.使用常量会有以下几个缺陷: 1. 类型不安全.若一个方法中要求传入季节这个参数,用常量的话,形参就是int类型,开发者传入任意类型的int类型值就行,但是如果是枚举类型的话,就只能传入枚举类中包含的对象. 2. 没有命名空间.开发者要在命名的时候以SEASON_开头,这样另外一个开发者再看这段代码的时候,才知道这四个常量分别代

  • Java多线程之CAS算法实现线程安全

    前言 对于线程安全,我们有说不尽的话题.大多数保证线程安全的方法是添加各种类型锁,使用各种同步机制,用限制对共享的.可变的类变量并发访问的方式来保证线程安全.文本从另一个角度,使用"比较交换算法"(CompareAndSwap)实现同样的需求.我们实现一个简单的"栈",并逐步重构代码来进行讲解. 本文通俗易懂,不会涉及到过多的底层知识,适合初学者阅读(言外之意是各位大神可以绕道了). 旅程开始 1.先定个小目标,实现一个"栈" "栈&q

  • java枚举是如何保证线程安全的

    前言 写在前面:Java SE5提供了一种新的类型-Java的枚举类型,关键字enum可以将一组具名的值的有限集合创建为一种新的类型,而这些具名的值可以作为常规的程序组件使用,这是一种非常有用的功能.本文将深入分析枚举的源码,看一看枚举是怎么实现的,他是如何保证线程安全的,以及为什么用枚举实现的单例是最好的方式. 枚举是如何保证线程安全的 要想看源码,首先得有一个类吧,那么枚举类型到底是什么类呢?是enum吗?答案很明显不是,enum就和class一样,只是一个关键字,他并不是一个类,那么枚举是

  • Java中对于双属性枚举的使用案例

    最近有小伙伴问我,双枚举类该怎么写,还得包括根据key取值方法. 于是就手写一个案例如下: /** * 关系类型枚举 */ public enum RelationType { MAPPING(0,"映射"), QUOTE(1,"引用/授权"), ENTRUST(2,"委托"), AGENT(3,"代理"); private int value; private String desc; RelationType(int va

  • 深入学习java枚举的应用

    一.枚举和静态常量区别 讲到枚举我们首先思考,它和public static final String 修饰的常量有什么不同. 我举枚举的两个优点: 1. 保证了类型安全:调用者无法随意传一个 int或者String 等值: 2.代码可读性非常高: 举个例子: 在实际编程中,往往存在着这样的"数据集",它们的数值在程序中是稳定的,而且"数据集"中的元素是有限的.例如春夏秋冬四个数据元素组成了四季的"数据集". 你写了方法get(String se

  • 优雅地在Java应用中实现全局枚举处理的方法

    背景描述 为了表达某一个属性,具备一组可选的范围,我们一般会采用两种方式.枚举类和数据字典,两者具有各自的优点.枚举类写在Java代码中,方便编写相应的判断逻辑,代码可读性高,枚举类中的属性是可提前预估和确定的.数据字典,一般保存在数据库,不便于编写判断和分支逻辑,因为数据如果有所变动,那么对应的代码逻辑很有可能失效,强依赖数据库数据的正确性,数据字典中对应的属性对业务影响并不大,日常开发中常用做分类,打标签使用,属性的多少无法估计. 目前基本上没有一个很好的全局处理枚举类的方案,所以我就自己综

  • 浅析对java枚举类型的认识

    而想弄明白枚举类型是什么,就要把他和类进行对比了.用ecplise创建一个类,你要使用这个类就得new一个对象出来对吧(当然了,别较真,说我用他的静态属性和方法):而当你用ecplise创建一个枚举类型时,在使用时是不需要再new的,它本身就创建好了几个对象在其内部,这也就是枚举和类的最大区别. 首先,先创建一个枚举,看一下它到底是什么东西. 我们给这个枚举对象加入两个对象(red和green),一个属性,一个构造方法,还有setget方法,这样一个简单的枚举类型就创建好了. 说说枚举的用处:一

  • java 中枚举类enum的values()方法的详解

    java 中枚举类enum的values()方法的详解 前言: 关于枚举,相信使用的已经很普遍了,现在主要写的是枚举中的一个特殊方法,values(), 为什么说特殊呢,因为在Enum 的 API 文档中也找不到这个方法.接下来就看看具体的使用. 理论上此方法可以将枚举类转变为一个枚举类型的数组,因为枚举中没有下标,我们没有办法通过下标来快速找到需要的枚举类,这时候,转变为数组之后,我们就可以通过数组的下标,来找到我们需要的枚举类.接下来就展示代码了. 首先是我们自己的枚举类. public e

  • Java多线程模拟售票程序和线程安全问题

    Java中线程部分知识中,售票程序非常经典.程序中也有一些问题存在! 需求:模拟3个窗口同时在售100张票. 问题1:为什么100张票被卖出了300张票? 原因:因为tickets是非静态的,非静态的成员变量数据是在每个对象中都会维护一份数据的,三个线程对象就会有三份. 解决方案:把tickets票数共享出来给三个线程对象使用.使用static修饰. 问题2: 出现了线程安全问题 ? 线程安全问题的解决方案:sun提供了线程同步机制让我们解决这类问题的. java线程同步机制的方式: 方式一:同

随机推荐