`
xyheqhd888
  • 浏览: 403694 次
  • 性别: Icon_minigender_1
  • 来自: 秦皇岛
社区版块
存档分类
最新评论

Java线程之同步化(Synchronized)主题

阅读更多

1.  如果一个对象所持有的数据可以被多线程同时共享存取,必须考虑到数据同步的问题。所谓数据同步指的是两份数据的整体性和一致性。数据在多线程下共享时容易由于同时多个线程可能更新同一个对象的信息,而造成对象数据的不同步,因为数据的不同步可能引发的错误通常不易察觉,而且可能是在程序执行了几千几万次之后,才会发生错误。这通常会发生在产品已经上线之后,甚至是程序已经执行了几年之后。

2. 举个简单的例子,设计了一个PersonalInfo类:   

package ysu.hxy;

public class PersonalInfo 
{
	private String name;
	private String id;
	private int count;

	public PersonalInfo()
	{
		name = "nobody";
		id = "N/A";
	}

	public void setNameAndID(String name,String id)
	{
		this.name = name;
		this.id = id;
		if(!checkNameAndIDEqual())
		{
             System.out.println(count + ") illegal name or ID...");
		}
		count ++;
	}

	private boolean checkNameAndIDEqual(){
		return (name.charAt(0) == id.charAt(0)) ? true:false;
	}
}

 单就这个类本身而言,它并没有任何的错误,但如果它被用于多线程的程序中,而且同一个对象被多个线程存取时,就会有可能发生错误。下面是一个简单的测试程序,看看PersonalInfo类在多线程共享数据下会发生什么问题。

package ysu.hxy;

public class PersonalInfoTest 
{
	public static void main(String[] args) 
	{
		final PersonalInfo person = new PersonalInfo();

		//假设会能两个线程可能更新person对象 
		Thread thread1 = new Thread(new Runnable() {
			public void run() {
				while(true){
					person.setNameAndID("Justin Lin","J.L");
				}
			}
		});

        Thread thread2 = new Thread(new Runnable() {
			public void run() {
				while(true){
					person.setNameAndID("Shang Hwang Lin","S.H");
				}
			}
		});

		System.out.println();

		thread1.start();
		thread2.start();
	}
}

 

执行结果:

D:\hxy>java ysu.hxy.PersonalInfoTest
开始测试...
23466451) illegal name or ID...
78044494) illegal name or ID...
101630476) illegal name or ID...
106496643) illegal name or ID...
145330181) illegal name or ID...
169674022) illegal name or ID...
174072203) illegal name or ID...
214717201) illegal name or ID...
219668799) illegal name or ID...
240921750) illegal name or ID...
265875722) illegal name or ID...
270920923) illegal name or ID...
281256783) illegal name or ID...

这个程序出现了错误,在23466451次的setNameAndID()执行时就开始了。如果程序完成并开始应用于实际场合之后,这个时间点可能是几个月甚至是几年之后。问题出在这里:

public void setNameAndID(String name,String id)
	{
		this.name = name;
		this.id = id;
		if(!checkNameAndIDEqual())
		{
             System.out.println(count + ") illegal name or ID...");
		}
		count ++;
	}

      虽然传递给setNameAndID()的变量并没有问题,在某个时间点时,thread1设定了Justin Lin、J.L给name和id,在进行if测试的前一刻,thread2可能此时刚好调用setNameAndID("Shang Hwang","S.H")。在name被设定为Shang HWang时,checkNameAndIDEqual()开始执行,此时name等于Shang HWang,而id还是J.L。所以,checkNameAndIDEqual()就会返回false,结果就显示了错误信息。

       必须同步数据对对象的更新,方法在有一个线程正在设定person对象的数据时,不可以被另一个线程同时进行设定。可以使用synchronized关键词来进行这个动作。

public synchronized void setNameAndID(String name,String id)
	{
		this.name = name;
		this.id = id;
		if(!checkNameAndIDEqual())
		{
             System.out.println(count + ") illegal name or ID...");
		}
		count ++;
	}

 这是synchronized关键词的一个使用方式,用于方法上让方法的范围内都成为被同步化区域。被同步化区域在有一个线程占据时就像一个禁区,不允许其他线程进入。由于同时间只能有一个线程在被同步化区域,所以更新共享数据时,就像单线程程序在更新数据一样,以保证对象中的数据会与给定的数据同步。

  sychronized的设定不只可用于方法上,也可以用于限定某个程序区块上被同步化区域。例如: 

public void setNameAndID(String name,String id)
  { //同步某个程序区块
       synchronized(this)
   {
       this.name = name;
       this.id = id;
       if(!checkNameAndIDEqual())
          {
               System.out.println(count+") illegal name or ID...");
          }
   }
}

   这个程序片段的意思是,在线程执行到synchronized设定的被同步化区块时锁定当前对象,这样就没有其他线程可以来执行这个被同步化区块。这个方式可以应用于您不想锁定整个方法区块,而只是想在更新共享数据时再确保对象与数据的同步化。由于只锁定方法中的某个区块,在执行完区块后即释放对对象的锁定,以便让其他线程能有机会对对象进行操作,相对于锁定整个方法区块效率较高。

   也可以标示某个对象要求同步化。例如在多线程中存取同一个ArrayList对象时,由于ArrayList并没有实现数据存取时的同步化,所以当它使用多线程环境时,必须注意多个线程存取同一个ArrayList时,有可能发生两个以上的线程将数据存入ArrayList的同一个位置,造成数据的相互覆盖。为了确保数据存入时的正确性,可以在存取ArrayList对象时要求同步化。例如:

//arraylist参考至一个ArrayList的一个实例
synchronized(arraylist)
{
     arrayList.add(new SomeClass());
}

 同步化确保数据的同步,但所牺牲的就是在于一个线程占据同步化区块,而其他线程等待它释放区块执行权时的延迟。这在线程少时可能看不出来,但在线程多的环境中必然造成一定的效率问题(例如大型网站的多人联机时)。   

 

分享到:
评论

相关推荐

    Java中的线程同步与ThreadLocal无锁化线程封闭实现

    主要介绍了Java中的线程同步与ThreadLocal无锁化线程封闭实现,Synchronized关键字与ThreadLocal变量的使用是Java中线程控制的基础,需要的朋友可以参考下

    java中的并发和多线程编程中文版

    读者将通过使用java.lang.thread类、synchronized和volatile关键字,以及wait、notify和notifyall方法,学习如何初始化、控制和协调并发操作。此外,本书还提供了有关并发编程的全方位的详细内容,例如限制和同步、...

    java面试题进阶版附答案.docx

    三、线程同步和互斥锁:解释了线程同步的概念,以及互斥锁的作用,包括使用synchronized关键字和Lock接口实现线程同步的方式。 四、反射机制:解释了Java中的反射机制,包括在运行时动态获取类的信息,操作类的属性...

    Java并发编程原理与实战

    线程的初始化,中断以及其源码讲解.mp4 多种创建线程的方式案例演示(一)带返回值的方式.mp4 多种创建线程的方式案例演示(二)使用线程池.mp4 Spring对并发的支持:Spring的异步任务.mp4 使用jdk8提供的lambda进行...

    Java SE实践教程 pdf格式电子书 下载(四) 更新

    第6章 三头六臂——线程和同步的基本概念 109 6.1 讲解 110 6.1.1 什么是线程 110 6.1.2 创建线程 110 6.1.3 线程的生命周期 112 6.1.4 线程的优先级 114 6.1.5 中断线程 115 6.1.6 线程组 116 6.1.7 处理未...

    java基础案例与开发详解案例源码全

    12.5.2 使用集合工具类同步化集合类对象324 12.5.3 使用JDK5.0后提供的并发集合类324 12.6 用Timer类调度任务325 12.7 本章练习326 第13章 13.1 java.io.File类328 13.1.1 文件和目录是什么?328 13.1.2 Java对文件...

    java 面试题 总结

    最大的不同是,Hashtable的方法是Synchronize的,而HashMap不是,在多个线程访问Hashtable时,不需要自己为它的方法实现同步,而HashMap 就必须为之提供外同步。 Hashtable和HashMap采用的hash/rehash算法都大概...

    Java并发编程实战

    本书深入浅出地介绍了Java线程和并发,是一本完美的Java并发参考手册。书中从并发性和线程安全性的基本概念出发,介绍了如何使用类库提供的基本并发构建块,用于避免并发危险、构造线程安全的类及验证线程安全的规则...

    JAVA入门1.2.3:一个老鸟的JAVA学习心得 PART1(共3个)

    1.2 准备好开始Java之旅 3 1.2.1 下载JDK 4 1.2.2 安装JDK 5 1.2.3 配置环境变量 6 1.2.4 测试环境是否安装成功 8 1.2.5 如果失败了怎么办? 9 1.3 让自己的第一个程序运行起来 10 1.3.1 编写自己的Hello ...

    java7源码-scn:《疯狂Java讲义》学习

    同步代码块,同步方法synchronized 同步锁:ReentrantLock锁(具有重入性) 死锁 线程通信 线程池 2.网络编程 Java的基本网络支持: InetAddress、URLDecoder/URLEncoder、URL/URLConnection/URLPermission 2017/07/13...

    java并发编程

    , 这里,读者将通过使用java.lang.thread类、synchronized和volatile关键字,以及wait、notify和notifyall方法,学习如何初始化、控制和协调并发操作。此外,本书还提供了有关并发编程的全方位的详细内容,例如限制...

    JAVA面试题最全集

    多线程,用什么关键字修饰同步方法?stop()和suspend()方法为何不推荐使用? 59.使用socket建立客户端与服务器的通信的过程 60.JAVA语言国际化应用,Locale类,Unicode 61.描述反射机制的作用 62.如何读写一个...

    java 并发编程实践 001

    java 并发编程实践001 002 两个文件全部下载后 用 7z解压 第1章 介绍 1.1 并发的(非常)简短历史 1.2 线程的优点 1.3 线程的风险 1.4 线程无处不在 第1部分 基础 ...4.5 同步策略的文档化 …… 共16个章节

    Java入门1·2·3:一个老鸟的Java学习心得.PART3(共3个)

    1.2 准备好开始Java之旅 3 1.2.1 下载JDK 4 1.2.2 安装JDK 5 1.2.3 配置环境变量 6 1.2.4 测试环境是否安装成功 8 1.2.5 如果失败了怎么办? 9 1.3 让自己的第一个程序运行起来 10 1.3.1 编写自己的Hello ...

    Java常见面试题208道.docx

    面试题包括以下十九部分:Java 基础、容器、多线程、反射、对象拷贝、Java Web 模块、异常、网络、设计模式、Spring/Spring MVC、Spring Boot/Spring Cloud、Hibernate、Mybatis、RabbitMQ、Kafka、Zookeeper、MySql...

    龙果java并发编程完整视频

    第6节线程的初始化,中断以及其源码讲解00:21:26分钟 | 第7节多种创建线程的方式案例演示(一)带返回值的方式00:17:12分钟 | 第8节多种创建线程的方式案例演示(二)使用线程池00:15:40分钟 | 第9节Spring对并发...

    Java SE实践教程 源代码 下载

    第6章 三头六臂——线程和同步的基本概念 109 6.1 讲解 110 6.1.1 什么是线程 110 6.1.2 创建线程 110 6.1.3 线程的生命周期 112 6.1.4 线程的优先级 114 6.1.5 中断线程 115 6.1.6 线程组 116 6.1.7 处理未...

Global site tag (gtag.js) - Google Analytics