精华内容
下载资源
问答
  • 同步函数

    2015-10-15 19:56:53
    class Bank { private int sum; public synchronized void add(int n) { //与同步代码块类似,只需要在函数生命中加入synchronized修饰。 sum = sum + n; try { Thread.sleep(10); } catch (Exce
    class Bank {
        private int sum;
    
        public synchronized void add(int n) {  //与同步代码块类似,只需要在函数声明中加入synchronized修饰。
    
            sum = sum + n;
            try {
                Thread.sleep(10);
            } catch (Exception e) {}
            System.out.println("sum=" + sum);
    
        }
    }
    
    class Cus implements Runnable {
        private Bank b = new Bank();
    
        public void run() {
            for (int x = 0; x < 3; x++) {
                b.add(100);
            }
        }
    }
    
    class BankDemo {
        public static void main(String[] args) {
            Cus c = new Cus();
            Thread t1 = new Thread(c);
            Thread t2 = new Thread(c);
            t1.start();
            t2.start();
        }
    }
    
    展开全文
  • 同步代码块: 可以自己任意指定同步锁对象 ...同步函数同步函数的锁对象是固定的this对象 静态同步函数: 静态同步函数的锁对象是该函数所属的类的字节码文件对象 以上可以自己通过代码验证。

    同步代码块:

    可以自己任意指定同步锁对象

    同步函数:

    同步函数的锁对象是固定的this对象

    静态同步函数:

    静态同步函数的锁对象是该函数所属的类的字节码文件对象


    以上可以自己通过代码验证。

    展开全文
  • 同步函数与异步函数

    千次阅读 2018-01-29 14:02:12
    1. 同步函数与异步函数  什么是同步函数?  什么是异步函数?  它们在线程中执行时会对线程有何影响?  一个线程如何与一个异步执行的函数进行同步? 依据微软的MSDN上的解说: (1) 同步函数:当一个...

    1. 同步函数与异步函数

            什么是同步函数?

            什么是异步函数?

            它们在线程中执行时会对线程有何影响?

            一个线程如何与一个异步执行的函数进行同步?

    依据微软的MSDN上的解说:

    (1)   同步函数:当一个函数是同步执行时,那么当该函数被调用时不会立即返回,直到该函数所要做的事情全都做完了才返回。

    (2)   异步函数:如果一个异步函数被调用时,该函数会立即返回尽管该函数规定的操作任务还没有完成。

    (3) 在一个线程中分别调用上述两种函数会对调用线程有何影响呢?

            当一个线程调用一个同步函数时(例如:该函数用于完成写文件任务),如果该函数没有立即完成规定的操作,则该操作会导致该调用线程的挂起将CPU的使用权交给系统,让系统分配给其他线程使用),直到该同步函数规定的操作完成才返回,最终才能导致该调用线程被重新调度。

             当一个线程调用的是一个异步函数(例如:该函数用于完成写文件任务),该函数会立即返回尽管其规定的任务还没有完成,这样线程就会执行异步函数的下一条语句,而不会被挂起。那么该异步函数所规定的工作是如何被完成的呢?当然是通过另外一个线程完成的了啊;那么新的线程是哪里来的呢?可能是在异步函数中新创建的一个线程也可能是系统中已经准备好的线程

    (4)一个调用了异步函数的线程如何与异步函数的执行结果同步呢?

            为了解决该问题,调用线程需要使用“等待函数”来确定该异步函数何时完成了规定的任务。因此在线程调用异步函数之后立即调用一个“等待函数”挂起调用线程,一直等到异步函数执行完其所有的操作之后,再执行线程中的下一条指令。

    我们是否已经发现了一个有趣的地方呢?!就是我们可以使用等待函数将一个异步执行的函数封装成一个同步函数。

     

    2.同步调用与异步调用

            操作系统发展到今天已经十分精巧,线程就是其中一个杰作。操作系统把 CPU 处理时间划分成许多短暂时间片,在时间 T1 执行一个线程的指令,到时间 T2 又执行下一线程的指令,各线程轮流执行,结果好象是所有线程在并肩前进。这样,编程时可以创建多个线程,在同一期间执行,各线程可以“并行”完成不同的任务。
            在单线程方式下,计算机是一台严格意义上的冯·诺依曼式机器,一段代码调用另一段代码时,只能采用同步调用,必须等待这段代码执行完返回结果后,调用方才能继续往下执行。有了多线程的支持,可以采用异步调用,调用方和被调方可以属于两个不同的线程,调用方启动被调方线程后,不等对方返回结果就继续执行后续代码。被调方执行完毕后,通过某种手段通知调用方:结果已经出来,请酌情处理。
            计算机中有些处理比较耗时。调用这种处理代码时,调用方如果站在那里苦苦等待,会严重影响程序性能。例如,某个程序启动后如果需要打开文件读出其中的数据,再根据这些数据进行一系列初始化处理,程序主窗口将迟迟不能显示,让用户感到这个程序怎么等半天也不出来,太差劲了。借助异步调用可以把问题轻松化解:把整个初始化处理放进一个单独线程,主线程启动此线程后接着往下走,让主窗口瞬间显示出来。等用户盯着窗口犯呆时,初始化处理就在背后悄悄完成了。程序开始稳定运行以后,还可以继续使用这种技巧改善人机交互的瞬时反应。用户点击鼠标时,所激发的操作如果较费时,再点击鼠标将不会立即反应,整个程序显得很沉重。借助异步调用处理费时的操作,让主线程随时恭候下一条消息,用户点击鼠标时感到轻松快捷,肯定会对软件产生好感。
            异步调用用来处理从外部输入的数据特别有效。假如计算机需要从一台低速设备索取数据,然后是一段冗长的数据处理过程,采用同步调用显然很不合算:计算机先向外部设备发出请求,然后等待数据输入;而外部设备向计算机发送数据后,也要等待计算机完成数据处理后再发出下一条数据请求。双方都有一段等待期,拉长了整个处理过程。其实,计算机可以在处理数据之前先发出下一条数据请求,然后立即去处理数据。如果数据处理比数据采集快,要等待的只有计算机,外部设备可以连续不停地采集数据。如果计算机同时连接多台输入设备,可以轮流向各台设备发出数据请求,并随时处理每台设备发来的数据,整个系统可以保持连续高速运转。编程的关键是把数据索取代码和数据处理代码分别归属两个不同的线程。数据处理代码调用一个数据请求异步函数,然后径自处理手头的数据。待下一组数据到来后,数据处理线程将收到通知,结束 wait 状态,发出下一条数据请求,然后继续处理数据。
            异步调用时,调用方不等被调方返回结果就转身离去,因此必须有一种机制让被调方有了结果时能通知调用方。在同一进程中有很多手段可以利用,笔者常用的手段是回调、event 对象和消息。
            回调:
    回调方式很简单:调用异步函数时在参数中放入一个函数地址,异步函数保存此地址,待有了结果后回调此函数便可以向调用方发出通知。如果把异步函数包装进一个对象中,可以用事件取代回调函数地址,通过事件处理例程向调用方发通知。
      event : event 是 Windows 系统提供的一个常用同步对象,以在异步处理中对齐不同线程之间的步点。如果调用方暂时无事可做,可以调用 wait 函数等在那里,此时 event 处于 nonsignaled 状态。当被调方出来结果之后,把 event 对象置于 signaled 状态,wait 函数便自动结束等待,使调用方重新动作起来,从被调方取出处理结果。这种方式比回调方式要复杂一些,速度也相对较慢,但有很大的灵活性,可以搞出很多花样以适应比较复杂的处理系统。

            消息:借助 Windows 消息发通知是个不错的选择,既简单又安全。程序中定义一个用户消息,并由调用方准备好消息处理例程。被调方出来结果之后立即向调用方发送此消息,并通过 WParam 和 LParam 这两个参数传送结果。消息总是与窗口 handle 关联,因此调用方必须借助一个窗口才能接收消息,这是其不方便之处。另外,通过消息联络会影响速度,需要高速处理时回调方式更有优势。
            如果调用方和被调方分属两个不同的进程,由于内存空间的隔阂,一般是采用 Windows 消息发通知比较简单可靠,被调方可以借助消息本身向调用方传送数据。event 对象也可以通过名称在不同进程间共享,但只能发通知,本身无法传送数据,需要借助 Windows 消息和 FileMapping 等内存共享手段或借助 MailSlot 和 Pipe 等通信手段。
            异步调用原理并不复杂,但实际使用时容易出莫名其妙的问题,特别是不同线程共享代码或共享数据时容易出问题,编程时需要时时注意是否存在这样的共享,并通过各种状态标志避免冲突。Windows 系统提供的 mutex 对象用在这里特别方便。mutex 同一时刻只能有一个管辖者。一个线程放弃管辖权后,另一线程才能接管。当某线程执行到敏感区之前先接管 mutex,使其他线程被 wait 函数堵在身后;脱离敏感区之后立即放弃管辖权,使 wait 函数结束等待,另一个线程便有机会光临此敏感区。这样就可以有效避免多个线程进入同一敏感区。
            由于异步调用容易出问题,要设计一个安全高效的编程方案需要比较多的设计经验,所以最好不要滥用异步调用。同步调用毕竟让人更舒服些:不管程序走到哪里,只要死盯着移动点就能心中有数,不至于象异步调用那样,总有一种四面受敌、惶惶不安的感觉。必要时甚至可以把异步函数转换为同步函数。方法很简单:调用异步函数后马上调用 wait 函数等在那里,待异步函数返回结果后再继续往下走。

     

    3.如何写一个异步函数

            我们平常编程写的函数 几乎都是同步调用函数,那么我们如何写一个异步执行的函数呢?!我想这个问题也许是哪些比较喜欢专研的程序员或者具有专研精神的人士回提出的问题吧!我们很多人已经习惯了windows系统提供的一些异步机制,使用这些异步机制我们很快的就能实现一些异步操作甚至可以很容易的实现一个异步执行的函数;但是我们研究过实现一个“异步函数”的本质吗?!

            在单线程的系统中,所以的指令执行都是顺序执行的,这就暗示了如果一个函数A中调用了函数B,则A必须等到B执行后才能继续执行A中剩下的代码。

            在多线程中,如果我们有一个threadA线程,在该线程中调用了一个函数C,而该C函数我们想将它实现成异步执行的,而异步执行必须要有多线程支持;如果我们在Windows中编写程序,创建一个线程是很简单只要使用

    HANDLE WINAPI CreateThread(

                                                      __in_opt LPSECURITY_ATTRIBUTES lpThreadAttributes,

                                                      __in SIZE_T dwStackSize,

                                                      __in LPTHREAD_START_ROUTINE lpStartAddress,

                                                      __in_opt LPVOID lpParameter,

                                                      __in DWORD dwCreationFlags,

                                                      __out_opt LPDWORD lpThreadId );

    函数就可以创建一个线程。

     

    那么我们按如下方式可以实现一个异步的FuncC函数:

    (1)先把你要异步完成的工作单独写成要给函数,如

    DWORD   WINAPI  AsyncronousThread(

    LPVOID   lpParameter       //   thread   data   
            )

    {

    .....

    }

    (2)在函数FuncC中使用CreateThtread函数将(1)中的函数创建一成一个线程,然后直接返回

    void FuncC(void)

    {

     .....

    CreateThread(....,AsyncronousThread,...);

    return;

    }

    当然,写一个异步函数的方法很多,但是一个本质不会变,就是必须要依据多线程才能实现。

     

    4.回调与异步机制

            回调函数是应用程序提供给Windows系统DLL或其它DLL调用的函数,一般用于截获消息、获取系统信息或处理异步事件。应用程序把回调函数的地址指针告诉DLL,而DLL在适当的时候会调用该函数。回调函数必须遵守事先规定好的参数格式和传递方式,否则DLL一调用它就会引起程序或系统的崩溃。通常情况下,回调函数采用标准WindowsAPI的调用方式,即__stdcall,当然,DLL编制者可以自已定义调用方式,但客户程序也必须遵守相同的规定。在__stdcall方式下,函数的参数按从右到左的顺序压入堆栈,除了明确指明是指针或引用外,参数都按值传递,函数返回之前自己负责把参数从堆栈中弹出。

    程序在调用一个函数(function)时(通常指api).相当于程序(program)呼叫(Call)了一个函数(function)关系表示如下:
                    call(调用)
       program  --------------------→    dll 

    程序在调用一个函数时,将自己的函数的地址作为参数传递给程序调用的函数时(那么这个自己的函数称回调函数).需要回调函数的 DLL 函数往往是一些必须重复执行某些操作的函数.关系表示如下:

                   call(调用)
       program  --------------------→    dll
          ↑                               ¦
          ¦_______________________________¦
                  callback(回调)

            当你调用的函数在传递返回值给回调函数时,你就可以利用回调函数来处理或完成一定的操作。至于如何定义自己的回调函数,跟具体使用的API函数有关,很多不同类别的回调函数有各种各样的参数,有关这些参数的描述一般在帮助中有说明回调函数的参数和返回值等.其实简单说回调函数就是你所写的函数满足一定条件后,被DLL调用!

            Windows 系统还包含着另一种更为广泛的回调机制,即消息机制。消息本是 Windows 的基本控制手段,是一种变相的函数调用。发送消息的目的是通知收方运行一段预先准备好的代码,相当于调用一个函数。消息所附带的 WParam 和 LParam 相当于函数的参数,应用程序可以主动发送消息,更多情况下是坐等 Windows 发送消息。一旦消息进入所属消息队列,便检感兴趣的那些,跳转去执行相应的消息处理代码。操作系统本是为应用程序服务,由应用程序来调用。而应用程序一旦启动,却要反过来等待操作系统的调用。这分明也是一种回调,或者说是一种广义回调。其实,应用程序之间也可以形成这种回调。假如进程 B 收到进程 A 发来的消息,启动了一段代码,其中又向进程 A 发送消息,这就形成了回调。这种回调比较隐蔽,弄不好会搞成递归调用,若缺少终止条件,将会循环不已,直至把程序搞垮。利用消息也可以构成狭义回调。把回调函数地址换成窗口 handle。如此,当需要比较数据大小时,不是去调用回调函数,而是借 API 函数 SendMessage 向指定窗口发送消息。收到消息方负责比较数据大小,把比较结果通过消息本身的返回值传给消息发送方。所实现的功能与回调函数并无不同。当然,此例中改为消息纯属画蛇添脚,反倒把程序搞得很慢。但其他情况下并非总是如此,特别是需要异步调用时,发送消息是一种不错的选择。假如回调函数中包含文件处理之类的低速处理,调用方等不得,需要把同步调用改为异步调用,去启动一个单独的线程,然后马上执行后续代码,其余的事让线程慢慢去做。一个替代办法是借 API 函数 PostMessage 发送一个异步消息,然后立即执行后续代码。这要比自己搞个线程省事许多,而且更安全。

            回调用于层间协作,上层将本层函数安装在下层,这个函数就是回调,而下层在一定条件下触发回调,例如作为一个驱动,是一个底层,他在收到一个数据时,除了完成本层的处理工作外,还将进行回调,将这个数据交给上层应用层来做进一步处理,这在分层的数据通信中很普遍。其实回调和API非常接近,他们的共性都是跨层调用的函数。但区别是API是低层提供给高层的调用,一般这个函数对高层都是已知的;而回调正好相反,他是高层提供给底层的调用,对于低层他是未知的,必须由高层进行安装,这个安装函数其实就是一个低层提供的API,安装后低层不知道这个回调的名字,但它通过一个函数指针来保存这个回调,在需要调用时,只需引用这个函数指针和相关的参数指针。    其实:回调就是该函数写在高层,低层通过一个函数指针保存这个函数,在某个事件的触发下,低层通过该函数指针调用高层那个函数。

     

     

    1. 同步函数与异步函数

            什么是同步函数?

            什么是异步函数?

            它们在线程中执行时会对线程有何影响?

            一个线程如何与一个异步执行的函数进行同步?

    依据微软的MSDN上的解说:

    (1)   同步函数:当一个函数是同步执行时,那么当该函数被调用时不会立即返回,直到该函数所要做的事情全都做完了才返回。

    (2)   异步函数:如果一个异步函数被调用时,该函数会立即返回尽管该函数规定的操作任务还没有完成。

    (3) 在一个线程中分别调用上述两种函数会对调用线程有何影响呢?

            当一个线程调用一个同步函数时(例如:该函数用于完成写文件任务),如果该函数没有立即完成规定的操作,则该操作会导致该调用线程的挂起将CPU的使用权交给系统,让系统分配给其他线程使用),直到该同步函数规定的操作完成才返回,最终才能导致该调用线程被重新调度。

             当一个线程调用的是一个异步函数(例如:该函数用于完成写文件任务),该函数会立即返回尽管其规定的任务还没有完成,这样线程就会执行异步函数的下一条语句,而不会被挂起。那么该异步函数所规定的工作是如何被完成的呢?当然是通过另外一个线程完成的了啊;那么新的线程是哪里来的呢?可能是在异步函数中新创建的一个线程也可能是系统中已经准备好的线程

    (4)一个调用了异步函数的线程如何与异步函数的执行结果同步呢?

            为了解决该问题,调用线程需要使用“等待函数”来确定该异步函数何时完成了规定的任务。因此在线程调用异步函数之后立即调用一个“等待函数”挂起调用线程,一直等到异步函数执行完其所有的操作之后,再执行线程中的下一条指令。

    我们是否已经发现了一个有趣的地方呢?!就是我们可以使用等待函数将一个异步执行的函数封装成一个同步函数。

     

    2.同步调用与异步调用

            操作系统发展到今天已经十分精巧,线程就是其中一个杰作。操作系统把 CPU 处理时间划分成许多短暂时间片,在时间 T1 执行一个线程的指令,到时间 T2 又执行下一线程的指令,各线程轮流执行,结果好象是所有线程在并肩前进。这样,编程时可以创建多个线程,在同一期间执行,各线程可以“并行”完成不同的任务。
            在单线程方式下,计算机是一台严格意义上的冯·诺依曼式机器,一段代码调用另一段代码时,只能采用同步调用,必须等待这段代码执行完返回结果后,调用方才能继续往下执行。有了多线程的支持,可以采用异步调用,调用方和被调方可以属于两个不同的线程,调用方启动被调方线程后,不等对方返回结果就继续执行后续代码。被调方执行完毕后,通过某种手段通知调用方:结果已经出来,请酌情处理。
            计算机中有些处理比较耗时。调用这种处理代码时,调用方如果站在那里苦苦等待,会严重影响程序性能。例如,某个程序启动后如果需要打开文件读出其中的数据,再根据这些数据进行一系列初始化处理,程序主窗口将迟迟不能显示,让用户感到这个程序怎么等半天也不出来,太差劲了。借助异步调用可以把问题轻松化解:把整个初始化处理放进一个单独线程,主线程启动此线程后接着往下走,让主窗口瞬间显示出来。等用户盯着窗口犯呆时,初始化处理就在背后悄悄完成了。程序开始稳定运行以后,还可以继续使用这种技巧改善人机交互的瞬时反应。用户点击鼠标时,所激发的操作如果较费时,再点击鼠标将不会立即反应,整个程序显得很沉重。借助异步调用处理费时的操作,让主线程随时恭候下一条消息,用户点击鼠标时感到轻松快捷,肯定会对软件产生好感。
            异步调用用来处理从外部输入的数据特别有效。假如计算机需要从一台低速设备索取数据,然后是一段冗长的数据处理过程,采用同步调用显然很不合算:计算机先向外部设备发出请求,然后等待数据输入;而外部设备向计算机发送数据后,也要等待计算机完成数据处理后再发出下一条数据请求。双方都有一段等待期,拉长了整个处理过程。其实,计算机可以在处理数据之前先发出下一条数据请求,然后立即去处理数据。如果数据处理比数据采集快,要等待的只有计算机,外部设备可以连续不停地采集数据。如果计算机同时连接多台输入设备,可以轮流向各台设备发出数据请求,并随时处理每台设备发来的数据,整个系统可以保持连续高速运转。编程的关键是把数据索取代码和数据处理代码分别归属两个不同的线程。数据处理代码调用一个数据请求异步函数,然后径自处理手头的数据。待下一组数据到来后,数据处理线程将收到通知,结束 wait 状态,发出下一条数据请求,然后继续处理数据。
            异步调用时,调用方不等被调方返回结果就转身离去,因此必须有一种机制让被调方有了结果时能通知调用方。在同一进程中有很多手段可以利用,笔者常用的手段是回调、event 对象和消息。
            回调:
    回调方式很简单:调用异步函数时在参数中放入一个函数地址,异步函数保存此地址,待有了结果后回调此函数便可以向调用方发出通知。如果把异步函数包装进一个对象中,可以用事件取代回调函数地址,通过事件处理例程向调用方发通知。
      event : event 是 Windows 系统提供的一个常用同步对象,以在异步处理中对齐不同线程之间的步点。如果调用方暂时无事可做,可以调用 wait 函数等在那里,此时 event 处于 nonsignaled 状态。当被调方出来结果之后,把 event 对象置于 signaled 状态,wait 函数便自动结束等待,使调用方重新动作起来,从被调方取出处理结果。这种方式比回调方式要复杂一些,速度也相对较慢,但有很大的灵活性,可以搞出很多花样以适应比较复杂的处理系统。

            消息:借助 Windows 消息发通知是个不错的选择,既简单又安全。程序中定义一个用户消息,并由调用方准备好消息处理例程。被调方出来结果之后立即向调用方发送此消息,并通过 WParam 和 LParam 这两个参数传送结果。消息总是与窗口 handle 关联,因此调用方必须借助一个窗口才能接收消息,这是其不方便之处。另外,通过消息联络会影响速度,需要高速处理时回调方式更有优势。
            如果调用方和被调方分属两个不同的进程,由于内存空间的隔阂,一般是采用 Windows 消息发通知比较简单可靠,被调方可以借助消息本身向调用方传送数据。event 对象也可以通过名称在不同进程间共享,但只能发通知,本身无法传送数据,需要借助 Windows 消息和 FileMapping 等内存共享手段或借助 MailSlot 和 Pipe 等通信手段。
            异步调用原理并不复杂,但实际使用时容易出莫名其妙的问题,特别是不同线程共享代码或共享数据时容易出问题,编程时需要时时注意是否存在这样的共享,并通过各种状态标志避免冲突。Windows 系统提供的 mutex 对象用在这里特别方便。mutex 同一时刻只能有一个管辖者。一个线程放弃管辖权后,另一线程才能接管。当某线程执行到敏感区之前先接管 mutex,使其他线程被 wait 函数堵在身后;脱离敏感区之后立即放弃管辖权,使 wait 函数结束等待,另一个线程便有机会光临此敏感区。这样就可以有效避免多个线程进入同一敏感区。
            由于异步调用容易出问题,要设计一个安全高效的编程方案需要比较多的设计经验,所以最好不要滥用异步调用。同步调用毕竟让人更舒服些:不管程序走到哪里,只要死盯着移动点就能心中有数,不至于象异步调用那样,总有一种四面受敌、惶惶不安的感觉。必要时甚至可以把异步函数转换为同步函数。方法很简单:调用异步函数后马上调用 wait 函数等在那里,待异步函数返回结果后再继续往下走。

     

    3.如何写一个异步函数

            我们平常编程写的函数 几乎都是同步调用函数,那么我们如何写一个异步执行的函数呢?!我想这个问题也许是哪些比较喜欢专研的程序员或者具有专研精神的人士回提出的问题吧!我们很多人已经习惯了windows系统提供的一些异步机制,使用这些异步机制我们很快的就能实现一些异步操作甚至可以很容易的实现一个异步执行的函数;但是我们研究过实现一个“异步函数”的本质吗?!

            在单线程的系统中,所以的指令执行都是顺序执行的,这就暗示了如果一个函数A中调用了函数B,则A必须等到B执行后才能继续执行A中剩下的代码。

            在多线程中,如果我们有一个threadA线程,在该线程中调用了一个函数C,而该C函数我们想将它实现成异步执行的,而异步执行必须要有多线程支持;如果我们在Windows中编写程序,创建一个线程是很简单只要使用

    HANDLE WINAPI CreateThread(

                                                      __in_opt LPSECURITY_ATTRIBUTES lpThreadAttributes,

                                                      __in SIZE_T dwStackSize,

                                                      __in LPTHREAD_START_ROUTINE lpStartAddress,

                                                      __in_opt LPVOID lpParameter,

                                                      __in DWORD dwCreationFlags,

                                                      __out_opt LPDWORD lpThreadId );

    函数就可以创建一个线程。

     

    那么我们按如下方式可以实现一个异步的FuncC函数:

    (1)先把你要异步完成的工作单独写成要给函数,如

    DWORD   WINAPI  AsyncronousThread(

    LPVOID   lpParameter       //   thread   data   
            )

    {

    .....

    }

    (2)在函数FuncC中使用CreateThtread函数将(1)中的函数创建一成一个线程,然后直接返回

    void FuncC(void)

    {

     .....

    CreateThread(....,AsyncronousThread,...);

    return;

    }

    当然,写一个异步函数的方法很多,但是一个本质不会变,就是必须要依据多线程才能实现。

     

    4.回调与异步机制

            回调函数是应用程序提供给Windows系统DLL或其它DLL调用的函数,一般用于截获消息、获取系统信息或处理异步事件。应用程序把回调函数的地址指针告诉DLL,而DLL在适当的时候会调用该函数。回调函数必须遵守事先规定好的参数格式和传递方式,否则DLL一调用它就会引起程序或系统的崩溃。通常情况下,回调函数采用标准WindowsAPI的调用方式,即__stdcall,当然,DLL编制者可以自已定义调用方式,但客户程序也必须遵守相同的规定。在__stdcall方式下,函数的参数按从右到左的顺序压入堆栈,除了明确指明是指针或引用外,参数都按值传递,函数返回之前自己负责把参数从堆栈中弹出。

    程序在调用一个函数(function)时(通常指api).相当于程序(program)呼叫(Call)了一个函数(function)关系表示如下:
                    call(调用)
       program  --------------------→    dll 

    程序在调用一个函数时,将自己的函数的地址作为参数传递给程序调用的函数时(那么这个自己的函数称回调函数).需要回调函数的 DLL 函数往往是一些必须重复执行某些操作的函数.关系表示如下:

                   call(调用)
       program  --------------------→    dll
          ↑                               ¦
          ¦_______________________________¦
                  callback(回调)

            当你调用的函数在传递返回值给回调函数时,你就可以利用回调函数来处理或完成一定的操作。至于如何定义自己的回调函数,跟具体使用的API函数有关,很多不同类别的回调函数有各种各样的参数,有关这些参数的描述一般在帮助中有说明回调函数的参数和返回值等.其实简单说回调函数就是你所写的函数满足一定条件后,被DLL调用!

            Windows 系统还包含着另一种更为广泛的回调机制,即消息机制。消息本是 Windows 的基本控制手段,是一种变相的函数调用。发送消息的目的是通知收方运行一段预先准备好的代码,相当于调用一个函数。消息所附带的 WParam 和 LParam 相当于函数的参数,应用程序可以主动发送消息,更多情况下是坐等 Windows 发送消息。一旦消息进入所属消息队列,便检感兴趣的那些,跳转去执行相应的消息处理代码。操作系统本是为应用程序服务,由应用程序来调用。而应用程序一旦启动,却要反过来等待操作系统的调用。这分明也是一种回调,或者说是一种广义回调。其实,应用程序之间也可以形成这种回调。假如进程 B 收到进程 A 发来的消息,启动了一段代码,其中又向进程 A 发送消息,这就形成了回调。这种回调比较隐蔽,弄不好会搞成递归调用,若缺少终止条件,将会循环不已,直至把程序搞垮。利用消息也可以构成狭义回调。把回调函数地址换成窗口 handle。如此,当需要比较数据大小时,不是去调用回调函数,而是借 API 函数 SendMessage 向指定窗口发送消息。收到消息方负责比较数据大小,把比较结果通过消息本身的返回值传给消息发送方。所实现的功能与回调函数并无不同。当然,此例中改为消息纯属画蛇添脚,反倒把程序搞得很慢。但其他情况下并非总是如此,特别是需要异步调用时,发送消息是一种不错的选择。假如回调函数中包含文件处理之类的低速处理,调用方等不得,需要把同步调用改为异步调用,去启动一个单独的线程,然后马上执行后续代码,其余的事让线程慢慢去做。一个替代办法是借 API 函数 PostMessage 发送一个异步消息,然后立即执行后续代码。这要比自己搞个线程省事许多,而且更安全。

            回调用于层间协作,上层将本层函数安装在下层,这个函数就是回调,而下层在一定条件下触发回调,例如作为一个驱动,是一个底层,他在收到一个数据时,除了完成本层的处理工作外,还将进行回调,将这个数据交给上层应用层来做进一步处理,这在分层的数据通信中很普遍。其实回调和API非常接近,他们的共性都是跨层调用的函数。但区别是API是低层提供给高层的调用,一般这个函数对高层都是已知的;而回调正好相反,他是高层提供给底层的调用,对于低层他是未知的,必须由高层进行安装,这个安装函数其实就是一个低层提供的API,安装后低层不知道这个回调的名字,但它通过一个函数指针来保存这个回调,在需要调用时,只需引用这个函数指针和相关的参数指针。    其实:回调就是该函数写在高层,低层通过一个函数指针保存这个函数,在某个事件的触发下,低层通过该函数指针调用高层那个函数。

     

     

    展开全文
  • 多线程同步实现原理、同步函数、同步代码块、静态同步函数(关键字synchronized)的详解。 在说明同步函数和同步代码快、静态同步函数之前我们先想想: 什么是线程同步? 多线程为什么需要实现同步? 实现...

    多线程同步实现原理、同步函数、同步代码块、静态同步函数(关键字synchronized)的详解。

    在说明同步函数和同步代码快、静态同步函数之前我们先想想:

          什么是线程同步?

          多线程为什么需要实现同步?

          实现同步的原理是什么?

    举个栗子:需求现在有100章火车票,有两个窗口同时抢火车票(相当于两个线程),请使用多线程模拟抢票效果。
    代码如下:(此段代码非线程安全)。

    class ThreadTrain1 implements  Runnable{
        private int trian1Count = 100;
    
        @Override
        public void run() {
            //为了能够模拟程序一致抢票的过程
            while (trian1Count>0){
                try {
                    Thread.sleep(50);
                } catch (Exception e) {
    
                }
                sale();
            }
        }
        //模拟出出售火车票
        public void sale(){
            System.out.println(Thread.currentThread().getName()+"出售第"+(100-trian1Count+1)+"票");
            trian1Count--;
        }
    }
    public class ThreadDemo {
        public static void main(String[] args) {
            /**
             * 错误模拟:应该是两个窗口同时抢票,这里产生了两个对象,意味着两个对象都有100张票,不是一个对象
             * ThreadTrain1 threadTrain1 = new ThreadTrain1();
             * ThreadTrain1 threadTrain2 = new ThreadTrain1();
             * Thread t1 = new Thread(threadTrain1,"窗口①");
             * Thread t2 = new Thread(threadTrain2,"窗口②");
             * t1.start();
             * t2.start();
             */
            ThreadTrain1 threadTrain1 = new ThreadTrain1();
            Thread t1 = new Thread(threadTrain1,"窗口①");
            Thread t2 = new Thread(threadTrain1,  "窗口②");
            t1.start();
            t2.start();
    
        }
    }

    这段程序的运行结果是(中间有截掉一部分):

    从结果看出,这段程序最终没有实现我们想要的结果,我们会看到卖出的票数为101张(票数总数就只有100张),这种现象叫做“超卖”现象,那问题出在哪里呢?原因是当两个进程同时访问同一个资源的时候,就会出现线程安全的问题,这里的资源相当于票数,两个线程就好比两个售票的窗口,由于每个线程执行的过程是不可控的,所以很可能导致最终的结果与实际上的愿望相违背或者直接导致程序出错。解决多线程的安全问题就需要用synchronized(同步实现)。

    首先,什么叫线程同步?

    线程同步:即当有一个线程在对内存进行操作时,其他线程都不可以对这个内存地址进行操作,直到该线程完成操作, 其他线程才能对该内存地址进行操作,而其他线程又处于等待状态,实现线程同步的方法有很多,临界区对象就是其中一种。

    多线程为什么需要实现同步?

    java允许多线程并发控制,当多个线程同时操作一个可共享的资源变量时(如数据的增删改查), 将会导致数据不准确,相互之间产生冲突,因此加入同步锁以避免在该线程没有完成操作之前,被其他线程的调用, 从而保证了该变量的原子性和准确性。

    实现同步的原理是什么?

    synchronized关键字在应用层的语义是可以把任何一个非null对象作为锁,当synchronized作用在方法上时,锁住的是对象实例(this),作用在静态方法上锁住的就是对象对应的Classs实例,由于Class实例存在于永久代,因此静态方法锁相当于类的一个全局锁,当synchronized作用在一个对象实例上,锁住的就是一个代码块 
    ps:在HotSpot JVM中 锁被称作对象监视器

    当有多个线程同时请求某个对象监视器时,对象监视器会设置几种状态来区分请求的线程:

    Contention List:所有请求锁的线程被首先放置在该竞争队列中
    Entry List:Contention List 中有机会获得锁的线程被放置到Entry List
    Wait Set:调用wait()方法被阻塞的线程被放置到Wait Set中
    OnDeck:任何一个时候只能有一个线程竞争锁 该线程称作OnDeck
    Owner:获得锁的线程成为Owner
    !Owner:释放锁的线程

    转换关系图如下:

    png

    个人对同步synchronized原理简单理解:

    1.首先拿到锁,其他线程已经有cpu执行权,一直排队,等待其他线程释放锁。(没有锁获得cpu执行权也进不去)
    2.锁是在什么时候释放,代码执行完毕或者抛出异常都会被释放掉。
    3.锁被释放后,其他线程开始获得锁进行同步中去。
    4.锁的资源竞争
    5.如果一直没有释放锁,就会产生死锁问题。

    synchronized同步函数:

    public synchronized void synMethod(){
    //方法体
    }

    同步代码块:

    public Object synMethod(Object a1){
        synchronized(a1){
    //一次只能有一个线程进入
      }
    }

    静态同步函数(模仿抢票):

    class ThreadTrain2 implements  Runnable{
        private static int trian1Count = 100;
        Object obj = new Object();
        public boolean flag = true;
    
        @Override
        public void run() {
            //为了能够模拟程序一致抢票的过程
        	if (flag) {
        		//执行同步代码块this锁
        			while (trian1Count>0) {
        				synchronized (ThreadTrain2.class) {
        	    			if (trian1Count>0) {
        	    				try {
        	                        Thread.sleep(50);
        	                    } catch (Exception e) {
        	                    	//TODO
        	                    }
        	        			System.out.println(Thread.currentThread().getName()+"出售第"+(100-trian1Count+1)+"票");
        	        	        trian1Count--;
        	        			}		
        				}		
    				}		
    		}             
               else {
            	   //执行同步函数
    			while (trian1Count>0) {
    				sale();	
    			}
    		}
        }
      
        //模拟出出售火车票
        public static synchronized void sale(){
    //    	synchronized (obj) {
        		if (trian1Count>0) {
        			try {
    					Thread.sleep(50);
    				} catch (Exception e) {
    					// TODO: handle exception
    				}
        			System.out.println(Thread.currentThread().getName()+"出售第"+(100-trian1Count+1)+"票");
        	        trian1Count--;
        			}
    //			}
        }
    }
    public class ThreadDemo2 {
        public static void main(String[] args) throws InterruptedException {
            ThreadTrain2 threadTrain2 = new ThreadTrain2();
            Thread t1 = new Thread(threadTrain2,"窗口①");
            Thread t2 = new Thread(threadTrain2,  "窗口②");
            t1.start();
            Thread.sleep(40);
            threadTrain2.flag=false;
            t2.start();
        }
    }
    

    运行结果:

    解决了多线程的安全问题,分析得出结论:

          1.同步函数使用的是this锁。

          2.一个函数使用同步函数,另一线程使用同步代码块(非this锁)不能实现同步,因为synchronized同步函数使用的是this锁,所以只有同步代码块使用this锁才能实现同步

          3.静态同步函数使用的锁是该函数所属字节码文件对象,可以使用this.getClass()方法获取,也可以用当前 类名.class表示。

    静态方法中没有this对象,静态方法没有所属对象。而任何类在加载的时候都会有字节码类对象,用getClass获取。

     

     

    展开全文
  • Java 同步函数

    2017-08-23 20:13:31
    同步函数 用的锁是this 可以将同步代码块 传入的对象为this 则同步代码块和同步函数调用了同一个锁 同步函数和同步代码块的区别: 同步函数的锁是固定的this(当前对象) 同步代码块的锁是任意对象 建议...
  • synchronized同步函数

    千次阅读 2017-03-15 18:34:26
    非静态的同步函数的锁对象是this对象,静态的同步函数的锁对象是当前函数所属的类的字节码文件(class对象)  2.同步函数的 锁对象是固定的,不能由你来指定。  推荐使用:同步代码块 原因:1.同步代码块的锁...
  • python异步函数中调用同步函数

    千次阅读 2019-08-24 08:44:13
    异步函数调用同步函数 普通函数里面不能await,只有async函数才能await。也就是说,同步函数执行的时候会阻塞所有协程。 直接调用 如果同步函数执行的时间很短。就直接调用。 在执行器里跑 如果同步函数执行的时间很...
  • 关于JS同步函数和异步函数

    千次阅读 2019-03-10 10:35:26
    关于JS中的同步函数和异步函数 自己个人的理解: 同步函数:同步函数是顺序执行的,如果这自上而下的过程很长,那么该线程就会一直挂起,知道函数返回,才能让该线程重新调度 异步函数:当该线程遇到譬如此类,就会执行下一...
  • Java——同步函数

    2017-10-06 15:06:20
    同步函数 当函数中的代码全部放在了同步代码块中,那么这个函数就是同步函数 */ //同步函数的锁是this锁,this是一个引用,this指向的对象就是锁 //下面证明一下同步函数的锁就是this //创建两个线程,一个在同步...
  • (1)同步代码块:位置比较灵活,锁对象可以任意对象,但必须是同一对象。格式:  synchronized(对象) { //任意对象都可以。... (2)同步函数:声明方法时加synchronized关键字,同步函数使用的锁...
  • CUDA 同步函数

    千次阅读 2017-09-12 14:54:57
    这里主要区别三个同步函数:cudaStreamSynchronize、CudaDeviceSynchronize 和 cudaThreadSynchronize。在文档中,这三个函数叫做barriers,只有满足一定的条件后,才能通过barriers向后执行。三者的区别如下: ...
  • 同步代码块和同步函数之间同步

    千次阅读 2012-08-17 19:29:31
    * 开始两个线程,一个线程调用同步代码块,另外一个调用同步函数,同步代码块和同步函数的代码一模一样,所以 * 同步代码块和同步函数两者之间也必须同步,即是不能存在一个线程调用同步代码块的同时另外一个线程...
  • 本文详细介绍了Java线程同步的概念以及常用方法,重点介绍了同步块和同步函数
  • //首先是对多线程this锁的证明,这里是看大牛博客的然后自己理解了的学习笔记,这里模拟两个窗口进行车票的售卖,当两个窗口对...//如何证明:对于两个线程,同时启动,一个线程使用同步函数,另一个使用同步代码块...
  • 文章出处:...   1. 同步函数与异步函数  什么是同步函数?  什么是异步函数?  它们在线程中执行时会对线程有何影响?  一个线程如何与一个异步执行的函数进行同步? 依据微软的MSDN上的解说
  • (1)同步代码块可以绑定任意对象,而同步函数只能绑定该类对象this,static同步函数只能绑定字节码类名.class(2)如果多个线程使用同一个锁的话,那么两者均可以使用,如果存在多个锁的话,只能使用同步代码块...
  • 同步函数使用的是哪一个锁呢?函数需要被对象调用,那么函数都有一个所属对象引用,就是this。。所以同步函数使用的锁是this。代码:class Demo implements Runnable { private int t=200; Object obj = new ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 22,640
精华内容 9,056
关键字:

同步函数