线程同步 订阅
线程同步:即当有一个线程在对内存进行操作时,其他线程都不可以对这个内存地址进行操作,直到该线程完成操作, 其他线程才能对该内存地址进行操作,而其他线程又处于等待状态,实现线程同步的方法有很多,临界区对象就是其中一种。 展开全文
线程同步:即当有一个线程在对内存进行操作时,其他线程都不可以对这个内存地址进行操作,直到该线程完成操作, 其他线程才能对该内存地址进行操作,而其他线程又处于等待状态,实现线程同步的方法有很多,临界区对象就是其中一种。
信息
外文名
thread synchronization
类    型
理论
定    义
协同步调,按预定的先后次序进行
中文名
线程同步
应    用
物理
线程同步简介
在一般情况下,创建一个线程是不能提高程序的执行效率的,所以要创建多个线程。但是多个线程同时运行的时候可能调用线程函数,在多个线程同时对同一个内存地址进行写入,由于CPU时间调度上的问题,写入数据会被多次的覆盖,所以就要使线程同步。同步就是协同步调,按预定的先后次序进行运行。如:你说完,我再说。“同”字从字面上容易理解为一起动作其实不是,“同”字应是指协同、协助、互相配合。如进程、线程同步,可理解为进程或线程A和B一块配合,A执行到一定程度时要依靠B的某个结果,于是停下来,示意B运行;B依言执行,再将结果给A;A再继续操作。所谓同步,就是在发出一个功能调用时,在没有得到结果之前,该调用就不返回,同时其它线程也不能调用这个方法。按照这个定义,其实绝大多数函数都是同步调用(例如sin, isdigit等)。但是一般而言,我们在说同步、异步的时候,特指那些需要其他部件协作或者需要一定时间完成的任务。例如Window API函数SendMessage。该函数发送一个消息给某个窗口,在对方处理完消息之前,这个函数不返回。当对方处理完毕以后,该函数才把消息处理函数所返回的LRESULT值返回给调用者。在多线程编程里面,一些敏感数据不允许被多个线程同时访问,此时就使用同步访问技术,保证数据在任何时刻,最多有一个线程访问,以保证数据的完整性。
收起全文
精华内容
下载资源
问答
  • 线程同步

    千次阅读 2016-05-14 15:20:51
    java多线程同步有5中方法: 1)同步方法 : 即有synchronized关键字修饰的方法。 由于java的每个对象都有一个内置锁,当用此关键字修饰方法时, 内置锁会保护整个方法。在调用该方法前,需要获得内置锁,否则...

    java多线程同步有5中方法:
    1)同步方法 :
    即有synchronized关键字修饰的方法。
    由于java的每个对象都有一个内置锁,当用此关键字修饰方法时,
    内置锁会保护整个方法。在调用该方法前,需要获得内置锁,否则就处于阻塞状态。
    2)同步代码块
    即有synchronized关键字修饰的语句块。
    被该关键字修饰的语句块会自动被加上内置锁,从而实现同步
    3)使用特殊域变量(volatile)实现线程同步
    a.volatile关键字为域变量的访问提供了一种免锁机制,
    b.使用volatile修饰域相当于告诉虚拟机该域可能会被其他线程更新,
    c.因此每次使用该域就要重新计算,而不是使用寄存器中的值
    d.volatile不会提供任何原子操作,它也不能用来修饰final类型的变量
    4)使用重入锁实现线程同步
    在JavaSE5.0中新增了一个java.util.concurrent包来支持同步。
    ReentrantLock类是可重入、互斥、实现了Lock接口的锁,
    它与使用synchronized方法和快具有相同的基本行为和语义,并且扩展了其能力
    ReentrantLock类的常用方法有:
    ReentrantLock() : 创建一个ReentrantLock实例
    lock() : 获得锁
    unlock() : 释放锁
    5)使用局部变量实现线程同步
    如果使用ThreadLocal管理变量,则每一个使用该变量的线程都获得该变量的副本,
    副本之间相互独立,这样每一个线程都可以随意修改自己的变量副本,而不会对其他线程产生影响。
    ThreadLocal 类的常用方法:
    ThreadLocal() : 创建一个线程本地变量
    get() : 返回此线程局部变量的当前线程副本中的值
    initialValue() : 返回此线程局部变量的当前线程的”初始值”
    set(T value) : 将此线程局部变量的当前线程副本中的值设置为value
    (注:5种方法具体实现将在后面的博客中给出)

    展开全文
  • Java多线程04_线程同步问题

    万次阅读 2020-08-30 16:25:59
    Java多线程04_线程同步问题 关键字 synchronized 可以保证在同一时刻,只有一个线程可以执行某个方法或某个代码块 案例1:不安全的买票 (多个线程访问同一个对象的同一个方法): public class ByTicket { public ...

    Java多线程04_线程同步问题

    关键字 synchronized 可以保证在同一时刻,只有一个线程可以执行某个方法或某个代码块


    案例1:不安全的买票 (多个线程访问同一个对象的同一个方法):

    public class ByTicket {
    	public static void main(String[] args) {
    		BuyThread buyThread = new BuyThread();
    		new Thread(buyThread,"zhangsan").start();
    		new Thread(buyThread,"lisi").start();
    		new Thread(buyThread,"wangwu").start();
    	}
    }
    
    class BuyThread implements Runnable{
    	
    	private int ticketNums = 10;
    	boolean flag = true;
    
    	@Override
    	public void run() {
    		while(flag) {
    			try {
    				buy();
    			} catch (InterruptedException e) {
    				e.printStackTrace();
    			}
    		}
    	}
    	
    	private void buy() throws InterruptedException {
    		if(ticketNums<=0) {
    			flag = false;
    			return;
    		}
    		
    		Thread.sleep(200);
    		
    		System.out.println(Thread.currentThread().getName()+"买到了"+ ticketNums--);
    	}
    	
    }
    
    wangwu买到了10
    zhangsan买到了8
    lisi买到了9
    lisi买到了7
    wangwu买到了7
    zhangsan买到了7
    zhangsan买到了6
    lisi买到了4
    wangwu买到了5
    lisi买到了3
    wangwu买到了3
    zhangsan买到了2
    wangwu买到了1
    lisi买到了0
    zhangsan买到了-1
    

    解决办法:在 buy() 方法上增加 synchronized 修饰,使其成为同步方法

    private synchronized void buy() throws InterruptedException {
    	
    }
    
    zhangsan买到了10
    zhangsan买到了9
    zhangsan买到了8
    wangwu买到了7
    wangwu买到了6
    wangwu买到了5
    lisi买到了4
    lisi买到了3
    lisi买到了2
    lisi买到了1
    

    案例2:不安全的取钱(多个线程操作同一个对象account):

    public class Bank {
    	public static void main(String[] args) {
    		Account account = new Account(100,"存款");
    		DrawMoney blu = new DrawMoney(account,20,"BLU");
    		DrawMoney gf = new DrawMoney(account,100,"gf");
    		
    		blu.start();
    		gf.start();
    	}
    }
    
    class Account{
    	int money;
    	String name;
    	
    	public Account(int money, String name) {
    		super();
    		this.money = money;
    		this.name = name;
    	}
    	
    }
    
    class DrawMoney extends Thread{
    	Account account;
    	int draw;
    	int nowmoney;
    	
    	public DrawMoney(Account account, int draw, String name) {
    		super(name);
    		this.account = account;
    		this.draw = draw;
    		this.nowmoney = nowmoney;
    	}
    	
    	@Override
    	public void run() {
    		
    		try {
    			Thread.sleep(1000);
    		} catch (InterruptedException e) {
    			e.printStackTrace();
    		}
    		
    		if(account.money-draw<0) {
    			System.out.println(Thread.currentThread().getName()+"账户余额不足");
    			return;
    		}
    		
    		account.money = account.money - draw;
    		nowmoney = nowmoney + draw;
    		System.out.println(account.name+"余额为:"+account.money);
    		System.out.println(Thread.currentThread().getName()+"手里的钱:"+nowmoney);
    	}
    	
    }
    
    存款余额为:80
    gf手里的钱:100
    存款余额为:80
    BLU手里的钱:20
    

    解决办法:使用synchronized (account) {} 对给定对象account加锁,进入同步代码块前要获得给定对象的锁

    @Override
    public void run() {
    	
    	synchronized (account) {
    		try {
    			Thread.sleep(1000);
    		} catch (InterruptedException e) {
    			e.printStackTrace();
    		}
    		
    		if(account.money-draw<0) {
    			System.out.println(Thread.currentThread().getName()+"账户余额不足");
    			return;
    		}
    		
    		account.money = account.money - draw;
    		nowmoney = nowmoney + draw;
    		System.out.println(account.name+"余额为:"+account.money);
    		System.out.println(Thread.currentThread().getName()+"手里的钱:"+nowmoney);
    	}
    
    }
    
    存款余额为:80
    BLU手里的钱:20
    gf账户余额不足
    

    案例3:不安全的集合(原因是两个线程可能会在同一瞬间操作了同一位置):

    public class UnsafeList {
    	
    	public static void main(String[] args) {
    		List<String> list = new ArrayList<String>();
    		for(int i=0;i<10000;i++) {
    			new Thread(()->{
    				list.add(Thread.currentThread().getName());
    			}).start();
    		}
    		System.out.println(list.size());
    	}
    }
    
    9998
    

    解决办法:锁住 list 对象

    import java.util.ArrayList;
    import java.util.List;
    
    public class UnsafeList {
    	
    	public static void main(String[] args) {
    		List<String> list = new ArrayList<String>();
    		for(int i=0;i<10000;i++) {
    			new Thread(()->{
    				synchronized (list) {
    					list.add(Thread.currentThread().getName());
    				}
    			}).start();
    		}
    		System.out.println(list.size());
    	}
    }
    
    10000
    
    展开全文
  • Java线程(二):线程同步synchronized和volatile

    万次阅读 多人点赞 2012-04-04 10:49:28
    要说明线程同步问题首先要说明Java线程的两个特性,可见性和有序性。多个线程之间是不能直接传递数据交互的,它们之间的交互只能通过共享变量来实现。拿上篇博文中的例子来说明,在多个线程之间共享了Count类的一个...

            要说明线程同步问题首先要说明Java线程的两个特性,可见性和有序性。多个线程之间是不能直接传递数据交互的,它们之间的交互只能通过共享变量来实现。拿上篇博文中的例子来说明,在多个线程之间共享了Count类的一个对象,这个对象是被创建在主内存(堆内存)中,每个线程都有自己的工作内存(线程栈),工作内存存储了主内存Count对象的一个副本,当线程操作Count对象时,首先从主内存复制Count对象到工作内存中,然后执行代码count.increment(),改变了num值,最后用工作内存Count刷新主内存Count。当一个对象在多个内存中都存在副本时,如果一个内存修改了共享变量,其它线程也应该能够看到被修改后的值,此为可见性。多个线程执行时,CPU对线程的调度是随机的,我们不知道当前程序被执行到哪步就切换到了下一个线程,一个最经典的例子就是银行汇款问题,一个银行账户存款100,这时一个人从该账户取10元,同时另一个人向该账户汇10元,那么余额应该还是100。那么此时可能发生这种情况,A线程负责取款,B线程负责汇款,A从主内存读到100,B从主内存读到100,A执行减10操作,并将数据刷新到主内存,这时主内存数据100-10=90,而B内存执行加10操作,并将数据刷新到主内存,最后主内存数据100+10=110,显然这是一个严重的问题,我们要保证A线程和B线程有序执行,先取款后汇款或者先汇款后取款,此为有序性。本文讲述了JDK5.0之前传统线程的同步方式,更高级的同步方式可参见Java线程(八):锁对象Lock-同步问题更完美的处理方式

            下面同样用代码来展示一下线程同步问题。

            TraditionalThreadSynchronized.java:创建两个线程,执行同一个对象的输出方法。

    public class TraditionalThreadSynchronized {
    	public static void main(String[] args) {
    		final Outputter output = new Outputter();
    		new Thread() {
    			public void run() {
    				output.output("zhangsan");
    			}
    		}.start();		
    		new Thread() {
    			public void run() {
    				output.output("lisi");
    			}
    		}.start();
    	}
    }
    class Outputter {
    	public void output(String name) {
    		// TODO 为了保证对name的输出不是一个原子操作,这里逐个输出name的每个字符
    		for(int i = 0; i < name.length(); i++) {
    			System.out.print(name.charAt(i));
    			// Thread.sleep(10);
    		}
    	}
    }

            运行结果:

    zhlainsigsan

            显然输出的字符串被打乱了,我们期望的输出结果是zhangsanlisi,这就是线程同步问题,我们希望output方法被一个线程完整的执行完之后再切换到下一个线程,Java中使用synchronized保证一段代码在多线程执行时是互斥的,有两种用法:

            1. 使用synchronized将需要互斥的代码包含起来,并上一把锁。

    {
        synchronized (this) {
            for(int i = 0; i < name.length(); i++) {
                System.out.print(name.charAt(i));
            }
        }
    }
    

            这把锁必须是需要互斥的多个线程间的共享对象,像下面的代码是没有意义的。

    {
        Object lock = new Object();
        synchronized (lock) {
            for(int i = 0; i < name.length(); i++) {
                System.out.print(name.charAt(i));
            }
        }
    }
    

            每次进入output方法都会创建一个新的lock,这个锁显然每个线程都会创建,没有意义。

            2. 将synchronized加在需要互斥的方法上。

    public synchronized void output(String name) {
        // TODO 线程输出方法
        for(int i = 0; i < name.length(); i++) {
            System.out.print(name.charAt(i));
        }
    }

            这种方式就相当于用this锁住整个方法内的代码块,如果用synchronized加在静态方法上,就相当于用××××.class锁住整个方法内的代码块。使用synchronized在某些情况下会造成死锁,死锁问题以后会说明。使用synchronized修饰的方法或者代码块可以看成是一个原子操作

            每个锁对象(JLS中叫monitor)都有两个队列,一个是就绪队列,一个是阻塞队列,就绪队列存储了将要获得锁的线程,阻塞队列存储了被阻塞的线程,当一个线程被唤醒(notify)后,才会进入到就绪队列,等待CPU的调度,反之,当一个线程被wait后,就会进入阻塞队列,等待下一次被唤醒,这个涉及到线程间的通信,下一篇博文会说明。看我们的例子,当第一个线程执行输出方法时,获得同步锁,执行输出方法,恰好此时第二个线程也要执行输出方法,但发现同步锁没有被释放,第二个线程就会进入就绪队列,等待锁被释放。一个线程执行互斥代码过程如下:

            1. 获得同步锁;

            2. 清空工作内存;

            3. 从主内存拷贝对象副本到工作内存;

            4. 执行代码(计算或者输出等);

            5. 刷新主内存数据;

            6. 释放同步锁。

            所以,synchronized既保证了多线程的并发有序性,又保证了多线程的内存可见性。

            volatile是第二种Java多线程同步的机制,根据JLS(Java LanguageSpecifications)的说法,一个变量可以被volatile修饰,在这种情况下内存模型(主内存和线程工作内存)确保所有线程可以看到一致的变量值,来看一段代码:

    class Test {
    	static int i = 0, j = 0;
    	static void one() {
    		i++;
    		j++;
    	}
    	static void two() {
    		System.out.println("i=" + i + " j=" + j);
    	}
    }

            一些线程执行one方法,另一些线程执行two方法,two方法有可能打印出j比i大的值,按照之前分析的线程执行过程分析一下:

            1. 将变量i从主内存拷贝到工作内存;

            2. 改变i的值;

            3. 刷新主内存数据;

            4. 将变量j从主内存拷贝到工作内存;

            5. 改变j的值;

            6. 刷新主内存数据;

            这个时候执行two方法的线程先读取了主存i原来的值又读取了j改变后的值,这就导致了程序的输出不是我们预期的结果,要阻止这种不合理的行为的一种方式是在one方法和two方法前面加上synchronized修饰符:

    class Test {
    	static int i = 0, j = 0;
    	static synchronized void one() {
    		i++;
    		j++;
    	}
    	static synchronized void two() {
    		System.out.println("i=" + i + " j=" + j);
    	}
    }

           根据前面的分析,我们可以知道,这时one方法和two方法再也不会并发的执行了,i和j的值在主内存中会一直保持一致,并且two方法输出的也是一致的。另一种同步的机制是在共享变量之前加上volatile:

    class Test {
    	static volatile int i = 0, j = 0;
    	static void one() {
    		i++;
    		j++;
    	}
    	static void two() {
    		System.out.println("i=" + i + " j=" + j);
    	}
    }

           one方法和two方法还会并发的去执行,但是加上volatile可以将共享变量i和j的改变直接响应到主内存中,这样保证了主内存中i和j的值一致性,然而在执行two方法时,在two方法获取到i的值和获取到j的值中间的这段时间,one方法也许被执行了好多次,导致j的值会大于i的值。所以volatile可以保证内存可见性,不能保证并发有序性。

           没有明白JLS中为什么使用两个变量来阐述volatile的工作原理,这样不是很好理解。volatile是一种弱的同步手段,相对于synchronized来说,某些情况下使用,可能效率更高,因为它不是阻塞的,尤其是读操作时,加与不加貌似没有影响,处理写操作的时候,可能消耗的性能更多些。但是volatile和synchronized性能的比较,我也说不太准,多线程本身就是比较玄的东西,依赖于CPU时间分片的调度,JVM更玄,还没有研究过虚拟机,从顶层往底层看往往是比较难看透的。在JDK5.0之前,如果没有参透volatile的使用场景,还是不要使用了,尽量用synchronized来处理同步问题,线程阻塞这玩意简单粗暴。另外volatile和final不能同时修饰一个字段,可以想想为什么。

            本文来自:高爽|Coder,原文地址:http://blog.csdn.net/ghsau/article/details/7424694,转载请注明。

    展开全文
  • 秒杀多线程第六篇 经典线程同步 事件Event

    万次阅读 多人点赞 2012-04-11 09:06:57
    阅读本篇之前推荐阅读以下姊妹篇:《秒杀多线程第四篇 一个经典的多线程同步问题》《秒杀多线程第五篇 经典线程同步关键段CS》 上一篇中使用关键段来解决经典的多线程同步互斥问题,由于关键段的“线程所有权”特性...

    阅读本篇之前推荐阅读以下姊妹篇:

    秒杀多线程第四篇 一个经典的多线程同步问题

    秒杀多线程第五篇 经典线程同步关键段CS

     

    上一篇中使用关键段来解决经典的多线程同步互斥问题,由于关键段的“线程所有权”特性所以关键段只能用于线程的互斥而不能用于同步。本篇介绍用事件Event来尝试解决这个线程同步问题。

    首先介绍下如何使用事件。事件Event实际上是个内核对象,它的使用非常方便。下面列出一些常用的函数。

     

    第一个 CreateEvent

    函数功能:创建事件

    函数原型:

    HANDLECreateEvent(

     LPSECURITY_ATTRIBUTESlpEventAttributes,

     BOOLbManualReset,

     BOOLbInitialState,

     LPCTSTRlpName

    );

    函数说明:

    第一个参数表示安全控制,一般直接传入NULL

    第二个参数确定事件是手动置位还是自动置位,传入TRUE表示手动置位,传入FALSE表示自动置位。如果为自动置位,则对该事件调用WaitForSingleObject()后会自动调用ResetEvent()使事件变成未触发状态。打个小小比方,手动置位事件相当于教室门,教室门一旦打开(被触发),所以有人都可以进入直到老师去关上教室门(事件变成未触发)。自动置位事件就相当于医院里拍X光的房间门,门打开后只能进入一个人,这个人进去后会将门关上,其它人不能进入除非门重新被打开(事件重新被触发)。

    第三个参数表示事件的初始状态,传入TRUR表示已触发。

    第四个参数表示事件的名称,传入NULL表示匿名事件。

     

    第二个 OpenEvent

    函数功能:根据名称获得一个事件句柄。

    函数原型:

    HANDLEOpenEvent(

     DWORDdwDesiredAccess,

     BOOLbInheritHandle,

     LPCTSTRlpName     //名称

    );

    函数说明:

    第一个参数表示访问权限,对事件一般传入EVENT_ALL_ACCESS。详细解释可以查看MSDN文档。

    第二个参数表示事件句柄继承性,一般传入TRUE即可。

    第三个参数表示名称,不同进程中的各线程可以通过名称来确保它们访问同一个事件。

     

    第三个SetEvent

    函数功能:触发事件

    函数原型:BOOLSetEvent(HANDLEhEvent);

    函数说明:每次触发后,必有一个或多个处于等待状态下的线程变成可调度状态。

     

    第四个ResetEvent

    函数功能:将事件设为末触发

    函数原型:BOOLResetEvent(HANDLEhEvent);

     

    最后一个事件的清理与销毁

    由于事件是内核对象,因此使用CloseHandle()就可以完成清理与销毁了。

     

    在经典多线程问题中设置一个事件和一个关键段。用事件处理主线程与子线程的同步,用关键段来处理各子线程间的互斥。详见代码:

    #include <stdio.h>
    #include <process.h>
    #include <windows.h>
    long g_nNum;
    unsigned int __stdcall Fun(void *pPM);
    const int THREAD_NUM = 10;
    //事件与关键段
    HANDLE  g_hThreadEvent;
    CRITICAL_SECTION g_csThreadCode;
    int main()
    {
    	printf("     经典线程同步 事件Event\n");
    	printf(" -- by MoreWindows( http://blog.csdn.net/MoreWindows ) --\n\n");
    	//初始化事件和关键段 自动置位,初始无触发的匿名事件
    	g_hThreadEvent = CreateEvent(NULL, FALSE, FALSE, NULL); 
    	InitializeCriticalSection(&g_csThreadCode);
    
    	HANDLE  handle[THREAD_NUM];	
    	g_nNum = 0;
    	int i = 0;
    	while (i < THREAD_NUM) 
    	{
    		handle[i] = (HANDLE)_beginthreadex(NULL, 0, Fun, &i, 0, NULL);
    		WaitForSingleObject(g_hThreadEvent, INFINITE); //等待事件被触发
    		i++;
    	}
    	WaitForMultipleObjects(THREAD_NUM, handle, TRUE, INFINITE);
    
    	//销毁事件和关键段
    	CloseHandle(g_hThreadEvent);
    	DeleteCriticalSection(&g_csThreadCode);
    	return 0;
    }
    unsigned int __stdcall Fun(void *pPM)
    {
    	int nThreadNum = *(int *)pPM; 
    	SetEvent(g_hThreadEvent); //触发事件
    	
    	Sleep(50);//some work should to do
    	
    	EnterCriticalSection(&g_csThreadCode);
    	g_nNum++;
    	Sleep(0);//some work should to do
    	printf("线程编号为%d  全局资源值为%d\n", nThreadNum, g_nNum); 
    	LeaveCriticalSection(&g_csThreadCode);
    	return 0;
    }

    运行结果如下图:

    可以看出来,经典线线程同步问题已经圆满的解决了——线程编号的输出没有重复,说明主线程与子线程达到了同步。全局资源的输出是递增的,说明各子线程已经互斥的访问和输出该全局资源。

     

    现在我们知道了如何使用事件,但学习就应该要深入的学习,何况微软给事件还提供了PulseEvent()函数,所以接下来再继续深挖下事件Event,看看它还有什么秘密没。

    先来看看这个函数的原形:

    第五个PulseEvent

    函数功能:将事件触发后立即将事件设置为未触发,相当于触发一个事件脉冲。

    函数原型:BOOLPulseEvent(HANDLEhEvent);

    函数说明:这是一个不常用的事件函数,此函数相当于SetEvent()后立即调用ResetEvent();此时情况可以分为两种:

    1.对于手动置位事件,所有正处于等待状态下线程都变成可调度状态。

    2.对于自动置位事件,所有正处于等待状态下线程只有一个变成可调度状态。

    此后事件是末触发的。该函数不稳定,因为无法预知在调用PulseEvent ()时哪些线程正处于等待状态

     

           下面对这个触发一个事件脉冲PulseEvent ()写一个例子,主线程启动7个子线程,其中有5个线程Sleep(10)后对一事件调用等待函数(称为快线程),另有2个线程Sleep(100)后也对该事件调用等待函数(称为慢线程)。主线程启动所有子线程后再Sleep(50)保证有5个快线程都正处于等待状态中。此时若主线程触发一个事件脉冲,那么对于手动置位事件,这5个线程都将顺利执行下去。对于自动置位事件,这5个线程中会有中一个顺利执行下去。而不论手动置位事件还是自动置位事件,那2个慢线程由于Sleep(100)所以会错过事件脉冲,因此慢线程都会进入等待状态而无法顺利执行下去。

    代码如下:

    //使用PluseEvent()函数
    #include <stdio.h>
    #include <conio.h>
    #include <process.h>
    #include <windows.h>
    HANDLE  g_hThreadEvent;
    //快线程
    unsigned int __stdcall FastThreadFun(void *pPM)
    {
    	Sleep(10); //用这个来保证各线程调用等待函数的次序有一定的随机性
    	printf("%s 启动\n", (PSTR)pPM);
    	WaitForSingleObject(g_hThreadEvent, INFINITE);
    	printf("%s 等到事件被触发 顺利结束\n", (PSTR)pPM);
    	return 0;
    }
    //慢线程
    unsigned int __stdcall SlowThreadFun(void *pPM)
    {
    	Sleep(100);
    	printf("%s 启动\n", (PSTR)pPM);
    	WaitForSingleObject(g_hThreadEvent, INFINITE);
    	printf("%s 等到事件被触发 顺利结束\n", (PSTR)pPM);
    	return 0;
    }
    int main()
    {
    	printf("  使用PluseEvent()函数\n");
    	printf(" -- by MoreWindows( http://blog.csdn.net/MoreWindows ) --\n\n");
    
    	BOOL bManualReset = FALSE;
    	//创建事件 第二个参数手动置位TRUE,自动置位FALSE
    	g_hThreadEvent = CreateEvent(NULL, bManualReset, FALSE, NULL);
    	if (bManualReset == TRUE)
    		printf("当前使用手动置位事件\n");
    	else
    		printf("当前使用自动置位事件\n");
    
    	char szFastThreadName[5][30] = {"快线程1000", "快线程1001", "快线程1002", "快线程1003", "快线程1004"};
    	char szSlowThreadName[2][30] = {"慢线程196", "慢线程197"};
    
    	int i;
    	for (i = 0; i < 5; i++)
    		_beginthreadex(NULL, 0, FastThreadFun, szFastThreadName[i], 0, NULL);
    	for (i = 0; i < 2; i++)
    		_beginthreadex(NULL, 0, SlowThreadFun, szSlowThreadName[i], 0, NULL);
    	
    	Sleep(50); //保证快线程已经全部启动
    	printf("现在主线程触发一个事件脉冲 - PulseEvent()\n");
    	PulseEvent(g_hThreadEvent);//调用PulseEvent()就相当于同时调用下面二句
    	//SetEvent(g_hThreadEvent);
    	//ResetEvent(g_hThreadEvent);
    	
    	Sleep(3000); 
    	printf("时间到,主线程结束运行\n");
    	CloseHandle(g_hThreadEvent);
    	return 0;
    }

    自动置位事件,运行结果如下:

    手动置位事件,运行结果如下:

     

     

    最后总结下事件Event

    1.事件是内核对象,事件分为手动置位事件自动置位事件。事件Event内部它包含一个使用计数(所有内核对象都有),一个布尔值表示是手动置位事件还是自动置位事件,另一个布尔值用来表示事件有无触发。

    2.事件可以由SetEvent()来触发,由ResetEvent()来设成未触发。还可以由PulseEvent()来发出一个事件脉冲。

    3.事件可以解决线程间同步问题,因此也能解决互斥问题。

     

    后面二篇《秒杀多线程第七篇 经典线程同步 互斥量Mutex》和《秒杀多线程第八篇 经典线程同步 信号量Semaphore》将介绍如何使用互斥量和信号量来解决这个经典线程同步问题。欢迎大家继续秒杀多线程之旅。

     

    转载请标明出处,原文地址:http://blog.csdn.net/morewindows/article/details/7445233

    如果觉得本文对您有帮助,请点击支持一下,您的支持是我写作最大的动力,谢谢。



     

    展开全文
  • 秒杀多线程第五篇 经典线程同步 关键段CS

    万次阅读 多人点赞 2012-04-11 09:06:40
    上一篇《秒杀多线程第四篇 一个经典的多线程同步问题》提出了一个经典的多线程同步互斥问题,本篇将用关键段CRITICAL_SECTION来尝试解决这个问题。本文首先介绍下如何使用关键段,然后再深层次的分析下关键段的实现...
  • 秒杀多线程第八篇 经典线程同步 信号量Semaphore

    万次阅读 多人点赞 2012-05-03 09:30:00
    阅读本篇之前推荐阅读以下姊妹篇:《秒杀多线程第四篇一个经典的多线程同步问题》《秒杀多线程第五篇经典线程同步关键段CS》《秒杀多线程第六篇经典线程同步事件Event》《秒杀多线程第七篇经典线程同步互斥量Mutex》...
  • (转)Java面试——线程同步volatile与synchronized详解注:本文转载地址http://blog.csdn.net/seu_calvin/article/details/523700680. 前言 面试时很可能遇到...提到线程安全、线程同步,我们经常会想到两个关键字:vo
  • linux中实现线程同步的6种方法

    万次阅读 2020-10-22 16:37:21
    linux线程同步的方法 下面是一个线程不安全的例子: #include<stdio.h> #include<pthread.h> int ticket_num=10000000; void *sell_ticket(void *arg) { while(ticket_num>0) { ticket_num--; }...
  • JAVA多线程——线程同步机制,同步方法和同步块 并发:同一个对象被多个线程同时操作 线程同步:处理多线程问题时,多个线程访问同一个对象,并且某个对象还想修改这个线程。这时候就需要线程同步线程同步其实就是...
  • Linux线程同步

    千次阅读 2020-03-06 09:20:48
    文章目录一、线程同步的概念二、互斥锁1、初始化锁2、阻塞加锁3、非阻塞加锁4、解锁5、销毁锁(此时锁必需unlock状态,否则返回EBUSY)三、示例程序四、版权声明 一、线程同步的概念 线程同步?怎么同步?一起运行?...
  • 秒杀多线程第七篇 经典线程同步 互斥量Mutex

    万次阅读 多人点赞 2012-04-18 09:26:51
    阅读本篇之前推荐阅读以下姊妹篇:《秒杀多线程第四篇一个经典的多线程同步问题》《秒杀多线程第五篇经典线程同步关键段CS》《秒杀多线程第六篇经典线程同步事件Event》 前面介绍了关键段CS、事件Event在经典线程...
  • Python中线程同步与线程锁

    千次阅读 2019-06-09 21:12:02
    文章目录Python中线程同步与线程锁线程同步threading.Event对象threading.Timer定时器,延迟执行threading.Lock锁可重入锁RLockCondition条件锁,等待通知therading.Semaphore信号量threading.BoundedSemaphore有界...
  • Python多线程—线程同步

    千次阅读 2019-03-25 23:05:17
    线程同步的真实意思和字面意思恰好相反。 线程同步的真实意思,其实是“排队”:几个线程之间要排队,一个一个对共享资源进行操作,而不是同时进行操作。 Python threading模块提供了Lock/RLock、Condition、queue...
  • C#多线程——线程同步

    千次阅读 2018-08-25 13:11:53
    一、为什么要线程同步? 多个线程同时使用共享对象会造成很多问题,同步这些线程使得对共享对象的操作能够以正确的顺序执行是非常重要的。 二、实现线程同步的方法: • 使用Mutex类 • 使用SemaphoreSlim类 • ...
  • 线程同步机制

    千次阅读 2019-02-14 17:52:46
    从广义上说,Java平台提供的线程同步机制包括锁、volatile关键字、final关键字、static关键字和一些相关的API,如Object.wait( )/.notify( )等   1、锁的概述和概念: a 线程安全问题的产生: 多个线程并发访问...
  • 线程同步辅助类

    万次阅读 2017-05-17 00:04:55
    前言关于线程的基础知识可以查看《有关线程的相关知识(上)》和《有关线程的相关知识(下)》,线程同步synchronized和Lock可以查看《线程同步synchronized》和《线程同步Lock》,在并发工具类中提供
  • Python 线程同步 线程优先级

    千次阅读 2017-08-01 11:52:34
    线程同步 如果多个线程共同对某个数据修改,则可能出现不可预料的结果,为了保证数据的正确性,需要对多个线程进行同步。 使用Thread对象的Lock和Rlock可以实现简单的线程同步,这两个对象都有acquire方法和release...
  • java多线程 —— 多线程同步

    千次阅读 2020-05-30 20:42:33
    1、什么是多线程同步器? 可以理解为,用于控制多线程之前同步动作的工具。 2、为什么使用多线程同步器? 在实际应用中,我们希望多线程根据某些一些特定的规则执行。因此有了多线程同步器,通过不同的多线程同步...
  • 线程同步与线程安全

    千次阅读 2018-02-09 16:47:32
    1线程同步 同步:多线程访问临界资源时,必须进行同步控制,多进程或者多线程的执行并不完全是绝对的并行运行,又可能主线程需要等待函数线程的某些条件的发生。 多线程的临界资源有全局数据,堆区数据,文件描述...
  • Java线程同步

    千次阅读 2018-01-06 17:56:53
    线程同步 最典型的例子就是银行取钱例子,两个线程同时取钱的时候会出现余额小于0的情况,即并发线程“同时”修改了共享对象的成员变量,为了解决这个问题,Java提供了同步代码块和同步方法。 //同步代码块 ...
  • 线程同步的四种方式

    万次阅读 多人点赞 2018-02-26 13:39:32
    转载地址: http://blog.csdn.net/ebowtang/article/details/29905309一,...线程同步是指多线程通过特定的设置(如互斥量,事件对象,临界区)来控制线程之间的执行顺序(即所谓的同步)也可以说是在线程之间通过...
  • 进程同步和线程同步

    千次阅读 2017-09-17 11:07:46
    怎样同步多个线程或多个进程的活动。为允许在线程或进程间共享数据,同步是必需的。互斥锁和条件变量是同步的基本组成部分。互斥锁和条件变量出自POSIX.1线程标准,它们总是可...多线程同步方法1)互斥锁 互斥锁是最基
  • MFC线程同步

    千次阅读 2017-01-01 11:31:22
    MFC线程同步 *为什么要进行线程同步? 线程是非常好的程序设计方法,线程可以简化程序设计,而且线程也极大的改善了程序性能,但是 ,使用线程要小心,比如多个线程同时使用了共享资源,如果多个线程同时修改了...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 113,218
精华内容 45,287
关键字:

线程同步