- 浏览: 702319 次
- 性别:
- 来自: 北京
博客专栏
-
读金庸故事,品程序人生
浏览量:47699
文章分类
最新评论
-
hty881008:
LZ,你的json返回是怎么出来的,我的怎么是No messa ...
使用CXF暴露您的REST服务 -
jxFY:
赞
Apache的对象池化工具commons-pool -
wangyudong:
新版本的Wisdom RESTClient地址https:// ...
使用CXF暴露您的REST服务 -
wangyudong:
由CXF实现的微服务需要有比较好的工具去测试RESTful A ...
使用CXF暴露您的REST服务 -
spring_springdata:
可以参考最新的文档:如何在eclipse jee中检出项目并转 ...
Maven3实战笔记01环境配置与使用入门
1. 什么样的对象存在线程安全问题
当我们刚学Java的时候不会考虑多线程的问题,在自己的IDE环境下运行成功了就行了,不会考虑并发使用此程序的时候会出现什么情况。等做程序员一段时间后发现自己编写的程序确实存在,多线程安全问题。之后走火入魔似地给自己写的方法加上synchronized。其实我们有时候没搞懂什么情况下会出现线程安全的问题。如果我们开发的是单机版的C/S应用系统,客户的计算机就是软件产品的服务器,那么会不会有并发现象呢?只要不是恶意恶搞你的软件,一般一个客户就对应着一个服务,这个时候不存在并发访问的问题,所以一般情况下不会考虑多线程的问题。如果在基于B/S的Web系统中,可能服务器是一台机器,世界各地的客户都来访问你开发的系统,为这些世界各地的客户提供服务,这个时候可能会出现线程安全的问题。为什么说可能,而不是一定呢?如果你的所有的类和类之间的调用都是通过new一个新的对象为之服务(极端情况下还真有),那么new实际上是在服务器内存中新开辟一块内存空间承载对象,那么这种极端情况是不存在线程安全问题的,只要您的服务器内存极大——1TB内存,每日客户量在2万人左右,估计运行1个月应该差不多没什么问题。不过考虑成本,一般没这样高级的服务器让这种极端的程序运行吧。那么最多的就是类之间的调用不是永远的new一个新的出来,而是为了节省资源,使用对象缓存池或者干脆整个应用软件调用比较频繁的类就使用一个单例对象就得了,每次使用这个对象都是原来已有的,不必在内存新建一个,这样垃圾回收器的工作量也不会那么大。而恰恰是这个时候,这个复用的对象就会出现线程安全的问题。所以在开发Web系统的时候大家一定要小心自己编写的类给别人调用的时候是否存在线程安全的问题。
如下程序:
package sy;
class Run implements Runnable {
Integer numIhread = new Integer(0);
int num;
String numStr = "";
public Run() {
}
@Override
public void run() {
// synchronized (numStr) {
for (int i = 0; i < 100; i++) {
numStr = "" + i;
System.out.println(Thread.currentThread().getName() + ":num=" + num
+ "---numStr:" + numStr + "======numIhread:"
+ numIhread);
numIhread++;
}
// }
}
}
public class RunClass {
/**
* @param args
*/
public static void main(String[] args) {
Run run1 = new Run();
// Run run2 = new Run();
new Thread(run1).start();
new Thread(run1).start();
}
}
运行之后,控制台线程冲突效果如下
Thread-0:num=0---numStr:0======numIhread:0 Thread-1:num=0---numStr:0======numIhread:0 Thread-0:num=0---numStr:1======numIhread:1 Thread-1:num=0---numStr:1======numIhread:1 Thread-1:num=0---numStr:2======numIhread:2 Thread-1:num=0---numStr:3======numIhread:3 Thread-1:num=0---numStr:4======numIhread:4 Thread-1:num=0---numStr:5======numIhread:5 Thread-1:num=0---numStr:6======numIhread:6 Thread-1:num=0---numStr:7======numIhread:7 Thread-0:num=0---numStr:2======numIhread:8
Thread-0从numIhread=1下次访问的时候一下子变成了numIhread=8,谁干的?这都是Thread-1影响的。
改进程序如下
package sy; class Run implements Runnable { ThreadLocal<Integer> numIhread = new ThreadLocal<Integer>(); int num; String numStr = ""; public Run() { } @Override public void run() { // synchronized (numStr) { for (int i = 0; i < 100; i++) { numStr = "" + i; System.out.println(Thread.currentThread().getName() + ":num=" + num + "---numStr:" + numStr + "======numIhread:" + numIhread.get()); num++; if (numIhread.get() == null) { numIhread.set(0); } int numTemp = numIhread.get(); numIhread.set(++numTemp); } // } } } public class RunClass { /** * @param args */ public static void main(String[] args) { Run run1 = new Run(); // Run run2 = new Run(); new Thread(run1).start(); new Thread(run1).start(); } }
运行后效果片断如下
Thread-0:num=0---numStr:0======numIhread:null Thread-1:num=0---numStr:0======numIhread:null Thread-1:num=2---numStr:1======numIhread:1 Thread-1:num=3---numStr:2======numIhread:2 Thread-1:num=4---numStr:3======numIhread:3 Thread-1:num=5---numStr:4======numIhread:4 Thread-1:num=6---numStr:5======numIhread:5 Thread-1:num=7---numStr:6======numIhread:6 Thread-1:num=8---numStr:7======numIhread:7 Thread-1:num=9---numStr:8======numIhread:8 Thread-1:num=10---numStr:9======numIhread:9 Thread-1:num=11---numStr:10======numIhread:10 Thread-1:num=12---numStr:11======numIhread:11 Thread-1:num=13---numStr:12======numIhread:12 Thread-1:num=14---numStr:13======numIhread:13 Thread-1:num=15---numStr:14======numIhread:14 Thread-1:num=16---numStr:15======numIhread:15 Thread-1:num=17---numStr:16======numIhread:16 Thread-1:num=18---numStr:17======numIhread:17 Thread-1:num=19---numStr:18======numIhread:18 Thread-1:num=20---numStr:19======numIhread:19 Thread-1:num=21---numStr:20======numIhread:20 Thread-1:num=22---numStr:21======numIhread:21 Thread-1:num=23---numStr:22======numIhread:22 Thread-0:num=2---numStr:1======numIhread:1 Thread-0:num=25---numStr:2======numIhread:2 Thread-1:num=24---numStr:23======numIhread:23
注意红色字体和蓝色字体,2个线程互不侵犯,拿到的numThread是第一个线程的变量副本,因此互不干扰,你走你的阳关道,我走我的独木桥。
2. 用synchronized和ThreadLocal的区别
其实网上已经很多资料介绍这2个的区别了,synchronized表示阻塞,是一种用时间换取空间的做法,而ThreadLocal呢是用资源空间换取时间的做法。synchronized表示一旦某个线程过来了,使用了此修饰后的方法或者代码块,那么其他任何线程不允许访问,直到此方法或者代码块都走完了,其他那些排队的线程再来执行,也就是说再某一个时刻,这个方法的权柄完全由单独一个线程把持着,别人别想越雷池一步,谁让你不早点来的,等老子使用完了,你再用吧。这就好比合租房子的厕所,厕所是个资源,一旦被人使用了,不好意思,其他人排队等着吧。而ThreadLocal呢,相当于大家上的公用厕所,而且只要是地球空间允许(对应于内存空间),那么将是无限个坑位,嘻嘻,爽吧,想上就上,而且互不打扰,你拉你的,我拉我的,而且大家的坑位都是从同一个模板构造出来的,不会出现不同的人使用不同样式的坑位,比如残疾人、老年人。一视同仁!ThreadLocal底层代码使用一个静态的内部类ThreadLocalMap存储副本变量,好让另一个线程使用(都是爷,它谁也不敢怠慢)。
3. 线程的启动
至于线程的调用,还有一点就是只要是线程,无论是继承自Thread类的还是实现类Runnable接口的,都必须使用start()方法作为启动线程的标识。如果不调用start()方法,那么他不能算是以线程为单元执行。
4. 静态同步方法
在静态方法前用synchronized修饰实际上是将整个静态类对象的该方法上了锁,也就是说锁定对象不是实例对象,而是类对象,所有的实例对象都是有内存中的仅有的一个类对象得来的。如果这个静态方法没有访问(或者说改变更贴切一些)任何的静态成员变量,那么其实进行加锁限定除了降低时间效率外没太大的作用。
package trap; public class ThreadTrap implements Runnable { static int num; public synchronized static void test() { int sum = 0; while (sum < 100) { System.out .println(Thread.currentThread().getName() + " sum:"+sum); sum++; try { Thread.sleep(1); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } if (sum == 50) { break; } } } @Override public void run() { test(); } /** * @param args */ public static void main(String[] args) { ThreadTrap run1 = new ThreadTrap(); // Run run2 = new Run(); new Thread(run1).start(); new Thread(run1).start(); } }
同步的静态方法中实际上并没有访问静态变量。那么其实加与不加synchronized差别不是很大,加入这个静态方法法十分消耗时间,那么另一个线程会等待很长时间才能执行此方法。那么这个时候加上synchronized还真不行,这就是典型的占着皇帝的宝座,不为老百姓办事!还不让别人坐,结果很显然,只能造反(系统崩溃),才能解决问题。
发表评论
-
Web应用单点压力测试调优-第6季-阶段性总结
2014-03-14 12:24 3377阶段性总结 <! ... -
Web应用单点压力测试调优-第5季
2014-03-13 09:32 4130各项配置: my.cnf [clien ... -
Web应用单点压力测试调优-第4季
2014-03-12 14:55 3155调整5-Tomcat的启动JVM参数 首先先启动 ... -
单点网站压力测试调优-第3季
2014-03-11 16:21 3416调整2-调整配置,数据库连接池数量 mysql ... -
Web应用单点压力测试调优-第2季
2014-03-07 16:52 8877并发1000,准备时间1s,让它产生大量的等待请求 ... -
单点网站压力测试调优-第1季
2014-03-07 10:36 3941环境介绍 虚拟机配置 ... -
编程质量提高建议总结1(持续总结)
2014-03-05 19:42 1297编程质量提高建议总结1(持续总结) 1.混淆字母要明显 ... -
关于博客文章内容显示不全的问题
2011-06-14 09:36 2384关于博客文章内容显示不全的问题,我发现有些文章显示内容不全。 ... -
Maven3实战笔记05仓库依赖解析与插件解析
2011-06-07 09:00 34231. Maven仓库依赖解析机 ... -
Apache的对象池化工具commons-pool
2011-05-16 09:21 130941. 前言 当我们的应用中创建一个十分最重量级的 ... -
将Sun的Open Message Queue与Spring集成
2011-05-06 09:01 34851. 前言 基于JMS标准的消息中间件实现的产品 ... -
要不要池化是个艰难的选择(转)-我觉得很生动就转载了下来
2011-05-05 09:50 1567转自http://www.ixpub.net/thre ... -
java.lang.IllegalStateException: STREAM错误的理解(转)
2011-05-04 18:09 13816转自http://dimple.iteye.com/blog/ ... -
Spring3配置声明式事务
2011-05-02 16:52 45471. 配置Spring3声明式事务 在Sprin ... -
Java基础复习笔记11基本排序算法
2011-04-25 13:20 21311. 排序 排序是一个历来都是很多算法家热衷的领 ... -
Java基础复习笔记08数据结构-二叉树和二叉树的遍历
2011-04-22 09:10 25431. 二叉树 一 ... -
Java基础复习笔记07数据结构-树的概述
2011-04-19 17:35 19361. 树的概念 如果线性表、栈、队列是线性结构( ... -
Java基础复习笔记06数据结构-队列
2011-04-19 17:25 16831. 队列 队列又是一种比较特殊的线性表,和栈一 ... -
Java基础复习笔记04数据结构-线性表
2011-04-15 14:14 23011. 线性表 线性表是数据结构的一种逻辑结构,其 ... -
Java基础复习笔记03面试、笔试、开发中我们不太注意的陷阱之流程控制、面向对象、异常处理
2011-04-13 09:59 22171. switch语句的用法 有人说:“笔者基础 ...
相关推荐
java面试笔试题库java学习比较开发教程互联网公司面试资料大全合集: 100家大公司java笔试题汇总.doc 125条常见的java 面试笔试题大汇总.pdf 2011最新整理java经典代码.doc 25个经典的Spring面试问答.docx 8张图解...
### Java基础复习笔记03:我们不会注意的陷阱 在Java编程中,存在许多细节问题,如果不加以注意,则可能在实际开发过程中导致各种难以预料的问题。本文将通过一系列实例来探讨那些经常被忽略的陷阱,并对其进行深入...
Java面试笔记 225道Java面试题JAVA面试基础知识点总结Java数据结构题 JAVA笔试面试WORD资料汇总(19个): 2014年最新Java笔试题及答案.docx 225道Java面试题 学会了Java面试随你问.docx Ant和Maven的作用是什么?两者...
根据提供的文件信息,我们可以推断出这是一本关于Java程序员面试和笔试准备的书籍,作者为何昊。本书可能包含了大量关于Java编程语言的基础知识、高级特性以及与面试相关的技巧和策略等内容。下面将对可能涉及的重要...
Java编程语言中,有一些细节和陷阱常常被开发者忽视,这些细节关系到字符串处理、编译时期优化、线程安全以及类型提升等核心概念。以下是对这些知识点的详细说明: 1. **虚拟机对字符串的处理**: Java虚拟机(JVM)...
这份"java笔记java笔试题 java面试题"的资源无疑是准备Java程序员的笔试和面试时的重要参考资料。以下是一些关键的Java知识点,这些内容可能会在笔记或面试中出现: 1. **Java基础**:Java的基础语法包括数据类型...
本学习笔记主要涵盖了Java的基础知识,包括面向对象、集合、IO流、多线程、反射与动态代理以及Java 8的新特性等方面,旨在帮助初学者或有经验的开发者巩固和提升Java编程技能。 1. 面向对象(OOP):Java的核心是...
Java基础每日复习笔记-JavaSE高级阶段.edf
java面试笔试资料java笔试题大集合及答案题库java笔试题汇总资料188个合集 100家大公司java笔试题汇总.doc 125条常见的java 面试笔试题大汇总.pdf 2011最新整理java经典代码.doc 25个经典的Spring面试问答.docx ...
Java多线程笔记是 Java 编程语言中关于多线程编程的笔记,涵盖了线程基础知识、线程优先级、线程状态、守护线程、构造线程、线程中断等多方面的内容。 获取简单 main 程序中的线程 在 Java 中,可以使用 ...
Java基础每日复习笔记-JavaSE高级阶段.2020-10-13-211312.edf
Java基础每日复习笔记-JavaSE基础阶段.edf
java面试笔试资料Java经典项目集锦java笔试题大集合及答案题库java笔试题汇总资料个合集(188) 100家大公司java笔试题汇总.doc 125条常见的java 面试笔试题大汇总.pdf 2011最新整理java经典代码.doc 25个经典的Spring...
Java EE的复习笔记是学习这个复杂框架的重要参考资料,尤其对于开发者来说,深入理解其核心概念和技术是必不可少的。 首先,Struts是Java EE中的一个MVC(Model-View-Controller)框架,它的主要任务是分离业务逻辑...
Java线程是多任务编程的重要组成部分,它允许程序同时执行多个独立的代码片段,从而提高程序的效率和响应性。本文将深入探讨Java线程的概念、原理以及如何在实际编程中进行有效管理。 首先,我们要了解操作系统中的...
Java架构面试笔试专题资料及经验(含答案)和学习笔记: ActiveMQ消息中间件面试专题.pdf Dubbo面试专题及答案(下).pdf Dubbo面试及答案(上).pdf java后端面试题答案.pdf Java基础面试题.pdf java多线程并发编程...
java面试笔试题库java软件设计java笔试题大集合及答案文档资料合集300MB“ 100家大公司java笔试题汇总.doc 125条常见的java 面试笔试题大汇总.pdf 2011最新整理java经典代码.doc 25个经典的Spring面试问答.docx 8张...