day_34Java多线程3

星期日, 十二月 06, 2015 21:52:16

五、多线程的同步

本节介绍多线程的同步,具体介绍同步问题的引出、同步代码块、同步方法和死锁等内容。

5.1同步问题的引出

     重现问题,可以在程序中调用Thread.sleep()静态方法来刻意造成线程间的这种切换。

Thread.sleep()方法将迫使线程执行到该处后暂停执行,让出cpu给别的线程,在指定的时间后,cpu回到刚才暂停的线程上执行。

5.1线程同步问题

代码案例:

package day34;

public class ThreadTb {
	public static void main(String[] args) {
		/*模拟铁路售票系统的范例,实现4个售票发售某日某次列车的车票20张,
		一个售票点用一个线程来表示。*/
		ThreadT tp = new ThreadT();
		//启动4个线程,并实现了资源共享的目的
		new Thread(tp).start();
		new Thread(tp).start();
		new Thread(tp).start();
		new Thread(tp).start();
		
	}

}
class ThreadT implements Runnable {
	private int tickets = 20;
	@Override
	public void run() {
		// TODO Auto-generated method stub
		while(true) {
			if(tickets>0) {
				try {
					Thread.sleep(100);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			System.out.println(Thread.currentThread().getName()+"..."+(tickets--));
			}
		}
	}
}
运行结果:
部分
Thread-2...6
Thread-1...7
Thread-3...4
Thread-1...2
Thread-2...3
Thread-0...1
Thread-1...0
Thread-3...-1
Thread-2...-2

 注意:

  1.程序中使用sleep()以让出cpu给别的线程。出现了负数

  2.造成这种意外的根本原因就是因为资源数据访问不同步引起的。

5.2解决方法 : 同步代码块

   必须保证这段代码的原子性

   即当一个线程运行到if(tickets>0)后,cpu不去执行其他线程中的、可能影响当前线程中的下一句代码的执行结果的代码块,

必须等到下一句执行完后才能执行其他线程中的有关代码块。

   这段代码就好比一座独木桥,任何时刻都只能有一个人在桥上走,即程序中不能有多个线程同时在这两句代码之间执行,

即线程同步。

语法:

  synchronized(对象){

   需要同步的代码;

}

5.2.1 修改后的代码

package day34;

public class ThreadSync {
	public static void main(String[] args) {
		/*模拟铁路售票系统的范例,实现4个售票发售某日某次列车的车票20张,
		一个售票点用一个线程来表示。*/
		ThreadT tp = new ThreadT();
		//启动4个线程,并实现了资源共享的目的
		new Thread(tp).start();
		new Thread(tp).start();
		new Thread(tp).start();
		new Thread(tp).start();
		
	}

}
class ThreadT implements Runnable {
	private int tickets = 20;
	@Override
	public void run() {
		// TODO Auto-generated method stub
		while(true) {
			synchronized(this){
			if(tickets>0) {
				try {
					Thread.sleep(100);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			System.out.println(Thread.currentThread().getName()+"..."+(tickets--));
			}
			}
		}
	}
}
运行结果:
部分
Thread-3...3
Thread-3...2
Thread-3...1

 总结:

   本程序将这些需要具有原子性的代码放入synchronized语句内,形成了同步代码块。

在同一时刻只能有一个线程可以进入同步代码块内运行,只有当该线程离开同步代码块后,其他线程才能进入同步代码内运行。

5.3同步方法

  除了可以对代码块进行同步外,也可以对方法实现同步,只要在需要同步的方法定义前面加上synchronized关键字即可。

语法:

 访问控制符 synchronized 返回值类型 方法名称(参数){}

代码案例:

package day34;

public class ThreadSync {
	public static void main(String[] args) {
		/*模拟铁路售票系统的范例,实现4个售票发售某日某次列车的车票20张,
		一个售票点用一个线程来表示。*/
		ThreadT tp = new ThreadT();
		//启动4个线程,并实现了资源共享的目的
		new Thread(tp).start();
		new Thread(tp).start();
		new Thread(tp).start();
		new Thread(tp).start();
		
	}

}
class ThreadT implements Runnable {
	private int tickets = 20;
	@Override
	public void run() {
		// TODO Auto-generated method stub
		while(true) {
			sale();
		}
	}
	public synchronized void sale() {
		if(tickets>0) {
			try {
				Thread.sleep(100);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		System.out.println(Thread.currentThread().getName()+"..."+(tickets--));
		}
	}
}
运行结果:
Thread-0...5
Thread-3...4
Thread-2...3
Thread-1...2
Thread-1...1

 注意:

    在同一类中,使用synchronized关键字定义的若干方法,可以在多线程之间同步。

当有一个线程进入有synchronized修饰的方法时,其他线程就不能进入同一个对象使用synchronized来修饰所以方法,

直到第1个线程执行完它所进入的synchronized修饰的方法为止。

5.4死锁

  一旦有多个进程,且它们都要争用对多个锁的独占访问,那么就有可能发生死锁。

如果有一组进程或线程,其中每个都在等待一个只有其他进程或线程才可以进行的操作,

那么就称为死锁了。

  

   要避免死锁,应该确保在获取多个锁时,在所有的线程中都以相同的顺序获取锁。

5.4.1代码案例

package day34;

public class DeadLock implements Runnable{
	A a = new A();
	B b = new B();
	DeadLock(){
		Thread.currentThread().setName("main--->thread");
		Thread tt = new Thread(this);
		tt.start();
		a.funA(b);
		System.out.println("main线程运行完毕");
	}
	
	@Override
	public void run() {
		// TODO Auto-generated method stub
		Thread.currentThread().setName("Test--->thread");
		b.funB(a);
		System.out.println("其他线程运行完毕");
	}
	
	public static void main(String[] args) {
		new DeadLock();
	}

	
}

class A{
	synchronized void funA(B b) {
		System.out.println(Thread.currentThread().getName()+"进入A");
		try {
			Thread.sleep(10000);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			System.out.println(Thread.currentThread().getName()+"调用B类中的last()方法");
			b.last();
		}
	}
	synchronized void last(){
		System.out.println("A类中的last()方法");
	}
}


class B{
	synchronized void funB(A a) {
		System.out.println(Thread.currentThread().getName()+"进入B");
		try {
			Thread.sleep(10000);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			System.out.println(Thread.currentThread().getName()+"调用A类中的last()方法");
			a.last();
		}
	}
	synchronized void last(){
		System.out.print("B类中的last()方法");
	}
}


运行结果:
main--->thread进入A
Test--->thread进入B
其他线程运行完毕
main线程运行完毕

 需要分析原因,运行结果有疑问

分析:

 Test-->thread 进入b的监视器,然后又等待a的监视器。

同时main-->thread进入了a的监视器,并等待b的监视器。

这个程序永远不会完成。

星期日, 十二月 06, 2015 23:02:34

371--381

猜你喜欢

转载自yuzhouxiner.iteye.com/blog/2262189