`

Java 线程同步

 
阅读更多

理解Java线程状态:

 

线程同步

多个线程操作一个资源的情况下,导致资源数据前后不一致。这样就需要协调线程的调度,即线程同步。

解决多个线程使用共通资源的方法:

        线程操作资源时独占资源,其他线程不能访问资源。

 

例:Timer实例的num成员,即add()方法是用的次数。即Timer实例是资源对象。

class TestSync implements Runnable {
  Timer timer = new Timer();
  public void run(){
    timer.add(Thread.currentThread().getName());
  }
}

class Timer{
  private static int num = 0;
  public /* synchronized */ void add(String name){ 
	    num ++;
	    try {Thread.sleep(1);} 
	    catch (InterruptedException e) {}
	    System.out.println(name+", 你是第"+num+"个使用timer的线程");
  }
}

public class testMain{
	public static void main(String[] args) {
		TestSync test = new TestSync();
		Thread t1 = new Thread(test);
		Thread t2 = new Thread(test);
		t1.setName("t1"); 
		t2.setName("t2");
		t1.start(); 
		t2.start();
	}
}

说明:

(1) 程序输出显示:

         t1你是第2个使用timer的线程

         t2你是第2个使用timer的线程

(2) 程序执行过程分析:

线程t1,线程t2 均调用timer的add()方法。

        a. 某一线程执行num++(num为1),该线程暂停了1ms。

        b. 另一线程执行num++(num为2),该线程暂停了1ms。

        c. 某一线程暂停结束,输出“是第2个使用timer的线程”。

        d. 另一线程暂停结束,输出“是第2个使用timer的线程”。

(3)  导致以上输出结果的原因:

        两个线程对同一个Timer实例的num成员做操作。没有线程之间协调运作。

 

synchronized关键字

synchronized关键字用作锁定当前对象。这种锁又称为“互斥锁”。

使用方法:

1. 同步代码块

synchronized(obj){

    //.....

}

说明:执行同步代码块。JVM会锁定obj对象。即锁定对象obj只允许单个线程操作。

 

2. 同步方法

public synchronized void xxx(){

    //.....

}

说明:执行同步方法,该方法只允许单个线程执行。同步方法被某线程执行后排斥其他线程。同步方法会使得程序效率性能降低。

 

synchronized关键字修饰的代码块之运行单个线程占用。只有在线程执行完同步代码块后,其他线程才能占用该代码块。

 

例:改进程序。

public class TestSync implements Runnable {
  Timer timer = new Timer();
  public void run(){
    timer.add(Thread.currentThread().getName());
  }
}

class Timer{
  private static int num = 0;
  public void add(String name){ 
    synchronized (this) {
	    num ++;
	    try {Thread.sleep(1);} 
	    catch (InterruptedException e) {}
	    System.out.println(name+", 你是第"+num+"个使用timer的线程");
	}
  }
}

/*或者一下形式
class Timer{
  private static int num = 0;
  public synchronized void add(String name){ 
	    num ++;
	    try {Thread.sleep(1);} 
	    catch (InterruptedException e) {}
	    System.out.println(name+", 你是第"+num+"个使用timer的线程");
  }
}
*/

class testMain(){
	public static void main(String[] args) {
		TestSync test = new TestSync();
		Thread t1 = new Thread(test);
		Thread t2 = new Thread(test);
		t1.setName("t1"); 
		t2.setName("t2");
		t1.start(); 
		t2.start();
	}
}

 

 

理解synchronized关键字

思考以下程序:当执行某个线程执行m1()方法,m2()方法还能执行吗?

public class TestSynchronized {
	int b = 100;
	
	public synchronized void m1() throws Exception{
		b = 1000;
		Thread.sleep(5000);
		System.out.println("b = " + b);
	}
	
	public void m2() throws Exception {
		System.out.println(b);
	}
	
}

答案:m2() 方法可以执行。即synchronized方法的作用只是用于单个线程执行,并没有真正锁定方法对应的this对象。其他线程可以访问没有synchronized关键字的方法。

 

由于类中的非同步方法可能会影响到类中的同步方法,所以若需要保证类对象的线程同步,则需要仔细考虑类方法是否需要添加synchronized关键字修饰。

 一般而言,对于某个类对象,读取方法(如get方法)不需要加锁,修改方法(如set方法)需要加锁。

 

ps:

关于该问题,自己又做了一个试验,仔细研究了下。

请看:http://shijiaqi1066.iteye.com/admin/blogs/1886791

 

 

 死锁

 当1号线程执行过程中需要使用(锁定)对象A,同时还需要使用(锁定)另一个对象B。但是2号线程使用(锁定)了对象B,同时还需要使用(锁定)对象A。

 这种情况导致 1号线程等待2号线程执行完才能继续执行;2号线程等待1号线程的执行完才能继续执行。即1号线程、2号线程均无法继续执行;其他线程无法所得资源,也无法继续执行。整个程序无法继续执行。即死锁。

说明:出现死锁现象需要多个锁定对象。

 

例:模拟死锁现象。

public class TestDeadLock implements Runnable {
	public int flag = 1;
	static Object o1 = new Object(), o2 = new Object();
	public void run() {
        System.out.println("flag=" + flag);
		if(flag == 1) {
			synchronized(o1) {        //线程锁住o1。
				try {
					Thread.sleep(500);
				} catch (Exception e) {
					e.printStackTrace();
				}
				synchronized(o2) {    //线程锁住o2。
					System.out.println("1");	
				}
			}
		}
		if(flag == 0) {
			synchronized(o2) {        //线程锁住o2。
				try {
					Thread.sleep(500);
				} catch (Exception e) {
					e.printStackTrace();
				}
				synchronized(o1) {    //线程锁住o1。
					System.out.println("0");
				}
			}
		}
	}	
	
	public static void main(String[] args) {
		TestDeadLock td1 = new TestDeadLock();
		TestDeadLock td2 = new TestDeadLock();
		td1.flag = 1;
		td2.flag = 0;
		Thread t1 = new Thread(td1);    //flag1线程。
		Thread t2 = new Thread(td2);    //flag2线程。
		t1.start();
		t2.start();
		
	}
}

 说明:

以上程序会出现死锁。

        a. flag1线程锁住o1,睡眠;同时,flag2线程锁住o2,睡眠。

        b. flag1线程苏醒需要锁住o2,才能执行完毕,但是o2被flag2线程占用;同时,flag2线程苏醒需要锁住o1,才能执行完毕,但是o1被flag1线程占用。

        c. 出现flag1线程等待flag2线程,flag2线程等待flag1线程。程序无法再继续执行。

 

 

哲学家吃饭问题:5个哲学家围绕圆桌吃饭。每个哲学家各左右手各拿一只筷子(即每个一双筷子)。

详述:

WIKI :http://zh.wikipedia.org/wiki/%E5%93%B2%E5%AD%A6%E5%AE%B6%E5%B0%B1%E9%A4%90%E9%97%AE%E9%A2%98。

百度百科 :http://baike.baidu.com/view/3446884.htm

 

 

解决死锁问题

如果不需要写接近底层的程序或很复杂的程序,死锁问题在实际编程中比较难遇上。

如果出现死锁问题:可以放大锁定对象的粒度。即不要一次锁定多个对象。尽量锁定一个对象。

 

 

 

 生产者-消费者问题

 例:模拟生产者消费者问题

 

public class ProducerConsumer {
	public static void main(String[] args) {
		SyncStack ss = new SyncStack();
		Producer p = new Producer(ss);
		Consumer c = new Consumer(ss);
		new Thread(p).start();
		new Thread(p).start();
		new Thread(p).start();
		new Thread(c).start();
	}
}

class WoTou {
	int id; 
	WoTou(int id) {
		this.id = id;
	}
	public String toString() {
		return "WoTou : " + id;
	}
}

class SyncStack {
	int index = 0;
	WoTou[] arrWT = new WoTou[6];
	
	public synchronized void push(WoTou wt) {
		while(index == arrWT.length) {
			try {
				this.wait();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		this.notifyAll();		
		arrWT[index] = wt;
		index ++;
	}
	
	public synchronized WoTou pop() {
		while(index == 0) {
			try {
				this.wait();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		this.notifyAll();
		index--;
		return arrWT[index];
	}
}

class Producer implements Runnable {
	SyncStack ss = null;
	Producer(SyncStack ss) {
		this.ss = ss;
	}
	
	public void run() {
		for(int i=0; i<20; i++) {
			WoTou wt = new WoTou(i);
			ss.push(wt);
System.out.println("生产了:" + wt);
			try {
				Thread.sleep((int)(Math.random() * 200));
			} catch (InterruptedException e) {
				e.printStackTrace();
			}			
		}
	}
} 

class Consumer implements Runnable {
	SyncStack ss = null;
	Consumer(SyncStack ss) {
		this.ss = ss;
	}
	
	public void run() {
		for(int i=0; i<20; i++) {
			WoTou wt = ss.pop();
System.out.println("消费了: " + wt);
			try {
				Thread.sleep((int)(Math.random() * 1000));
			} catch (InterruptedException e) {
				e.printStackTrace();
			}			
		}
	}
}
 

 

 

 

Object类对线程的控制

void wait()

void wait(long timeout) 

void wait(long timeout, int nanos)

在其他线程调用此对象的 notify() 方法或 notifyAll() 方法前,

导致当前线程等待。  

 

void notify() 

void notifyAll()

唤醒在此对象监视器上等待的单个线程。 

唤醒在此对象监视器上等待的所有线程。

 

在 http://blog.csdn.net/zyplus/article/details/6672775 看到的一些介绍 (语句略有改动)

 

如果需要在线程间相互唤醒的话就需要借助Object.wait(), Object.nofity() 。

Obj.wait(),与Obj.notify()必须要与synchronized(Obj)一起使用,也就是wait,与notify是针对已经获取了Obj锁进行操作,从语法角度来说就是Obj.wait(),Obj.notify必须在synchronized(Obj){...}语句块内。

 

从功能上来说:

obj.wait()方法使得获取对象锁的线程主动释放对象锁,同时休眠线程;直到有其它线程调用对象的notify()唤醒该线程,才能继续获取对象锁,并继续执行。

obj.notify() 方法唤醒因释放obj的锁而休眠的线程(唤醒obj对象上被wait()的线程)。注意:notify()调用后,并不是马上就释放对象锁的,而是在相应的synchronized(){}语句块执行结束,自动释放锁后,JVM会在wait()对象锁的线程中随机选取一线程,赋予其对象锁,唤醒线程,继续执行。这样就提供了在线程间同步、唤醒的操作。

 

Thread.sleep()与Object.wait()二者都可以暂停当前线程,释放CPU控制权,主要的区别在于Object.wait()在释放CPU同时,释放了对象锁的控制。

 

 

 

在JAVA中,是没有类似于PV操作、进程互斥等相关的方法的。JAVA的进程同步是通过synchronized()来实现的,需要说明的是,JAVA的synchronized()方法类似于操作系统概念中的互斥内存块,在JAVA中的Object类型中,都是带有一个内存锁的,在有线程获取该内存锁后,其它线程无法访问该内存,从而实现JAVA中简单的同步、互斥操作。明白这个原理,就能理解为什么synchronized(this)与synchronized(static XXX)的区别了,synchronized就是针对内存区块申请内存锁,this关键字代表类的一个对象,所以其内存锁是针对相同对象的互斥操作,而static成员属于类专有,其内存空间为该类所有成员共有,这就导致synchronized()对static成员加锁,相当于对类加锁,也就是在该类的所有成员间实现互斥,在同一时间只有一个线程可访问该类的实例。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

分享到:
评论

相关推荐

    java 线程同步 信号量控制同步

    Java 线程同步控制机制 线程同步是 Java 编程中的一种机制,用于控制多个线程之间的资源访问顺序,以避免线程之间的冲突和数据不一致。线程同步的目的就是避免线程“同步”执行,即让多个线程之间排队操作共享资源...

    java线程同步详解

    总结一下,Java线程同步的关键点: 1. **线程同步是为了解决共享资源的并发访问问题,防止数据不一致和冲突。** 2. **同步意味着线程排队,依次访问共享资源,而不是同时访问。** 3. **只有共享变量(可变状态)才...

    Java 线程同步调用

    在深入探讨Java线程同步调用这一主题之前,我们首先需要理解线程同步的基本概念及其在多线程环境中的重要性。线程同步是多线程编程中的一个关键概念,它确保了多个线程在访问共享资源时不会发生冲突,避免了数据不...

    java线程同步及通信

    Java线程同步与通信是多线程编程中的关键概念,用于解决并发访问共享资源时可能出现的数据不一致性和竞态条件问题。以下将详细介绍这两个主题,以及如何通过代码示例进行演示。 1. **线程同步**: 线程同步是确保...

    java线程同步java线程同步

    java线程同步java线程同步java线程同步

    JAVA100例之实例65 JAVA线程同步

    本实例65着重讲解了Java线程同步的实现方法,帮助开发者理解和掌握如何在并发环境中保证代码的正确执行。 首先,我们要了解什么是线程。线程是程序中的执行流,每个线程都有自己的程序计数器、栈、局部变量和常量,...

    Java线程同步例子.pdf

    在Java中,线程同步是保证多线程安全...上述Java线程同步例子中涉及到的代码虽然是片段,但涵盖了线程同步处理的多个重要方面,帮助我们理解和使用Java线程同步机制,以及在设计和实现多线程应用程序时的实践和技巧。

    java线程同步基础知识

    Java线程同步是Java编程中一个关键的概念,用于解决多线程环境下的数据一致性问题。在Java中,线程同步主要依赖于监视器(Monitor)机制,它支持两种线程行为:互斥和协作。 互斥是通过对象锁来实现的,确保在任意...

    java线程同步的例子.doc

    Java线程同步是一种控制多个线程访问共享资源的方式,确保数据的一致性和完整性。在这个例子中,我们将讨论如何使用`synchronized`关键字实现线程同步,以及它的工作原理。 首先,我们有两个类`ThreadDemo`和`...

    java 线程同步

    Java线程同步是多线程编程中的一个关键概念,它涉及到如何在并发环境中控制线程对共享资源的访问,以避免数据不一致性和竞态条件等问题。在Java中,线程同步主要有以下几种机制: 1. **synchronized 关键字**: - ...

    Java线程同步与 Java 并发设施.pdf

    Java线程同步是多线程编程中的一个重要概念,其主要目的是确保多个线程在访问共享资源时能够有序进行,避免数据不一致性和竞态条件。在Java中,线程同步可以通过多种方式实现,如synchronized关键字、java.util....

    java线程同步的例子.pdf

    Java线程同步是一种确保多个线程在访问共享资源时能够有序执行的技术,避免了数据的不一致性和竞态条件。在给定的示例中,通过两种方式展示了线程同步:synchronized方法和synchronized代码块。 首先,我们来看同步...

    java线程同步的例子[文].pdf

    本文将详细解释Java线程同步的概念,并通过示例代码来说明其工作原理。 首先,我们需要理解Java中的两个主要同步机制:`synchronized`关键字和`wait()`, `notify()`, `notifyAll()`方法。`synchronized`关键字可以...

    java线程同步(实例讲解,清晰易懂)

    ### Java线程同步详解 #### 一、同步问题的提出 在多线程环境中,当多个线程同时访问和修改同一个共享资源时,可能会导致数据的一致性和完整性问题。例如,考虑下面的一个简单示例: ```java public class Foo { ...

Global site tag (gtag.js) - Google Analytics