黑马程序员——Java基础——多线程(二)

------<a href="http://www.itheima.com" target="blank">Java培训、Android培训、iOS培训、.Net培训</a>、期待与您交流! -------


一、死锁

由于线程可以获取多个锁,比如在一个对象的同步方法里面调用另外一个对象的同步方法,则获取了两个对象的同步锁,这样就可能会出现同步嵌套的情况。当两个线程被阻塞,每个线程在等待另一个线程时会导致死锁。在开发中一定要避免出现死锁。

class DeadLock implements Runnable
{
	//定义一个线程运行的标记
	private boolean flag;
	DeadLock(boolean flag)
	{
		this.flag = flag;
	}
	//定义两个对象锁
	static Object locka = new Object();
	static Object lockb = new Object();

	//定义线程运行中的同步嵌套
	public void run()
	{
		if(flag)
		{
			synchronized(locka)
			{
				System.out.println(Thread.currentThread().getName()+"...if locka ");
				synchronized(lockb)
				{
					System.out.println(Thread.currentThread().getName()+"..if lockb");					
				}
			}
			
		}
		else
		{
			synchronized(lockb)
			{
				System.out.println(Thread.currentThread().getName()+"..else lockb");
				synchronized(locka)
				{
					System.out.println(Thread.currentThread().getName()+".....else locka");
				}
			}
		}
	}
}
class  DeadLockDemo
{
	public static void main(String[] args) 
	{
		//定义两个线程,在构造中传递不同的标记并运行
		Thread t1 = new Thread(new DeadLock(true));
		Thread t2 = new Thread(new DeadLock(false));
		t1.start();
		t2.start();
	}
}

二、线程间的通信

1.线程间的通信就是多个线程在操作同一个资源,但是操作的动作却不一样,比如多线程常见的生产者—消费者-仓库模式,实现存入仓库一个资源,接着取出一个。

//创建仓库资源
class Resource {
	private String name;
	private int count = 1;
	private boolean flag = false;
	//定义存和取资源的方法,因为属于多个线程共同操作的内容,所以要加同步
	public synchronized void setIn(String name) {
		//根据定义的标记判断仓库内是否有资源
		//没有就往仓库存放,存放结束后改变标记,唤醒等待的线程
		//有就线程等待取出结束
		while (flag)
			try {
				wait();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		this.name = name + "--" + count++;
		System.out.println(Thread.currentThread().getName() + "...生产者.."
				+ this.name);
		flag = true;
		this.notifyAll();
	}
	public synchronized void takeOut() {
		//根据定义的标记判断仓库内是否有资源
		//有就从仓库取出,取出结束后改变标记,唤醒等待的线程
		//如果没有就线程等待存放结束
		while (!flag)
			try {
				wait();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		System.out.println(Thread.currentThread().getName() + "...消费者.."
				+ this.name);
		flag = false;
		this.notifyAll();
	}
}
//定义生产者,存放商品,循环十次方便查看结果
class Producer implements Runnable {
	private Resource res;

	Producer(Resource res) {
		this.res = res;
	}

	public void run() {
		for(int x = 0 ; x < 10 ; x++)
		{
			res.setIn("+商品+");
		}
	}
}
//定义消费者,取出商品
class Consumer implements Runnable {
	private Resource res;

	Consumer(Resource res) {
		this.res = res;
	}

	public void run() {
		for(int x = 0; x < 10; x++)
		{
			res.takeOut();
		}
	}
}

//创建线程分别进行存放和读取,并运行
public class ProducerConsumerDemo {
	public static void main(String[] args) {
		Resource r = new Resource();

		Producer pro = new Producer(r);
		Consumer con = new Consumer(r);

		Thread t1 = new Thread(pro);
		Thread t2 = new Thread(pro);
		Thread t3 = new Thread(con);
		Thread t4 = new Thread(con);

		t1.start();
		t2.start();
		t3.start();
		t4.start();

	}
}


2.线程状态及常用方法

1)线程状态

被创建:线程对象已经创建,还没有在其上调用start方法。

可运行状态:当线程有资格运行,但调度程序还没有把它选定为运行线程时线程所处的状态。当start方法调用时,线程首先进入可运行状态。在线程运行之后或者从阻塞、等待或睡眠状态回来后,也返回到可运行状态。

运行状态:获取了CPU执行权,执行程序代码。

临时状态(阻塞):有执行资格,但是没有执行权。

冻结状态:遇到sleep方法和wait方法时,失去执行资格和执行权,sleep方法时间到或者调用notify方法时,获得执行资格,变为临时状态。

消忙状态:线程执行完了或者因异常退出了run方法,该线程结束生命周期

2)常用方法

wait:将同步中的线程处于冻结状态。释放了CPU执行权,释放了锁。同时将线程对象存储到线程池中。

sleep:释放CPU执行权,不释放锁。

notify:唤醒线程池中某一个等待线程。

notifyAll:唤醒的是线程池中的所有线程。

3.JDK1.5的升级

JDK1.5 中直接将锁封装成对象,把同步Synchronized替换成显示的Lock操作。将Object中的wait,notify notifyAll,替换了Condition对象。该对象可以通过Lock锁进行获取,并支持多个相关的Condition对象,可以针对性的唤醒指定Condition对象的线程。生产者消费者代码也可以用下面的代码实现:

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

//创建仓库资源
class Resource
{
	private String name;
	private int count = 1;
	private boolean flag = false;

	private Lock lock = new ReentrantLock();
	//创建两个Condition,控制生产者和消费者线程间的通信
	private Condition condition_pro = lock.newCondition();
	private Condition condition_con = lock.newCondition();



	public void setIn(String name) throws InterruptedException
	{
		//获取锁
		lock.lock();
		try
		{
			//根据定义的标记判断仓库内是否有资源
			//没有就往仓库存放,存放结束后改变标记,唤醒消费者等待的线程
			//有就等待消费者线程取出结束
			while(flag)
				condition_pro.await();
			this.name = name+"--"+count++;

			System.out.println(Thread.currentThread().getName()+"...生产者.."+this.name);
			flag = true;
			condition_con.signal();
		}
		//释放锁的动作一定要执行,所以使用finally
		finally
		{
			lock.unlock();
		}
	}
	public void takeOut() throws InterruptedException
	{
		//获取锁
		lock.lock();
		try
		{
			//根据定义的标记判断仓库内是否有资源
			//有就从仓库取出,取出结束后改变标记,唤醒生产者等待的线程
			//如果没有就等待生产者线程存放结束
			while(!flag)
				try {
					condition_con.await();
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			System.out.println(Thread.currentThread().getName()+"...消费者.."+this.name);
			flag = false;
			condition_pro.signal();
		}
		//释放锁的动作一定要执行,所以使用finally
		finally
		{
			lock.unlock();
		}
		
	}
}
//定义生产者,存放商品,循环十次方便查看结果
class Producer implements Runnable
{
	private Resource res;

	Producer(Resource res)
	{
		this.res = res;
	}
	public void run()
	{
		for(int x = 0 ;x < 10; x++)
		{
			try {
				res.setIn("+商品+");
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}	
		}
	}
}
//定义消费者,取出商品
class Consumer implements Runnable
{
	private Resource res;

	Consumer(Resource res)
	{
		this.res = res;
	}
	public void run() 
	{
		for(int x = 0 ;x < 10; x++)
		{
			try {
				res.takeOut();
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		
	}
}

//创建线程分别进行存放和读取,并运行
public class ProducerConsumerDemo {
	public static void main(String[] args) {
		Resource r = new Resource();

		Producer pro = new Producer(r);
		Consumer con = new Consumer(r);

		Thread t1 = new Thread(pro);
		Thread t2 = new Thread(pro);
		Thread t3 = new Thread(con);
		Thread t4 = new Thread(con);

		t1.start();
		t2.start();
		t3.start();
		t4.start();

	}
}

三、多线程题目

1.下列程序的运行结果

		new Thread(new Runnable(){
			public void run()
			{
				System.out.println("接口线程...");
			}
		})
		{
			public void run()
			{
				System.out.println("类线程...");
			}
		}.start();

运行结果:

这个题目使用两个匿名内部类,在没指定的情况下,子类构造函数调用的都是父类无参的构造函数,所以传入实现Runnbale的子类对象没有作用。

2.创建3个线程依次打印ABC,并且循环10次

public class ABCThread {

	// 定义标记,控制打印顺序和打印次数
	static int count = 1;

	public static void main(String[] args) {

		// 创建同步代码块的锁
		Object obj = new Object();

		//因为只有简单的多线程运行代码,所以使用匿名内部类传递
		Thread A = new Thread(new Runnable() {
			@Override
			//复写run方法,判断标记,如果符合就等待,
			//如果不符合执行打印代码,并使标记加1,唤醒其他所有在等待的线程
			public void run() {
				while (count <= 30) {
					synchronized (obj) {
						if (count % 3 != 1)
							try {
								obj.wait();
							} catch (InterruptedException e) {
								// TODO Auto-generated catch block
								e.printStackTrace();
							}
						else {
							System.out.println("A");
							count++;
							obj.notifyAll();
						}
					}

				}
			}
		});
		Thread B = new Thread(new Runnable() {
			@Override
			//复写run方法,判断标记,如果符合就等待,
			//如果不符合执行打印代码,并使标记加1,唤醒其他所有在等待的线程
			public void run() {
				while (count <= 30) {
					synchronized (obj) {
						if (count % 3 != 2)
							try {
								obj.wait();
							} catch (InterruptedException e) {
								// TODO Auto-generated catch block
								e.printStackTrace();
							}
						else {
							System.out.println("B");
							count++;
							obj.notifyAll();

						}

					}
				}
			}
		});
		Thread C = new Thread(new Runnable() {
			@Override
			//复写run方法,判断标记,如果符合就等待,
			//如果不符合执行打印代码,并使标记加1,唤醒其他所有在等待的线程
			public void run() {
				while (count <= 30) {
					synchronized (obj) {
						if (count % 3 != 0)
							try {
								obj.wait();
							} catch (InterruptedException e) {
								// TODO Auto-generated catch block
								e.printStackTrace();
							}
						else {
							System.out.println("C");
							System.out.println("-------"+count/3);
							count++;
							obj.notifyAll();
						}

					}
				}
			}
		});
		A.start();
		B.start();
		C.start();
	}
}







  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值