`
chaijuntao
  • 浏览: 24786 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

java线程互斥&同步(二)

 
阅读更多

       这一次,来探讨下线程间同步的问题,网上很多小伙伴讲到同步,自然而然会说synchronized,但是synchronized英文翻译的确是“同步的”,但此同步非彼同步,synchronized关键字其实实现的是对于一个共享资源的加锁操作,确保这个共享资源同一时间内只被一个对象(线程)进行操作,这是我们前一节所说的线程间互斥,所以synchronized并非实现了线程间同步。

 

       那我们说的线程间同步的含义是什么呢?更直观的翻译我觉得应该是线程间的协同工作,比方有两个线程任务A和B,当B必须等待A执行完成后才能执行,那么我们说这两个线程是同步的,与之相对的就是A/B同时运行,互补干涉,也即线程的异步。

 

        理解了上面的概念,那我们更深入一步,知道线程间同步是怎么回事,那么java是提供了哪些办法来实现线程间同步呢?

        我们先来看一下下面这些方法:sleep()、wait()、notify()、notifyAll()

        sleep():sleep()是线程类的方法,sleep() 允许指定以毫秒为单位的一段时间作为参数,它使得线程在指定的时间内进入阻塞状态,不能得到CPU 时间,指定的时间一过,线程重新进入可执行状态。说白了 ,也就是把机会给其他线程,但是监控状态依然保持。重要的一点就是 当调用sleep()方法是 不会 释放对象锁的。

        wait():导致线程阻塞,并且该对象上的锁被释放。

        notify():释放对象的wait()方法而阻塞线程(但是也要当得到锁后才可以运行)但是这个释放是随机的,也就是不一定要释放那个线程。(因为调用同一资源的可能不是一个线程或者说是有多个阻塞的线程在等待,但是如果加了synchronized也只有一个线程,也有其他的线程在等待中,也就是阻塞)我们无法预料哪一个线程将会被选择,所以编程时要特别小心,避免因这种不确定性而产生问题。

        notifyall():将把因调用该对象的 wait() 方法而阻塞的所有线程一次性全部解除阻塞。当然,只有获得锁的那一个线程才能进入可执行状态。

        概念性的东西,就不深入解释了,不明白的同学可以多查看相关资料。

        上面几个方法,跟我们所讲的线程同步关系最密切的是wait、notify、notifyall这三个方法,有了它们就可以很方便地控制线程的协同工作了。

 

        注意:Obj.wait(),与Obj.notify()必须要与synchronized(Obj)一起使用,也就是wait,与notify是针对已经获取了Obj锁进行操作,从语法角度来说就是Obj.wait(),Obj.notify必须在synchronized(Obj){...}语句块内,否则会报java.lang.IllegalMonitorStateException错误。从功能上来说wait就是说线程在获取对象锁后,主动释放对象锁,同时本线程休眠。直到有其它线程调用对象的notify()唤醒该线程,才能继续获取对象锁,并继续执行。相应的notify()就是对对象锁的唤醒操作。但有一点需要注意的是notify()调用后,并不是马上就释放对象锁的,而是在相应的synchronized(){}语句块执行结束,自动释放锁后,JVM会在wait()对象锁的线程中随机选取一线程,赋予其对象锁,唤醒线程,继续执行。这样就提供了在线程间同步、唤醒的操作。Thread.sleep()与Object.wait()二者都可以暂停当前线程,释放CPU控制权,主要的区别在于Object.wait()在释放CPU同时,释放了对象锁的控制。

         

        下面我们来看一个经典的多线程同步问题,三线程打印ABC的问题:建立三个线程,A线程打印10次A,B线程打印10次B,C线程打印10次C,要求线程同时运行,交替打印10次ABC。这个问题用Object的wait(),notify()就可以很方便的解决。

 

package Tread_TB;

public class ThreadPrint implements Runnable{

	private String name;
	private Object prev;
	private Object thisone;
	
	public ThreadPrint(String name,Object prev,Object thisone) {
		// TODO Auto-generated method stub
		this.name = name;
		this.prev = prev;
		this.thisone = thisone;
	}
	
	@Override
	public void run() {
		// TODO Auto-generated method stub
		int count = 10;
		while(count > 0){
			synchronized (prev) {
				synchronized (thisone) {
					System.out.print(name);
					count--;
					thisone.notify();
				}
				try {
					prev.wait();
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}
	}

	public static void main(String[] args) {
		Object a = new Object();
		Object b = new Object();
		Object c = new Object();
		ThreadPrint A = new ThreadPrint("A",c,a);
		ThreadPrint B = new ThreadPrint("B",a,b);
		ThreadPrint C = new ThreadPrint("C",b,c);
		
		new Thread(A).start();
		new Thread(B).start();
		new Thread(C).start();
	}
		
}

 

运行结果:ABCABCABCABCABCABCABCABCABCABC

达到了我们想要的效果,但是细心的同学可能会发现,多执行几次,有时候会出现ACBACBACB..这样的结果,也即C和B顺序颠倒,那我们仔细分析下代码,其实不难发现,当A任务在执行过程中,B和C任务都启动了,当A执行到thisone.notify();之前,B一直在synchronized (prev) {处等待a对象的锁,而此时C却在synchronized (prev) {处优先得到了b对象的锁,所以导致C先于B开始运行,之后释放c与b的对象锁,此时B拿到锁开始执行,所以就出现了ACBACBACB的结果,那么可以通过人为错开任务运行时间来防止C抢在B之前运行,代码如下:

package Tread_TB;

public class ThreadPrint implements Runnable{

	private String name;
	private Object prev;
	private Object thisone;
	
	public ThreadPrint(String name,Object prev,Object thisone) {
		// TODO Auto-generated method stub
		this.name = name;
		this.prev = prev;
		this.thisone = thisone;
	}
	
	@Override
	public void run() {
		// TODO Auto-generated method stub
		int count = 10;
		while(count > 0){
			synchronized (prev) {
				synchronized (thisone) {
					System.out.print(name);
					count--;
					thisone.notify();
				}
				try {
					prev.wait();
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}
	}

	public static void main(String[] args) {
		Object a = new Object();
		Object b = new Object();
		Object c = new Object();
		ThreadPrint A = new ThreadPrint("A",c,a);
		ThreadPrint B = new ThreadPrint("B",a,b);
		ThreadPrint C = new ThreadPrint("C",b,c);
		
		try {
			new Thread(A).start();
			Thread.sleep(1000);
			new Thread(B).start();
			Thread.sleep(1000);
			new Thread(C).start();
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
		
}

 

 
分享到:
评论

相关推荐

    Java线程间同步互斥

    摘要:Java源码,系统相关,线程同步,线程互斥 Java线程间同步互斥,在实际的编程中,经常要处理线程间的同步互斥问题。Java 语言内在强大的多线程支持使得处理这类问题变得相对来说比较简单。本例将模仿经典的线程...

    JAVA实现线程间同步与互斥生产者消费者问题

    在Java编程中,线程同步和互斥是多线程编程中的重要概念,它们用于解决多个线程同时访问共享资源时可能出现的问题。本项目通过一个生产者消费者问题的实例,展示了如何在Java中实现线程间的同步与互斥。 生产者消费...

    操作系统实验 多线程同步与互斥 java编写 有界面

    在“操作系统实验 多线程同步与互斥 java编写 有界面”的实验中,可能需要设计一个图形用户界面(GUI),通过按钮或事件触发线程的创建和同步操作,直观地展示线程间的交互和同步效果。例如,可以模拟银行账户转账,...

    Java实现的进程同步与互斥(PV)

    在计算机科学中,进程同步与互斥...在Java中,我们可以使用上述机制来实现进程同步与互斥,确保多线程程序的正确运行。在实际开发中,根据具体需求选择合适的同步机制是非常重要的,这有助于优化性能,避免死锁等问题。

    线程互斥和同步代码样本滴水三期信号量布置的作业

    线程互斥和同步是多线程编程中的关键概念,它们确保了在并发环境中资源的安全访问和程序的正确执行。在计算机系统中,一个进程可能包含多个线程,这些线程共享同一内存空间,因此必须有机制来防止线程间的冲突。 ...

    实例讲述线程的同步互斥

    **线程互斥** 是线程同步的一种特殊形式,确保同一时间只有一个线程能够访问特定的资源。互斥锁是实现线程互斥的常用工具。当一个线程获取了互斥锁后,其他试图获取该锁的线程会被阻塞,直到持有锁的线程释放它。在...

    java 多线程synchronized互斥锁demo

    总结来说,Java中的`synchronized`关键字是实现线程同步的关键,它通过互斥锁确保对共享资源的访问是线程安全的。在多线程编程中,合理使用`synchronized`可以有效避免竞态条件,保证程序的正确性和稳定性。对于...

    操作系统中对于线程的同步与互斥问题的设计原理及代码

    线程互斥是指当多个线程访问同一临界资源时,同一时刻只允许一个线程进行操作,以防止数据竞争和不一致的状态。线程同步则是协调多个线程执行的顺序和条件,避免出现有害的并发行为,如死锁和饥饿。 在实现线程互斥...

    编程实现进程(线程)同步和互斥

    在Windows等操作系统下,使用的VC、VB、java或C等编程语言,采用进程(线程)同步和互斥的技术编写程序实现生产者-消费者问题或哲学家进餐问题或读者-写者问题或自己设计一个简单进程(线程)同步和互斥的实际问题。

    java多线程同步例子

    java多线程同步互斥访问实例,对于初学者或是温故而知新的同道中人都是一个很好的学习资料

    Java多线程同步具体实例讲解 .doc

    Java多线程同步是编程中一个非常重要的概念,特别是在并发编程和高并发系统设计中起到关键作用。在Java中,为了保证线程安全,避免数据竞争和不一致的状态,我们通常会使用同步机制来控制对共享资源的访问。本文将...

    线程间互斥2

    在Java中,我们可以使用`synchronized`关键字或ReentrantLock等类来实现线程互斥。 1. **synchronized 关键字**:synchronized 可以用来修饰方法或代码块,当一个线程进入一个由 synchronized 修饰的方法或代码块时...

    Java语言中的线程同步互斥研究.pdf

    Java语言中的线程同步互斥研究 1. 引言 在现代计算机系统中,程序的执行都需要占用处理器,因此处理器管理一直是操作系统的核心组成部分。为了合理利用资源,有效调度任务执行,现代操作系统采用并发的方式,调度...

    多线程编程和操作系统线程同步互斥演示

    在这个“多线程编程和操作系统线程同步互斥演示”中,作者可能创建了一个或多个人工场景,展示了如何在VC++环境中使用多线程,并且演示了线程同步和互斥的实践应用。这可能涉及到以下几个方面: 1. **线程创建**:...

    Java多线程同步论文.doc

    在Java中,synchronized关键字是实现线程同步的关键,它提供了互斥访问,保证在任何时刻只有一个线程能够访问共享资源。 1. **synchronized关键字的使用** - **synchronized方法**:当一个方法被声明为...

    java ATM存取一体机(线程同步互斥)

    在Java编程领域,ATM(Automated Teller Machine,自动取款机)模拟是一个常见的多线程问题,用于展示线程同步和互斥的概念。在这个场景中,ATM被设计成一个可以执行存款和取款操作的系统,而这些操作需要确保在并发...

    Java线程.ppt

    学习Java线程,理解其创建、状态转换、调度和控制,以及如何处理线程间的互斥和同步,对于开发高效、稳定的并发程序至关重要。通过深入研究这些概念,开发者能够编写出更符合现代计算需求的高质量软件。

    线程 互斥.rar案例

    线程互斥是一种同步机制,它的目标是限制同一时间只有一个线程能够访问特定的代码段或数据结构,这通常被称为临界区。当一个线程进入临界区时,其他试图访问该区域的线程会被阻塞,直到持有资源的线程完成其操作并...

    java多线程代码案例(创建线程,主线程,线程优先级,线程组,线程同步,线程间的通信)

    本文将深入探讨Java多线程中的关键知识点,包括创建线程、主线程、线程优先级、线程组、线程同步以及线程间的通信。 1. **创建线程** 在Java中,可以通过两种方式创建线程:继承`Thread`类或实现`Runnable`接口。...

    第20章 Part3 多线程互斥与协作.pdf

    ### 第20章 Part3:多线程互斥与协作 #### 一、互斥(Mutual Exclusion) 互斥是指在线程编程中确保多个线程不会同时访问同一资源的技术。这种技术非常重要,因为如果不加以控制,多个线程对共享资源的并发访问...

Global site tag (gtag.js) - Google Analytics