在编写一个类时,如果该类中的代码可能运行于多线程环境下,那么就要考虑同步的问题,Java实现线程同步的方法很多,具体如下。
(1)synchronized关键字
在Java中内置了语言级的同步原语synchronized关键字,其在多线程条件下实现了对共享资源的同步访问。根据synchronized关键字修饰的对象不同可以分为以下几种情况。
*synchronized关键字同步方法
public synchronized void method(){
//do something
}
注意: 如果使用synchronized关键字同步方法,很容易误认为同步关键字锁住了它所包围的代码。但是实际情况却不是这样,同步加锁的是对象而并非代码。因此。如果在一个类中有一个同步方法,该方法是可以被两个不同的线程同时执行的,只要每个线程自己创建一个该类的实例即可。
示例代码:
package newthread;
public class TestSync {
public static void main(String[] args) {
MyThread1 my1=new MyThread1(1);
MyThread1 my2=new MyThread1(3);
my1.start();
my2.start();
}
}
class MyThread1 extends Thread{
private int val;
public MyThread1(int v){
val=v;
}
public synchronized void printVal(int v){
for(int i=0;i<100;i++){
System.out.print(v);
}
}
public void run(){
printVal(val);
}
}
执行代码结果是1和3交叉输出的,即1和3两个线程在并发执行printVal方法,并没有实现同步功能。原因在于synchronized关键字锁定的是对象而并非代码块,如果需要实现真正的同步,必须同步一个全局对象或者对类进行同步。synchronized关键字同步类的格式如下:
synchronized(MyThread.class){}
改进代码
package newthread;
public class TestSync_2 {
public static void main(String[] args) {
MyThread_2 my1=new MyThread_2(1);
my1.start();
MyThread_2 my2=new MyThread_2(2);
my2.start();
}
}
class MyThread_2 extends Thread{
private int val;
public MyThread_2(int v){
val=v;
}
public void printVal(int v){
synchronized(MyThread_2.class){
for(int i=0;i<100;i++){
System.out.print(v);
}
}
}
public void run(){
printVal(val);
}
}
在上面的实例中,printVal()方法的功能不再对个别的类实行同步,而是对当前类进行同步。对于MyThread而言,它只有惟一的类定义,两个线程在相同的锁上同步,因此在同一时刻只有一个线程可以执行printVal()方法。至于输出结果的两种可能,则是由于Java线程调度的抢占实现模式所决定的。
*synchronized关键字同步公共的静态成员变量
在上面的示例中,通过对当前类进行加锁,达到了线程同步的效果,但是基于线程同步的一般原理是应该尽量减小同步的粒度以达到更高的性能。其实针对上文中的示例,也可以通过对公共对象加锁,即添加一个静态成员变量来实现,两种方法都通过同步该对象而达到线程安全。示例代码如下:
package newthread;
public class TestSync_3 {
public static void main(String[] args) {
MyThread_3 my1=new MyThread_3(2);
my1.start();
MyThread_3 my2=new MyThread_3(5);
my2.start();
}
}
class MyThread_3 extends Thread{
private int val;
private static Object lock=new Object();
public MyThread_3(int v){
val=v;
}
public void printVal(int v){
synchronized(lock){
for(int i=0;i<100;i++){
System.out.print(v);
}
}
}
public void run(){
printVal(val);
}
}
注意:为了能够提高程序的性能,针对示例代码中对于对象的选取做一个简单的介绍:基于JVM的优化机制,由于String类型的对象是不可变的,因此当用户使用“”的形式引用字符串时,如果JVM发现内存已经有一个这样的对象,那么它就使用该对象而不再生成一个新的String对象,这样是为了减小内存的使用;而零长度的byte数组对象创建起来将比任何对象要实用,生成零长度的byte[]对象只需要3条操作码,而Object lock=new Object()则需要7行操作码。
*synchronized关键字同步游离块
synchronized{
//do something
}
synchronized关键字同步代码块和上文中所提到的synchronized关键字同步公共的静态成员变量类似,都是为了降低同步粒度避免对整个类进行同步而极大降低了程序的性能
*synchronized关键字同步静态方法
public synchronized static void methodAAA(){
//do something
}
public void methodBBB(){
synchronized(Test.class){
//do something
}
}
synchronized关键字同步静态方法与synchronized关键字同步类实现的效果相同,唯一不同的是获得的锁对象不同,当同一个object对象访问methodAAA()和methodBBB()时,同步静态方法获得的锁就是该object类,而同步类方法获得的对象锁则是object对象所属的那个类
(2)Metux互斥体的设计和使用
Metux称为互斥体,同样广泛应用于多线程编程中。。其中以Doug Lea教授编写的concurrent工具包中实现的Mutex最为典型,本文将以此为例,对多线程编程中互斥体的设计和使用做简单介绍。在Doug Lea的concurrent工具包中,Mutex实现了Sync借口,该接口是concurrent工具包中所有锁(lock)、门(gate)、和条件变量(condition)的公共接口,SyncD的实现类主要有Metux、Semaphore及其子类、Latch、CountDown和ReentrantLock等。这也体现了面向对象抽象编程的思想,使开发者可以在不改变代码或者改变少量代码的情况下,选择使用Sync的不同实现。Sync接口的定义如下:
package newthread;
public interface Sync {
//获取许可
public void acquire() throws InterruptedException;
//尝试获取许可
public boolean attempt(long msecs)throws InterruptedException;
//释放许可
public void realse();
}
通过使用Sync接口可以替代synchronized关键字,并提供更加灵活的同步控制,但是并不是说concurrent工具包是独立于synchronized的技术,其实concurrent工具包也是在synchronized的基础上搭建的。区别在于synchronized关键字仅在方法内或者代码块内有效,而使用Sync却可以跨越方法甚至通过在对象之间传递,跨越对象进行同步。这是Sync及concurrent工具包比直接使用synchronized更加强大的地方。
需要注意的是Sync中的acquire()和attempt()方法都会抛出InterruptedException异常,因此在使用Sync及其子类时,调用这些方法一定要捕获InterruptedException。而release()方法并不会抛出InterruptedException异常,这是因为在acquire()和attemp()方法中都有可能会调用wait()方法等待其他线程释放锁。因此,如果对一个并没有acquire()方法的线程调用release()方法不会存在什么问题。而由于release()方法不会抛出InterruptedException,因此必须在catch或finally子句中调用release()方法以保证获得的锁能够被正确释放。示例代码如下:
package newthread;
public class TestSync_4 {
Sync gate;
public void test(){
try {
gate.acquire();
try{
//do something
}finally{
gate.realse();
}
} catch (InterruptedException ex) {
}
}
}
Mutex是一个非重入的互斥锁。广泛应用于需要跨越方法的before or after类型的同步环境中。下面是一个Doug Lea的concurrent工具包中的Mutex的实现,代码如下:
package newthread;
import com.sun.corba.se.impl.orbutil.concurrent.Sync;
public class TestMutex implements Sync{
protected boolean flg=false;
public void acquire() throws InterruptedException {
if(Thread.interrupted())
throw new InterruptedException;
synchronized(this){
try{
while(flg)
wait();
flg=true;
}catch(InterruptedException ex){
notify();
throw ex;
}
}
}
public boolean attempt(long msecs) throws InterruptedException {
if(Thread.interrupted())
throw new InterruptedException;
synchronized(this){
if(!flg){
flg=true;
return true;
}else if(msecs<=0){
return false;
}else{
long waitTime=msecs;
long start=System.currentTimeMillis();
try{
for(;;){
wait(waitTime);
if(!flg){
flg=true;
return true;
}else{
waitTime=msecs-(System.currentTimeMillis()-start);
if(waitTime<=0)
return false;
}
}
}catch(InterruptedException ex){
notify();
throw ex;
}
}
}
}
public void release() {
flg=false;
notify();
}
}
在上述示例中,在acquire()和attempt()方法的开始都要检查当前线程的中断标志是为了在当前线程已经被打断时可以立即返回,而不会在锁标志上等待。调用一个线程的interrupt()方法根据当前线程所处的状态,可能产生两种不同的结果,即当前线程在运行过程中被打断,则设置当前线程的中断标志为true;当前线程阻塞于wait()、sleep()、或join()方法,则当前线程的中断标志被清空,同时抛出InterruptedException。release()方法简单地重置flg标志,并通知其他线程。attempt()方法是利用Java的Object.wait(long)进行计时的,由于Object.wait(long)不是一个精确的时钟,因此attempt(long)方法也是一个粗略的计时。
- 浏览: 52369 次
- 性别:
- 来自: 北京
文章分类
最新评论
发表评论
-
ubuntu的数据库主从复制
2015-12-30 08:58 0详情见附件 -
spring JAR包详解
2015-12-11 11:32 563spring.jar 是包含有完整 ... -
JUnit Assert方法总结
2015-12-07 11:48 681junit中的assert方法全部放在Assert类中 ... -
windows中zookeeper的配置
2015-10-19 16:12 8331:首先下载一个zookeeper,我下载的版本是zooke ... -
StringUtils的常用方法
2015-09-17 17:02 7511.取得字符串的缩写 使用函数: StringUtils.ab ... -
详细设计
2015-04-21 14:33 531详细设计 OA协同办公系统,详细设计说明书 h ... -
没有安装jdk的系统中执行jar
2014-06-19 13:51 1079一、精简jre 制作了一个SWING程序,为用户方便,需 ... -
河南交通运输厅OA办公自动化项目总结
2014-05-09 09:24 0一、new很多spring容器 二、线程创建没有放在线程 ... -
Office转pdf
2014-04-29 09:11 1107一、使用Jodconverter 利用OpenOffice ... -
jdbc使用总结
2014-04-07 16:22 441JDBC连接数据库 •创建一个以JDBC连接数据库 ... -
log4j的使用
2014-03-10 16:03 407第一步:加入log4j-1.2.8.jar到lib下。第二 ... -
spring整合hibernate
2014-03-10 15:56 395原文转自:http://wanqiufeng.blog.51 ... -
webservice中的session的管理
2014-03-07 12:54 540Session.java代码如下: ... -
unicode编码的转化
2013-08-12 18:52 651/** * 把中文转成Unicode码 * @param ... -
java学习视频
2013-07-16 12:58 362http://edu.csdn.net/java/video ... -
读取Jar包内的资源
2013-04-26 11:21 900在项目中使用Maven,有大量的jar包。 原来代码中 直接 ... -
JVM参数说明
2013-04-13 10:27 736JVM配置参数中文说明: --------------- ... -
线程池ExecuteService使用总结一
2013-04-11 16:02 2645三个区别: 1、接收的参数不一样 2、submit有返回 ... -
https开发应用
2013-04-10 11:53 636SSL, 或者Secure Socket Laye ... -
json验证
2013-04-09 15:50 0try {// JSONObject json = ne ...
相关推荐
1.使用三种VC的多线程同步方法编写一个多线程的程序(要求在屏幕上先显示Hello,再显示World)。 1)基于全局变量的多线程同步程序; 2)基于事件的多线程同步程序; 3)基于临界区的多线程同步程序。
Java多线程同步.pdf
VC++MFC多线程同步实例,信号量,互斥锁,事件,临界资源
MFC多线程同步类的使用
.NET多线程同步方法详解(一):自由锁(InterLocked) 本文主要描述在C#中线程同步的方法。线程的基本概念网上资料也很多就不再赘述了。直接接入主题,在多线程开发的应用中,线程同步是不可避免的。在.Net框架中,...
windows系统的多线程同步实验报告和cpp文件
C#实现多线程同步并发操作,在线源码,供你下载学习
C#的多线程同步,C#中四种进程或线程同步互斥的控制方法
Monitor Mutex EventWaithandle五个多线程同步应用小程序
操作系统 用多线程同步方法解决生产者-消费者问题 课设报告
大家不要下载,这程序是本人上传的,只是实现了多线程和互斥,老师说不能算是同步。
在掌握基于消息的windows程序结构和多线程程序设计方法的基础上,设计一个多线程同步的程序。使学生能够从程序设计的角度了解多线程程序设计的方法和在windows系统下多线程同步互斥的机制。 2、实验内容 理解Windows...
很不错的Delphi多线程和线程同步的例子,完整源码 原来的一个不知道CSDN怎么把文件搞丢了!新传一个资源,包含一个线程排序的例子!代码均搜集自网上!
设计一个多线程, 并且实现同步, 我理解的多线程需求如下: 1. 线程在Java端启动, 两个线程都调用C的方法 2. 有一个共同的数据, 被C的代码修改, 要求线程能对这个修改做同步, 即线程1
不精通线程、不擅长对多线程进行管理,就不可能在当今多CPU多核心的年代写出优秀的程序代码,软件的性能将会大打折扣。本文及其示例代码,诠释System.Classes.pas中的(多)线程 和System.SyncObjs.pas (深入应用...
多线程同步解决卖票问题
python 条件同步的使用 条件同步:threading.Condition 多线程同步,python2例程 多线程的同步 多线程情况下最常见的问题之一:数据共享; 当多个线程都要去修改某一个共享数据的时候,需要对数据访问进行同步...
java 多线程同步方法的实例 java 多线程同步方法的实例 java 多线程同步方法的实例
利用临界区的多线程同步测试