`

多线程同步

阅读更多

在编写一个类时,如果该类中的代码可能运行于多线程环境下,那么就要考虑同步的问题,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)方法也是一个粗略的计时。

分享到:
评论

相关推荐

    使用三种VC的多线程同步方法编写一个多线程的程序

    1.使用三种VC的多线程同步方法编写一个多线程的程序(要求在屏幕上先显示Hello,再显示World)。 1)基于全局变量的多线程同步程序; 2)基于事件的多线程同步程序; 3)基于临界区的多线程同步程序。

    简单实现多线程同步示例(模拟购票系统)

    本示例“简单实现多线程同步示例(模拟购票系统)”旨在通过一个具体的实例,帮助开发者理解如何在Java中创建并管理多线程以及如何实现线程同步,确保数据的一致性和正确性。 首先,我们要明确多线程的基本概念。在...

    Java多线程同步.pdf

    "Java多线程同步.pdf" Java多线程同步是指在Java语言中,如何使用synchronized关键字和其他同步机制来确保多线程程序的正确执行。在Java语言中,synchronized关键字用于对方法或者代码块进行同步,但是仅仅使用...

    操作系统实验多线程同步(含C++源代码)

    操作系统中的多线程同步是一个关键概念,特别是在并发编程中,它涉及到如何协调多个线程以避免数据不一致性和竞态条件。在这个实验中,我们关注的是C++编程语言中的实现,以及操作系统如何处理线程的优先级。 首先...

    JNI 多线程同步机制的源码实现

    本文将深入探讨如何使用JNI实现多线程同步,并通过源码来解析这一过程。 1. **JNI基础知识** JNI为Java程序员提供了一种方式,可以在Java代码中调用本地方法,反之亦然。它定义了一系列的函数,例如`FindClass`...

    C#实现多线程同步并发操作

    本文将深入探讨如何在C#中实现多线程同步并发操作,这不仅对于提高软件性能至关重要,也是高级程序员必须掌握的核心技能之一。 ### C#中的多线程同步并发操作 多线程编程可以极大地提高CPU的利用率,特别是在处理I...

    多线程同步演示(采用CreateThread()和WaitForMultipleObjects()函数)

    以下是对这两个函数及其在多线程同步演示中的应用的详细解释。 首先,`CreateThread()`函数用于创建一个新的线程来执行指定的函数,即线程的入口点。该函数接收一系列参数,包括线程函数指针、初始线程堆栈大小、...

    VC++MFC多线程同步实例

    多线程同步是保证多个线程安全交互的关键技术,避免了数据竞争和死锁等问题。本实例主要探讨了四种主要的同步机制:信号量、互斥锁、事件以及临界资源。 首先,信号量(Semaphore)是一种计数型同步对象,用于控制...

    windows系统的多线程同步实验报告和cpp

    在Windows操作系统中,多线程同步是一个至关重要的概念,它涉及到如何有效地管理和协调多个线程在共享资源时的执行顺序,以避免数据竞争和其他并发问题。这篇实验报告和相关的C++代码着重探讨了这一主题。 多线程是...

    MFC多线程同步类的使用

    【MFC多线程同步类的使用】 在MFC(Microsoft Foundation Classes)中,多线程编程是一项重要的技术,尤其在开发复杂的、并发执行的任务时。多线程允许程序同时执行多个任务,提升效率和响应速度。然而,线程间的...

    五个多线程同步应用小程序

    本文将深入探讨在.NET框架中用于多线程同步的三个主要工具:Monitor、Mutex和EventWaitHandle,并结合提供的"五个多线程同步应用小程序",解释它们的应用场景和使用方法。 首先,我们来看Monitor类。Monitor是.NET...

    多线程同步Demo

    本Demo主要关注四种多线程同步机制:临界区(CriticalSection)、互斥量(Mutex)、事件(Event)以及信号量(Semaphore)。下面将详细阐述这些概念及其在实际应用中的作用。 1. 临界区(CriticalSection) 临界区...

    用多线程同步方法解决生产者-消费者问题

    生产者-消费者问题是多线程同步的一个经典案例,主要探讨如何在并发环境下,确保生产者进程和消费者进程之间正确地共享资源,避免数据竞争和死锁。在这个问题中,生产者进程负责创建产品并将产品放入缓冲区,而消费...

    利用临界区的多线程同步测试.rar_临界区_多线程同步_线程 同步_线程同步

    "临界区"和"多线程同步"是解决这一问题的关键概念。临界区是一种同步机制,它允许一次只有一个线程访问特定的代码区域或资源,以避免并发访问时可能产生的数据竞争和不一致性。多线程同步则是为了协调多个线程的执行...

    VC++多线程同步基本示例

    本示例着重讲解了VC++中的多线程同步,这是多线程编程中确保数据安全和正确性的重要概念。我们将深入探讨临界区、互斥量、事件和信号量这四种多线程同步机制。 1. **临界区(Critical Section)**:临界区是多线程...

    Delphi多线程同步的例子

    本文将深入探讨Delphi中的多线程和线程同步,并以"SortThreads"和"delphi-thread-gui"这两个示例项目为例,讲解如何在实践中应用这些概念。 1. **多线程**:多线程允许应用程序同时执行多个独立的任务,提高程序的...

    NET多线程同步方法详解

    .NET中的多线程同步是确保在并发环境下多个线程安全访问共享资源的关键技术。本文将深入探讨.NET框架中实现线程同步的多种方法。 首先,我们来看自由锁(InterLocked)。自由锁主要通过Interlocked类提供的静态方法来...

    3种多线程实现同步方法

    本篇文章将深入探讨三种在C++中实现多线程同步的方法:事件对象、关键代码段和互斥对象。 首先,我们来看**事件对象**。事件对象是一种信号机制,用于线程间通信和同步。在Windows API中,CreateEvent函数创建一个...

Global site tag (gtag.js) - Google Analytics