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

------- android培训java培训、期待与您交流! ----------

第五部分:死锁

死锁的出现是因为同步中出现了同步,而且用的是不同的锁。

代码示例:

class Test implements Runnable
{
	private boolean flag;
	Test(boolean flag)
	{
		this.flag = flag;
	}
	public void run()
	{
		if(flag)
		{
			//同步中嵌套了同步,locka和lockb的互相嵌套导致了死锁问题
			synchronized(MyLock.locka)
			{
				s.o.p("if locka");
				synchronized(MyLock.lockb)
				{
					s.o.p("if lockb");
				}
			}
		}
		else
			synchronized(MyLock.lockb)
			{
				s.o.p("else lockb");
				synchronized(MyLock.locka)
				{
					s.o.p("else locka");
				}
			}
	}
}
class MyLock
{
	static Object locka = new Object();
	static Object lockb = new Object();
}
class	
{
	public static void main(String[] args) 
	{
			Thread t1 = new Thread(new Test(true));
		Thread t2 = new Thread(new Test(false));

		t1.start();
		//try{Thread.sleep(10);}catch (Exception e){};//这句的作用是切换作用
		//t.flag = false;
		t2.start();

	}
}

第六部分:线程通信和等待唤醒机制

1、线程通信:多个线程在对处理统一资源,但是处理任务不同(如存入和取出),这时就需要线程通信。

2、等待\唤醒机制的方法:

wait():让线程处于冻结状态,被wait的线程会被存储到线程池中。

notify():唤醒线程池中的一个线程(任何一个都有可能)。

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

PS

a、这些方法都要使用在同步中,因为要对持有监视器(锁)线程进行操作。

b、必须要明确到底操作的是哪个锁上的线程。

c、wait和sleep区别?
1)wait可以指定时间也可以不指定。sleep必须指定时间。
2)在同步中时,对CPU的执行权和锁的处理不同。
wait:释放执行权,释放锁。
sleep:释放执行权,不释放锁。

d、为什么操作线程的方法wait、notify、notifyAll定义在了object类中?

因为这些方法在操作同步中的线程时,都必须要表示他们所操作线程只有的锁,只有同一个锁上的被等待线程,可以被锁上notify唤醒。
不可以对不同锁中的线程进行唤醒。也就是说,等待和唤醒必须是同一把锁。锁可以是任意的对象,任意的对象调用的方式一定在object类中。

3、需求:一个线程存入名字和性别,另一个线程打印存入的名字和线程

class Res
{
	private String name;//
	private String sex;//
	private boolean flag = false;//
	//
	public synchronized void set(String name,String sex)
	{
		if(flag)
			try{this.wait();}catch(Exception e){}
		this.name = name;
		this.sex = sex;
		flag = true;
		this.notify();
	}
	public synchronized void out()
	{
		if(!flag)
			try{this.wait();}catch(Exception e){}
		s.o.p(name+" "+sex);
		flag = true;
		this.notify();
	}
}
class Input implements Runnable;
{
	private Res r;
	Input(Res r)
	{
		this.r = r;
	}
	public void run()
	{
		int x = 0;
		while(true)
		{
			synchronized(r)
			{
				if(r.flag)
					try{r.wait();}catch(Exception e){}
				if(x==0)
					r.set("mike","man");
				else
					r.set("lili","girl");
				x = (x+1)%2;
			}
		}
	}
}
class Output implements Runnable;
{
	private Res r;
	Output(Res r)
	{
		this.r = r;
	}
	public void run()
	{
		while(true)
		{
			r.out();
		}
	}
}
class  
{
	public static void main(String[] args) 
	{
		Res r = new Res();
		
		new Thread(new Input(r)).start();
		new Thread(new Output(r)).start();

	}
}

第七部分:生产者和消费者问题

思路:

1、使用if判断标志会导致不该运行的线程运行了,所以应该用while来判断标志,线程获取CPU执行权及锁后,将重新判断是否具备运行条件。

2、notify方法只能唤醒一个线程,如果本方唤醒了本方,没有意义。而且while判断标记+notify会导致死锁。notifyAll解决了本方线程一定会唤醒对方线程的问题。

代码示例:

public class ProConDemo {

	public static void main(String[] args) throws Exception{
		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();
		System.out.println("Hello World!");
	}

}
class Consumer implements Runnable
{
	private Resource res;
	Consumer(Resource res)
	{
		this.res = res;
	}
	public void run()
	{
		while(true)
		{
			res.out();
		}
	}
}
class Producer implements Runnable
{
	private Resource res;
	Producer(Resource res)
	{
		this.res = res;
	}
	public void run()
	{
		while(true)
		{
			res.set("+商品+");
		}
	}
}
class Resource
{
	private String name;
	private int count = 1;
	private boolean flag = false;
	//在同步函数内使用wait方法,notifyAll方法。
	public synchronized void set(String name)
	{
		while(flag)//修改为while(flag)
			try{wait();}catch(Exception e){}
		this.name = name+"--"+count++;
		System.out.println(Thread.currentThread().getName()+"..生产者"+this.name);
		flag = true;
		this.notifyAll();//修改为this.notifyAll()
	}
	//在同步函数内使用wait方法,notifyAll方法。
	public synchronized void out()
	{
		while(!flag)//修改为while(flag)
			try{wait();}catch(Exception e){}
		System.out.println(Thread.currentThread().getName()+"..............消费者"+this.name);
		flag = false;
		this.notifyAll();//修改为this.notifyAll()
	}
}

第八部分:使用JDK1.5以后的新特性方法

1、背景:同步代码快对于锁来说是隐式操作。JDK1.5以后将同步和锁封装成了对象,并且将操作锁的隐式方式定义到了该对象中,将隐式动作变成了显示动作。

2、使用的方法和接口:

Lock接口:出现替代了同步代码块或者同步函数,将同步的隐式操作变成显示锁操作。同时更为灵活,可以一个锁上加上多组监视器。

lock():获取锁。

unlock():释放锁,为了防止异常出现,导致锁无法被关闭,所以锁的关闭动作要放在finally中。

Condition接口:出现替代了Object中的wait、notify、notifyAll方法。将这些监视器方法单独进行了封装,变成Condition监视器对象,可以任意锁进行组合。

Condition接口中的await方法对应于Object中的wait方法。

Condition接口中的signal方法对应于Object中的notify方法。

Condition接口中的signalAll方法对应于Object中的notifyAll方法。

3、还是以生产者和消费者为例子:

package cn.itcast.demo;

import java.util.concurrent.locks.*;

public class NewProConDemo {

	public static void main(String[] args) {
		Resource1 r = new Resource1();
		Producer1 p = new Producer1(r);
		Consumer1 c = new Consumer1(r);
		Thread t1 = new Thread(p);
		Thread t2 = new Thread(p);
		Thread t3 = new Thread(c);
		Thread t4 = new Thread(c);
		t1.start();
		t2.start();
		t3.start();
		t4.start();
	}

}
class Resource1
{
	private String name ;
	private int count = 1;
	private boolean flag = false;
	//创建一个锁对象
	private Lock lock = new ReentrantLock();
	//通过已有的锁获取该锁上的监视器对象
	private Condition condition_pro = lock.newCondition();
	private Condition condition_con = lock.newCondition();
	
	public void set(String name)throws InterruptedException
	{
		//新特性
		/*
		 * 需要注意的地方,唤醒消费者用condition_con.signalAll();如果使用condition_pro.signalAll();是唤醒消费者。
		 * 在一个同步中要交替使用。
		 * */
		lock.lock();
		try
		{
			while(flag)
				condition_pro.await();
			this.name = name+"--"+count++;
			System.out.println(Thread.currentThread().getName()+"...生产者"+this.name);
			flag = true;
			condition_con.signalAll();
		}
		finally
		{
			//一定要关闭资源,所以在finally中使用解锁
			lock.unlock();
		}
	}
	public void out() throws InterruptedException{
		lock.lock();
		try{
			while(!flag)
				condition_con.await();
			System.out.println(Thread.currentThread().getName()+"---消费者"+this.name);
			flag = false;
			condition_pro.signalAll();
		}
		finally{
			//一定要关闭资源,所以在finally中使用解锁
			lock.unlock();
		}
	}
	
}
class Producer1 implements Runnable{
	private Resource1 res;
	Producer1(Resource1 res)
	{
		this.res = res;
	}
	public void run()
	{
		while(true)
		{
			try{res.set("+商品+");}catch(Exception e){}
		}
	}
}
class Consumer1 implements Runnable{
	private Resource1 res;
	Consumer1(Resource1 res)
	{
		this.res = res;
	}
	public void run()
	{
		while(true)
		{
			try{res.out();}catch(Exception e){}
		}
	}
}


第九部分:停止线程(冻结)和强制恢复线程

stop方法已经过时。

怎么控制线程的任务结束呢?

任务中都会有循环结构,只要控制住循环就可以结束任务。

控制循环通常就用定义标记来完成。

在while循环中通常要判断标志flag。

public void run()
	{
		while(true)
		{
			s.o.p(Thread.currentThread().getName()+"...run");
		}
	}


这是如果定义一个方法,满足条件后改变判断标志。

public void changeFlag()
{
        flag = false;
}
但是如果线程处于了冻结状态,无法读取标记,如何结束呢?
可以使用interrupt()方法将线程从冻结状态强制恢复到运行状态中来,让线程具备CPU的执行资格。
强制动作会发生InterruptedException,一定要记得处理。


第十部分:线程的其他方法

1、守护线程:

使用setDaemon方法将线程设置为守护线程,该方法必须在启动线程前调用。

代码示例:

t1.setDaemon(true);
t2.setDaemon(true);
t1.start();
t2.start();

2、join方法

当A线程执行到了B线程的join方法时,A就会等待。等B线程都执行完,A才会执行。join可以用来临时加入线程执行

代码示例:

t1.start();
t1.join();//抢夺cpu执行权
t2.start();
//如果将t1.join();放在这儿,则是t1和t2抢夺cpu抢夺执行权
		




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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值