`
JohnShen0708
  • 浏览: 59064 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

Java多线程 - 不要同步Boolean常量

阅读更多

在JAVA中通过synchronized语句可以实现多线程并发。使用同步代码块,JVM保证同一时间只有一个线程可以拥有某一对象的锁。锁机制实现了多个线程安全地对临界资源进行访问。

 

同步代码写法如下:

 

代码1:

Object obj = new Object();
...
synchronized(obj) {
  //TODO: 访问临界资源
}

 

 

JAVA的多线程总是充满陷阱,如果我们用Boolean作为被同步的对象,可能会出现以下两种情况:

 

一. 以为对一个对象加锁,实际同步的是不同对象。

 

代码2:

 

private volatile Boolean isTrue = false;

publich void aMethod() {
  ...
  synchronized(isTrue) {
    isTrue = !isTrue;
    //TODO: 访问临界资源
    isTrue = !isTrue;
  }
  ...
}

 咋一看上面的代码没有问题,由于使用了synchronized(isTrue)同一时间只能有一个线程访问临界资源,但事实并不是这样。因为false和true这两个常量对应着两个不同的对象。当isTrue产生变化时,很可能导致不同的线程同步了不同的对象。JAVA的自动装箱会将false变为Boolean.FALSE,将true变为Boolean.TRUE(同时这也说明了此处若将false改为Boolean.FALSE其结果也是一样的)。写一个以上情况的测试代码如下:

 

代码3:

 

public class BooleanTest {
	
	private volatile Boolean isTrue = Boolean.FALSE; //此处用false也一样
	
	public void aMethod() {
		for(int i=0;i<10;i++) {
			Thread t = new Thread() {
				public void run() {
					synchronized(isTrue) {
						isTrue = !isTrue;
						System.out.println(Thread.currentThread().getName() + " - isTrue=" + isTrue);
						try{
							Double ran = 1000 * Math.random();
							Thread.sleep(ran.intValue());
						}catch(InterruptedException e) {}
						
						if(!isTrue) System.out.println(Thread.currentThread().getName() + " - Oh, No!");

						isTrue = !isTrue;
					}
				}
			};
			t.start();
		}
	}
	
	public static void main(String... args) {
		BooleanTest bt = new BooleanTest();
		bt.aMethod();
	}
}

 运行以上代码,不时的会看到 " - Oh, No!",表示不同的线程同时进入到synchronized代码块中。

 

二. 以为同步的是不同对象,实际是一个对象。

 

有时候我们可能希望在多个对象上进行同步,如果使用了Boolean作为被同步对象,很可能会导致本来应该没有关系的两个同步块使用了相同对象的锁。示例如下:

 

代码4:

 

private volatile Boolean aBoolean = Boolean.FALSE;

private volatile Boolean anotherBoolean = false;

public void aMethod() {
  ...
  synchronized(aBoolean) {
    //TODO: 访问临界资源1
  }
  ...
}

public void anotherMethod() {
  ...
  synchronized(anotherBoolean) {
    //TODO: 访问临界资源2
  }
  ...
}

 假设原本aMethod和anotherMethod分别会被两组没有关系的线程调用。但是由于Boolean.FALSE和false指向的是同一个对象,可能导致对临界资源2的访问被临界资源1阻塞了(反之亦然)

 

以上两种情况说明,在使用同步块时,尽量不用使用Boolean对象作为被同步对象,不然可能会出现意想不到的问题,或者对以后的代码修改造成陷阱。

 

从此也可以看出,任何对常量的同步都是有风险的。如果一定要对 Boolean 进行同步,一定要用 new 操作符来创建 Boolean 对象。

分享到:
评论

相关推荐

    浅谈Java多线程编程中Boolean常量的同步问题

    主要介绍了浅谈Java多线程编程中Boolean常量的同步问题,主要针对线程之间同步了不同的布尔对象的问题,需要的朋友可以参考下

    Java学习笔记-个人整理的

    {8}多线程}{121}{chapter.8} {8.1}线程的常用属性与方法}{121}{section.8.1} {8.2}后台线程}{123}{section.8.2} {8.3}创建线程的两种方法}{123}{section.8.3} {8.4}Runnable}{123}{section.8.4} {8.5}Sleep...

    JAVA基础课程讲义

    JAVA中如何实现多线程(重点!!) 168 通过继承Thread类实现多线程 168 通过Runnable接口实现多线程 169 线程状态和sleep/yield/join/stop/destroy方法 170 新生状态 170 就绪状态 170 运行状态 170 死亡状态 170 ...

    Java2核心技术.part5

    1.2.10多线程 1.2.11动态性 1. 3 Java与Internet 1. 4 Java发展简史 1.5关于Java的常见误解 第2章Java程序设计环境 2.1安装Java开发工具箱 2.1.1下载JDK 2.1.2设置执行路径 2.1.3安装库源代码...

    java 面试题 总结

    与cgi的区别在于servlet处于服务器进程中,它通过多线程方式运行其service方法,一个实例可以服务于多个请求,并且其实例一般不会销毁,而CGI对每个请求都产生新的进程,服务完成后就销毁,所以效率上低于servlet。...

    Java2核心技术.part3

    1.2.10多线程 1.2.11动态性 1. 3 Java与Internet 1. 4 Java发展简史 1.5关于Java的常见误解 第2章Java程序设计环境 2.1安装Java开发工具箱 2.1.1下载JDK 2.1.2设置执行路径 2.1.3安装库源代码...

    Java2核心技术.part1

    1.2.10多线程 1.2.11动态性 1. 3 Java与Internet 1. 4 Java发展简史 1.5关于Java的常见误解 第2章Java程序设计环境 2.1安装Java开发工具箱 2.1.1下载JDK 2.1.2设置执行路径 2.1.3安装库源代码和文档 ...

    Java2核心技术.part6

    1.2.10多线程 1.2.11动态性 1. 3 Java与Internet 1. 4 Java发展简史 1.5关于Java的常见误解 第2章Java程序设计环境 2.1安装Java开发工具箱 2.1.1下载JDK 2.1.2设置执行路径 2.1.3安装库源代码...

    Java2核心技术.part4

    1.2.10多线程 1.2.11动态性 1. 3 Java与Internet 1. 4 Java发展简史 1.5关于Java的常见误解 第2章Java程序设计环境 2.1安装Java开发工具箱 2.1.1下载JDK 2.1.2设置执行路径 2.1.3安装库源代码...

    Java2核心技术.part2

    1.2.10多线程 1.2.11动态性 1. 3 Java与Internet 1. 4 Java发展简史 1.5关于Java的常见误解 第2章Java程序设计环境 2.1安装Java开发工具箱 2.1.1下载JDK 2.1.2设置执行路径 2.1.3安装库源代码...

    java面试题

    多线程几种实现方法,同步? 答:多线程有两种实现方法,一种是继承Thread类或者实现Runnable接口。同步就是在方法返回类型后面加上synchronized。 c#中的委托,事件是不是委托? 答:委托就是将方法作为一个参数...

    超级有影响力霸气的Java面试题大全文档

    与cgi的区别在于servlet处于服务器进程中,它通过多线程方式运行其service方法,一个实例可以服务于多个请求,并且其实例一般不会销毁,而CGI对每个请求都产生新的进程,服务完成后就销毁,所以效率上低于servlet。...

    java经典面试2010集锦100题(不看你后悔)

    D) 在Java中,最多使用的是抽象类,因为应用抽象类也可以实现多继承。而接口已经被限制使用。 题目21:d 程序如下: interface Eo { double PI=3.1415926; } abstract class A { void shot() { System.out....

    javaSE代码实例

    第16章 多线程——Java中的并发协作 343 16.1 线程的基本知识 343 16.1.1 多线程编程的意义 343 16.1.2 定义自己的线程 344 16.1.3 创建线程对象 345 16.1.4 启动线程 347 16.1.5 同时使用多个线程 ...

    C#微软培训资料

    第五章 变量和常量 .44 5.1 变 量 .44 5.2 常 量 .46 5.3 小 结 .47 第六章 类 型 转 换 .48 6.1 隐式类型转换 .48 6.2 显式类型转换 .53 6.3 小 结 .56 第七章 表 达 式 .58 7.1 操 作 符 .58 ...

Global site tag (gtag.js) - Google Analytics