黑马程序员——多线程:轻量级进程

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

线程的生命周期


new: 一个新的线程以一个新的状态开始了它的生命周期。它始终留在这个状态中直到程序开始线程。它也被称为一个生成的进程。
Runnable 在一个新生成的线程开始后,这个线程变得可运行。在这个状态下的线程被认为正在执行任务。
Waiting: 有时候,一个线程当它等待另一个线程工作时跃迁到等待状态。一个线程仅当另一个线程发信号给等待的线程继续执行才跃迁回可运行转态。
Timed waiting: 一个可运行的线程能进入定时等待状态等待指定的时间间隔。在这种转态下的线程当时间间隔死亡或者当它所等待的活动发生时跃迁回可运行状态。
Terminated: 一个可运行的线程,当它完成了它的任务后进入 terminated 状态,否则它就结束。

线程的创建过程

创建线程的第一种方式:继承Thread类

步骤:
1,定义一个类继承自Thread。
2,复写Thread类中的run方法。(目的:将自定义的代码存储在run方法中。让线程运行。)
3,调用线程的start方法。(该方法两个作用:启动线程,调用run方法。)

为什么要覆盖run方法?
Thread类用于描述线程。该类就定义了一个功能,用于存储线程要运行的代码。该存储功能就是run方法。
也就是说Thread类中的run方法,用于存储线程要运行的代码。
版本1:创建一个线程,和主线程交替运行
class Demo extends Thread//1,定义一个类继承自Thread。
{
	public void run()//2,复写Thread类中的run方法。
	{
		for(int x = 0; x < 100; x++)
			System.out.println("demo run----" + x);
	}
}


class ThreadDemo 
{
	public static void main(String[] args) 
	{
		//for(int x=0; x<4000; x++)
		//System.out.println("Hello World!");

		Demo d = new Demo();//创建好一个线程。
		d.start();//3, 开启线程并执行该线程的run方法。虚拟机会调用线程的run方法。该方法两个作用:启动线程,调用run方法
		//d.run();//仅仅是对象调用方法。而线程创建了,并没有运行。只有调用了start,线程才真正被执行起来

		for(int x = 0; x < 100; x++)
			System.out.println("Hello World!--"+x);
	}
}
/*
Hello World!--0
Hello World!--1
Hello World!--2
demo run----0
Hello World!--3
demo run----1
Hello World!--4
demo run----2
demo run----3
demo run----4
Hello World!--5
....
 */

版本2:创建两个线程和主线程交替运行,并获取线程的对象和名称
获取当前线程对象:static Thread currentThread()
获取线程名称:getName():
设置线程名称:setName或者使用构造函数
</pre></div><pre name="code" class="java"><pre name="code" class="java">package Thread;

class Test extends Thread
{
	//private String name;
	Test(String name)
	{
		//this.name = name;
		super(name);//不需要自己再定义private String name;this.name = name;使用super问老爸要
	}
	public void run()
	{
		for(int x=0; x<60; x++)
		{
			System.out.println((Thread.currentThread()==this)+"..."+this.getName()+" run..."+x);
			//返回的是ture,说明Thread.currentThread()和this相同,Thread.currentThread()是通用使用方式
		}
	}
}


class ThreadTest 
{
	public static void main(String[] args) 
	{
		Test t1 = new Test("一号线程---");//通过构造函数中使用super(name);自定义了线程的名称
		Test t2 = new Test("二号线程+++");//如果不自定义线程的名称,线程有其默认的名称Thread-编号 该编号从0开始。
		t1.start();
		t2.start();
//		t1.run();
//		t2.run();

		for(int x=0; x<60; x++)
		{
			System.out.println("main....."+x);
		}
	}
}
/*
true...二号线程+++ run...0
main.....0
true...一号线程--- run...0
main.....1
true...二号线程+++ run...1
main.....2
true...一号线程--- run...1
true...一号线程--- run...2
...
 */


 
 
版本3:售票厅多线程实现:尝试使用static
package Thread;

class Ticket extends Thread
{
	private static int ticket = 100;
	public void run()
	{
		while(true)
		{
			if(ticket > 0)
			{
				System.out.println(Thread.currentThread().getName()+"....sale : "+ ticket--);
			}
		}
	}
}


class  TicketDemo
{
	public static void main(String[] args) 
	{


		Ticket t1 = new Ticket();
		Ticket t2 = new Ticket();
		Ticket t3 = new Ticket();
		Ticket t4 = new Ticket();

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

	}
}
/*
如果private  int ticket = 100;不使用static将会发生的问题:
一共100张票,4个售票厅各卖了100张,比如其中Thread-3卖了48,Thread-2....sale : 48
Thread-3....sale : 48
Thread-1....sale : 76
Thread-3....sale : 47
Thread-2....sale : 48
Thread-0....sale : 65
Thread-2....sale : 47
 */
版本4:售票厅多线程实现:不使用static(会发生IllegalThreadStateException)
因为static的生命周期太长,一般不使用,要解决不使用静态的问题,尝试使用如下方式:
创建一个Thread对象,让其跑4次,但是发生了IllegalThreadStateException,已经在运行的程序是不需要再次开启的
package Thread;

class Ticket extends Thread
{
	private  int ticket = 100;
	public void run()
	{
		while(true)
		{
			if(ticket > 0)
			{
				System.out.println(Thread.currentThread().getName()+"....sale : "+ ticket--);
			}
		}
	}
}


class  TicketDemo
{
	public static void main(String[] args) 
	{


		Ticket t1 = new Ticket();

		t1.start();
		t1.start();
		t1.start();
		t1.start();

	}
}
/*
Thread-0....sale : 5
Thread-0....sale : 4
Thread-0....sale : 3
Thread-0....sale : 2
Thread-0....sale : 1

Exception in thread "main" java.lang.IllegalThreadStateException
	at java.lang.Thread.start(Unknown Source)
	at Thread.TicketDemo.main(TicketDemo.java:28)

 */

创建线程的第二种方式:实现Runable接口

步骤:
1,定义类实现Runnable接口
2,覆盖Runnable接口中的run方法。将线程要运行的代码存放在该run方法中。
3,通过Thread类建立线程对象。
4,将Runnable接口的子类对象作为实际参数传递给Thread类的构造函数。
5,调用Thread类的start方法开启线程并调用Runnable接口子类的run方法。

为什么要将Runnable接口的子类对象传递给Thread的构造函数?
自定义的run方法所属的对象是Runnable接口的子类对象。所以要让线程去指定一个指定对象的run方法。就必须明确该run方法所属对象。

实现方式和继承方式有什么区别呢?
继承Thread:线程代码存放Thread子类run方法中。
实现Runnable:线程代码存在接口的子类的run方法。
实现方式好处:
避免了单继承的局限性,继承了Thread后,该类就不能继承其他类了。在定义线程时,建议使用实现方式。
版本5:使用实现Runnable方法来完成售票厅多线程程序
package Thread;

//1,定义类实现Runnable接口
class Ticket implements Runnable//extends Thread
{
	private  int ticket = 100;
	
	//2,覆盖Runnable接口中的run方法。将线程要运行的代码存放在该run方法中。
	public void run()
	{
		while(true)
		{
			if(ticket > 0)
			{
				System.out.println(Thread.currentThread().getName()+"....sale : "+ ticket--);
			}
		}
	}
}


class  TicketDemo
{
	public static void main(String[] args) {
		
		Ticket t = new Ticket();
		
		//3,通过Thread类建立线程对象
		//4,将Runnable接口的子类对象作为实际参数传递给Thread类的构造函数。
		Thread t1 = new Thread(t);//对比第一种方式:Ticket t1 = new Ticket();
		Thread t2 = new Thread(t);
		Thread t3 = new Thread(t);
		Thread t4 = new Thread(t);
		
		//5,调用Thread类的start方法开启线程并调用Runnable接口子类的run方法。
		t1.start();//调用的是Thread类中的Run方法,要让它调用Ticket对象中的Run方法,就要传参数给Thread类
		t2.start();
		t3.start();
		t4.start();
	}
}
/*
Thread-0....sale : 9
Thread-2....sale : 10
Thread-0....sale : 5
Thread-3....sale : 6
Thread-1....sale : 7
Thread-3....sale : 2
Thread-0....sale : 3
Thread-2....sale : 4
Thread-1....sale : 1
 */

多线程的安全问题

问题的原因:
当多条语句在操作同一个线程共享数据时,一个线程对多条语句只执行了一部分,还没有执行完,另一个线程参与进来执行。导致共享数据的错误。
解决办法:
对多条操作共享数据的语句,只能让一个线程都执行完。在执行过程中,其他线程不可以参与执行。

制造一个多线程的安全问题

package Thread;

class Ticket2 implements Runnable
{
	private  int ticket = 100;
	
	public void run()
	{
		while(true)
		{
			if(ticket > 0)
			{
				try {
					Thread.sleep(100);//睡上100ms,从而演示一个线程安全问题,sleep方法上有throws Exception的声明,要处理
				} catch (InterruptedException e) {
					// 中断异常
					e.printStackTrace();
				}//只能try..catch,不能抛,因为是复写接口的方法,接口方法不能抛异常
				System.out.println(Thread.currentThread().getName()+"....sale : "+ ticket--);
			}
		}
	}
}

 
class  TicketDemo2
{
	public static void main(String[] args) {
		
		Ticket2 t = new Ticket2();
		
		Thread t1 = new Thread(t);//对比第一种方式:Ticket t1 = new Ticket();
		Thread t2 = new Thread(t);
		Thread t3 = new Thread(t);
		Thread t4 = new Thread(t);
		
		t1.start();
		t2.start();
		t3.start();
		t4.start();
	}
}

/* 
产生的问题:
Thread-0....sale : 100
Thread-1....sale : 99
Thread-1....sale : 97
Thread-2....sale : 99
Thread-3....sale : 100
....
 */


多线程安全问题的解决:同步代码块

Java对于多线程的安全问题提供了专业的解决方式,就是同步代码块。

格式:
synchronized(对象)
{
	需要被同步的代码
}
对上面多线程安全问题的解决
package Thread;

class Ticket2 implements Runnable
{
	private  int ticket = 100;
	Object obj = new Object();//synchronized(对象),需要传入一个对象,可以用上帝
	public void run()
	{
		while(true)
		{
			if(ticket > 0)
			{
				synchronized(obj){//加了一把锁,进入其中的线程锁住门,此时即使进入的线程放弃了执行权,其他进程也暂时进不去。只有它出去才打开锁
					try {
						Thread.sleep(100);//睡上100ms,从而演示一个线程安全问题,sleep方法上有throws Exception的声明,要处理
					} catch (InterruptedException e) {
						// 中断异常
						e.printStackTrace();
					}//只能try..catch,不能抛,因为是复写接口的方法,接口方法不能抛异常
					System.out.println(Thread.currentThread().getName()+"....sale : "+ ticket--);
				}
			}
		}
	}
}

 
class  TicketDemo2
{
	public static void main(String[] args) {
		
		Ticket2 t = new Ticket2();
		
		Thread t1 = new Thread(t);//对比第一种方式:Ticket t1 = new Ticket();
		Thread t2 = new Thread(t);
		Thread t3 = new Thread(t);
		Thread t4 = new Thread(t);
		
		t1.start();
		t2.start();
		t3.start();
		t4.start();
	}
}




同步的前提:
1,必须要有两个或者两个以上的线程。
2,必须是多个线程使用同一个锁。
同步的利弊:
好处:解决了多线程的安全问题。
弊端:多个线程需要判断锁,较为消耗资源。

多线程安全问题的解决:同步函数

如何找问题:
1,明确哪些代码是多线程运行代码。
2,明确哪些数据是共享数据。
3,明确多线程运行代码中哪些语句是操作共享数据的。
存款问题
package Thread;

class Bank
{
	private int sum;
	//Object obj = new Object();
	public synchronized void add(int n)//使用synchronized关键字使函数具备同步性
	{
		//synchronized(obj)
		//{
			sum = sum + n;
			try{Thread.sleep(10);}catch(Exception e){}
			System.out.println(Thread.currentThread().getName() + " sum="+sum);
		//}
	}
}

class Cus implements Runnable
{
	private Bank b = new Bank();
	public void run()
	{		
		for(int x=0; x<3; x++)
		{
			b.add(100);//存三次的行为两个线程都使用一遍。这里的add也是先在run()方法外被封装好,继而再在run()中被调用
		}
	}
}


class  BankDemo
{
	public static void main(String[] args) 
	{
		Cus c = new Cus();
		Thread t1 = new Thread(c);
		Thread t2 = new Thread(c);
		t1.start();
		t2.start();
	}
}
/*
Thread-0 sum=100
Thread-0 sum=200
Thread-0 sum=300
Thread-1 sum=400
Thread-1 sum=500
Thread-1 sum=600
 */

同步函数的锁是this
同步函数用的是哪一个锁?
函数需要被对象调用。那么函数都有一个所属对象引用。就是this。
所以同步函数使用的锁是this。
package Thread;

/**

使用两个线程来买票:
一个线程在同步代码块中。
一个线程在同步函数中。
都在执行买票动作。

 */
class Ticket3 implements Runnable
{
	private  int tick = 100;
	Object obj = new Object();
	boolean flag = true;//两个线程分别在两个while(ture)中
	public  void run()
	{
		if(flag)
		{
			while(true)
			{
				synchronized(this)//如果此处用的是Obj,那么两个同步部分所使用的锁就不一样了,会产生线程不安全的问题
				//使用run方法的所属对象引用:this
				{
					if(tick>0)
					{
						try{Thread.sleep(10);}catch(Exception e){}
						System.out.println(Thread.currentThread().getName()+"....code : "+ tick--);
					}
				}
			}
		}
		else
			while(true)
				show();//同步函数部分,调用封装后的show();
		/*
		 假如直接这样做:
		 public “synchronized”void run(){
		 	while(true){
		 			if(tick>0)
					{
						try{Thread.sleep(10);}catch(Exception e){}
						System.out.println(Thread.currentThread().getName()+"....show.... : "+ tick--);
					}
		 	}
		 }
			0线程进入到其中后,其他进程即使获得执行权,也无法进入其代码块中执行语句,结果是只有0线程在无限循环体中
		 */
	}
	
	
	//show()把要执行的部分单独封装起来,run()中调用封装好的同步函数
	public synchronized void show()//它的锁是this
	{
		if(tick>0)
		{
			try{Thread.sleep(10);}catch(Exception e){}
			System.out.println(Thread.currentThread().getName()+"....show.... : "+ tick--);
		}
	}
}


class  ThisLockDemo
{
	public static void main(String[] args) 
	{
		Ticket3 t = new Ticket3();

		Thread t1 = new Thread(t);
		Thread t2 = new Thread(t);
		t1.start();
		try{Thread.sleep(10);}catch(Exception e){}//防止主线程瞬间完成,开启完t后,停个10ms
		t.flag = false;
		t2.start();

	}
}

/*
Thread-1....show.... : 65
Thread-1....show.... : 64
Thread-0....code : 63
Thread-0....code : 62
Thread-0....code : 61
 */


静态同步函数的锁是Class对象
静态进内存是,内存中没有本类对象,但是一定有该类对应的字节码文件对象。
类名.class  该对象的类型是Class
静态的同步方法,使用的锁是该方法所在类的字节码文件对象。 类名.class
延迟加载的单例设计模式示例:
class Single
{
	private static Single s = null;
	private Single(){}


	public static Single getInstance()
	{
		if(s==null)//双重判断,解决效率问题,把后进来的线程都挡在这把“锁”之外,不必再进入到synchronized的锁来判断,减少了许多判断次数
		{
			synchronized(Single.class)//静态所使用的锁。synchronized不一定非要在多线程时使用,本质上是一把锁
			{
				if(s==null)
					//如果没有同步,懒汉式在多线程访问时的安全隐患
					//--->A挂着
					//--->B挂着,创建了不止一个实例
					s = new Single();
			}
		}
		return s;
	}
}

class SingleDemo 
{
	public static void main(String[] args) 
	{
		System.out.println("Hello World!");
	}
}

线程的通信

线程间通信:其实就是多个线程在操作同一个资源,但是操作的动作不同。

小例子

未经处理的情况下,在线程通信时所产生的问题
package Thread;

class Res//创建一个Resource的类,用于给不同的线程共同使用
{
	String name;
	String sex;
	//boolean flag = false;
}

class In implements Runnable
{
	private Res r ;
	In(Res r)//Input类的构造函数,传入Resource对象的引用r
	{
		this.r = r;
	}
	
	public void run()//复写run方法,放入Input的运行代码
	{
		int x = 0;
		while(true)
		{
			//synchronized(r)
			//{

				//if(r.flag)
					//try{r.wait();}catch(Exception e){}
				if(x==0)
				{
					r.name="周瑜";
					r.sex="男";
				}
				else
				{
					r.name="孙尚香";
					r.sex = "----女";
				}
				x = (x+1)%2;//可以控制反复相间进入if..else代码块中
				//r.flag = true;
				//r.notify();
			//}
		}
	}
}

class Out implements Runnable
{
	private Res r ;
	
	Out(Res r)
	{
		this.r = r;
	}
	public void run()
	{
		while(true)
		{
			//synchronized(r)
			//{
				//if(!r.flag)
				//	try{r.wait();}catch(Exception e){}
		System.out.println(Thread.currentThread().getName() + r.name +"...."+r.sex);
				//r.flag = false;
				//r.notify();
			//}
		}
	}
}


class  IOWrongDemo
{
	public static void main(String[] args) 
	{
		Res r = new Res();
		
		In in = new In(r);//Ticket t = new Ticket();比较
		Out out = new Out(r);

		Thread t1 = new Thread(in);//Thread t1 = new Thread(t);
		Thread t2 = new Thread(out);

		t1.start();
		t2.start();
	}
}

/* 
线程通信时产生的问题:
Thread-1周瑜....----女
Thread-1周瑜....男
Thread-1孙尚香....男
Thread-1周瑜....男
Thread-1周瑜....男
Thread-1周瑜....----女
Thread-1周瑜....----女
 */

安全问题的解决:使用synchronzied

package Thread;

class Resource//创建一个Resource的类,用于给不同的线程共同使用
{
	String name;
	String sex;
	//boolean flag = false;
}

class Input implements Runnable
{
	private Resource r ;
	Input(Resource r)//Input类的构造函数,传入Resource对象的引用r
	{
		this.r = r;
	}
	
	public void run()//复写run方法,放入Input的运行代码
	{
		int x = 0;
		while(true)
		{
			synchronized(r)
			/*
			 假如用的是obj的锁,问题没有解决,此处同步的其实只有一个输入线程,并没有同步多个线程
			 遥遥相望的Output中System.out.println(Thread.currentThread().getName() + r.name +"...."+r.sex);
			 与此处的Input在处理同一个资源,所以加在一起才是两个线程,需要在Output的输出语句中加锁
			 并且Input中的锁应当与Output中的锁一致,用this锁也不行,因为this是本类对象的引用,他们各自都有引用
			 内存中唯一的对象就是Resource,所以用Resource的引用r传入synchronized,作为锁
			 */
			
			{

				//if(r.flag)
					//try{r.wait();}catch(Exception e){}
				if(x==0)
				{
					r.name="周瑜";
					r.sex="男";
				}
				else
				{
					r.name="孙尚香";
					r.sex = "----女";
				}
				x = (x+1)%2;//可以控制反复相间进入if..else代码块中
				//r.flag = true;
				//r.notify();
			}
		}
	}
}

class Output implements Runnable
{
	private Resource r ;
	
	Output(Resource r)
	{
		this.r = r;
	}
	public void run()
	{
		while(true)
		{
			synchronized(r)
			{
				//if(!r.flag)
					//try{r.wait();}catch(Exception e){}
				System.out.println(Thread.currentThread().getName() + r.name +"...."+r.sex);
				//r.flag = false;
				//r.notify();
			}
		}
	}
}


class  InputOutputDemo
{
	public static void main(String[] args) 
	{
		Resource r = new Resource();
		
		Input in = new Input(r);//Ticket t = new Ticket();比较
		Output out = new Output(r);

		Thread t1 = new Thread(in);//Thread t1 = new Thread(t);
		Thread t2 = new Thread(out);

		t1.start();
		t2.start();
	}
}
/*
出现的问题:
不能Input一个,就Output一个,孙尚香和周瑜没有交替出现。Input抢到执行权,就一股脑Input。Output一旦抢到执行权,就一股脑Output
Thread-1孙尚香....----女
Thread-1孙尚香....----女
Thread-1孙尚香....----女
Thread-1孙尚香....----女
Thread-1孙尚香....----女
Thread-1周瑜....男
Thread-1周瑜....男
Thread-1周瑜....男
Thread-1周瑜....男
 */

安全问题解决的优化:使用等待唤醒机制

wait,notify(),notifyAll():为什么这些操作线程的方法要定义Object类中呢?
因为这些方法在操作同步中线程时,都必须要标识它们所操作线程只有的锁,而等待和唤醒必须是同一个锁。
但锁可以是任意对象,所以可以被任意对象调用的方法定义Object类中。

package Thread;

class Resource//创建一个Resource的类,用于给不同的线程共同使用
{
	String name;
	String sex;
	boolean flag = false;//等待唤醒机制需要在Resource中加一个flag,并置为false
}

class Input implements Runnable
{
	private Resource r ;
	Input(Resource r)//Input类的构造函数,传入Resource对象的引用r
	{
		this.r = r;
	}
	
	public void run()//复写run方法,放入Input的运行代码
	{
		int x = 0;
		while(true)
		{
			synchronized(r)
			/*
			 假如用的是obj的锁,问题没有解决,此处同步的其实只有一个输入线程,并没有同步多个线程
			 遥遥相望的Output中System.out.println(Thread.currentThread().getName() + r.name +"...."+r.sex);
			 与此处的Input在处理同一个资源,所以加在一起才是两个线程,需要在Output的输出语句中加锁
			 并且Input中的锁应当与Output中的锁一致,用this锁也不行,因为this是本类对象的引用,他们各自都有引用
			 内存中唯一的对象就是Resource,所以用Resource的引用r传入synchronized,作为锁
			 */
			
			{

				if(r.flag)//flag为true的时候,该线程就等着别动,执行r.wait()
					try{r.wait();}catch(Exception e){}//wait(),等着别动,我叫你动,你再动。要等到值被全部取走之后才能动
				if(x==0)
				{
					r.name="周瑜";
					r.sex="男";
				}
				else
				{
					r.name="孙尚香";
					r.sex = "----女";
				}
				x = (x+1)%2;//可以控制反复相间进入if..else代码块中
				r.flag = true;//Input执行完毕之后,就把flag改为true
				r.notify();//唤醒Output,告诉它我输入完了,可以输出了
			}
		}
	}
}

class Output implements Runnable
{
	private Resource r ;
	
	Output(Resource r)
	{
		this.r = r;
	}
	public void run()
	{
		while(true)
		{
			synchronized(r)
			{
				if(!r.flag)//注意Input中,此处是if(r.flag),对Output来说,如果flag是true,那么它就等着别动,让Input动
					try{r.wait();}catch(Exception e){}
				System.out.println(Thread.currentThread().getName() + r.name +"...."+r.sex);
				r.flag = false;//Input中此处是r.flag = true;
				r.notify();//唤醒Input,告诉它我输出完了,可以输入了
			}
		}
	}
}


class  InputOutputDemo
{
	public static void main(String[] args) 
	{
		Resource r = new Resource();
		
		Input in = new Input(r);//Ticket t = new Ticket();比较
		Output out = new Output(r);

		Thread t1 = new Thread(in);//Thread t1 = new Thread(t);
		Thread t2 = new Thread(out);

		t1.start();
		t2.start();
	}
}
/*
问题解决后的情况:
Input一个,就Output一个,孙尚香和周瑜交替出现
Thread-1周瑜....男
Thread-1孙尚香....----女
Thread-1周瑜....男
Thread-1孙尚香....----女
Thread-1周瑜....男
Thread-1孙尚香....----女
 */

安全问题解决的再优化:私有化变量、匿名对象直接调用方法

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){}
		System.out.println(name+"........"+sex);
		flag = false;
		this.notify();
	}
}

class Input implements Runnable
{
	private Res r ;
	Input(Res r)
	{
		this.r = r;
	}
	public void run()
	{
		int x = 0;
		while(true)
		{
			if(x==0)				
				r.set("周瑜","男");				
			else	
				r.set("孙尚香","----女");				
			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  InputOutputDemo2
{
	public static void main(String[] args) 
	{
		Res r = new Res();

		new Thread(new Input(r)).start();//Thread使用了匿名对象,直接调用start方法
		new Thread(new Output(r)).start();
		
		/*
		比较:
		Input in = new Input(r);//Ticket t = new Ticket();比较
		Output out = new Output(r);

		Thread t1 = new Thread(in);//Thread t1 = new Thread(t);
		Thread t2 = new Thread(out);

		t1.start();
		t2.start();
		 */

	}
}

生产者消费者问题:多个线程生产,多个线程消费(while&notifyAll)

之前的输入输出例子中,是一个线程生产,一个线程消费。现在将问题进行扩展,假如两个线程生产,两个线程消费,那么如何进行处理?
定义while判断标记:
原因:让被唤醒的线程再一次判断标记。
定义notifyAll:
因为需要唤醒对方线程。只用notify,容易出现只唤醒本方线程的情况。导致程序中的所有线程都等待。
package Thread;



class ProducerConsumerDemo 
{
	public static void main(String[] args) 
	{
		Resource2 r = new Resource2();

		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();

	}
}


class Resource2
{
	private String name;
	private int count = 1;
	private boolean flag = false;
			//  t1    t2 生产者
	public synchronized void set(String name)
	{
		while(flag)//原因:让被唤醒的线程再一次判断标记。不会醒来之后直接从wait之后的语句开始执行
			try{this.wait();}catch(Exception e){}//t1(放弃资格)  t2(获取资格)
		this.name = name+"--"+count++;//count用来统计商品数

		System.out.println(Thread.currentThread().getName()+"...生产者.."+this.name);
		flag = true;
		this.notifyAll();//只用notify,容易出现只唤醒本方线程的情况。当使用while(flag)时导致程序中的所有线程都等待。

	}


	//  t3   t4  消费者
	public synchronized void out()
	{
		while(!flag)
			try{wait();}catch(Exception e){}//t3(放弃资格) t4(放弃资格)
		System.out.println(Thread.currentThread().getName()+"...消费者........."+this.name);
		flag = false;
		this.notifyAll();
	}
}

class Producer implements Runnable
{
	private Resource2 res;

	Producer(Resource2 res)
	{
		this.res = res;
	}
	public void run()
	{
		while(true)
		{
			res.set("+商品+");
		}
	}
}

class Consumer implements Runnable
{
	private Resource2 res;

	Consumer(Resource2 res)
	{
		this.res = res;
	}
	public void run()
	{
		while(true)
		{
			res.out();
		}
	}
}

/*
程序结果:Thread-1和Thread-0是生产者,Thread-2和Thread-3是消费者
生产者生产一个商品,消费者消费一个商品
Thread-1...生产者..+商品+--17392
Thread-2...消费者.........+商品+--17392
Thread-1...生产者..+商品+--17393
Thread-3...消费者.........+商品+--17393
Thread-0...生产者..+商品+--17394
Thread-3...消费者.........+商品+--17394
Thread-1...生产者..+商品+--17395
Thread-2...消费者.........+商品+--17395
Thread-1...生产者..+商品+--17396
Thread-3...消费者.........+商品+--17396
Thread-0...生产者..+商品+--17397
Thread-3...消费者.........+商品+--17397
Thread-1...生产者..+商品+--17398
Thread-2...消费者.........+商品+--17398
Thread-1...生产者..+商品+--17399
Thread-3...消费者.........+商品+--17399
Thread-0...生产者..+商品+--17400
Thread-3...消费者.........+商品+--17400
 */

生产者消费者问题:JDK1.5处理方法

JDK1.5 中提供了多线程升级解决方案。
1.将同步Synchronized替换成显式的Lock操作。
2.将Object中的wait,notify notifyAll,替换了Condition对象。
该对象可以Lock锁 进行获取。
该示例中,实现了本方只唤醒对方操作。
Lock:替代了Synchronized
lock 
unlock
newCondition()
Condition:替代了Object wait notify notifyAll
await();
signal();
signalAll();
import java.util.concurrent.locks.*;

class Resource
{
	private String name;
	private int count = 1;
	private boolean flag = false;
			//  t1    t2
	private Lock lock = new ReentrantLock();//创建一个锁的对象

	private Condition condition_pro = lock.newCondition();//根据所创建出的锁的对象,来创建一个Condition对象
	private Condition condition_con = lock.newCondition();



	public  void set(String name)throws InterruptedException
	{
		lock.lock();//进来先锁上
		try
		{
			if(flag)
				condition_pro.await();//t1,t2 生产者进行等待
				//如果只写condition.await(),而又不用signalAll,则会全等着,卡住不动
				//但是singalAll除了唤醒对方之外,还唤醒了本方,所以要使用while(flag)
				//利用condition,可以不唤醒本方,而只唤醒对方,这样即使使用if(flag)也不会发生问题。不用signalAll,只用signal
			this.name = name+"--"+count++;

			System.out.println(Thread.currentThread().getName()+"...生产者.."+this.name);
			flag = true;
			condition_con.signal(); //唤醒消费者
		}
		finally//释放锁的动作一定要执行,所以要放在finally中
		{
			lock.unlock();//出去释放锁这个资源
		}
	}


	//  t3   t4  
	public  void out()throws InterruptedException
	{
		lock.lock();
		try
		{
			if(!flag)
				condition_con.await();//消费者等待
			System.out.println(Thread.currentThread().getName()+"...消费者........."+this.name);
			flag = false;
			condition_pro.signal();//唤醒生产者
		}
		finally
		{
			lock.unlock();
		}
		
	}
}

class Producer implements Runnable
{
	private Resource res;

	Producer(Resource res)
	{
		this.res = res;
	}
	public void run()
	{
		while(true)
		{
			try
			{
				res.set("+商品+");//set方法throws了一个InterruptedException,使用这个方法时必须要进行异常处理
			}
			catch (InterruptedException e)
			{
			}
			
		}
	}
}

class Consumer implements Runnable
{
	private Resource res;

	Consumer(Resource res)
	{
		this.res = res;
	}
	public void run()
	{
		while(true)
		{
			try
			{
				res.out();//out方法throws了一个InterruptedException,使用这个方法时必须要进行异常处理
			}
			catch (InterruptedException e)
			{
			}
		}
	}
}
/* 
Thread-1...生产者..+商品+--38213
Thread-2...消费者.........+商品+--38213
Thread-0...生产者..+商品+--38214
Thread-3...消费者.........+商品+--38214
Thread-1...生产者..+商品+--38215
Thread-2...消费者.........+商品+--38215
Thread-0...生产者..+商品+--38216
Thread-3...消费者.........+商品+--38216
 */

Thread类中的其他操作方法

停止线程:stop方法(已过时)的替换方法

stop方法已经过时。如何停止线程?
run方法结束:
开启多线程运行,运行代码通常是循环结构。只要控制住循环,就可以让run方法结束,也就是线程结束。

class StopThread implements Runnable
{
	private boolean flag = true;//设置一个flag,用于给changeFlag来改变开关状态,以控制while循环,进而控制线程的执行次数
	public  void run()
	{
		while(flag)
		{
			
			System.out.println(Thread.currentThread().getName()+"....run");
		}
	}
	public void changeFlag()
	{
		flag = false;
	}
}


class  StopThreadDemo
{
	public static void main(String[] args) 
	{
		StopThread st = new StopThread();
		
		Thread t1 = new Thread(st);
		Thread t2 = new Thread(st);


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

		int num = 0;

		while(true)
		{
			if(num++ == 60)
			{
				st.changeFlag();
				/*
				 Thread-0....run
				 main.......57
				 main.......58
				 main.......59
				 main.......60
				 over
				 Thread-0....run
				 */
				//t1.interrupt();强制将处于冻结状态的线程重新回到运行状态中
				//t2.interrupt();
				break;
			}
			System.out.println(Thread.currentThread().getName()+"......."+num);
		}
		System.out.println("over");
	}
}

特殊情况:当线程处于冻结状态,就不会读取到标记。那么线程就不会结束。
当没有指定的方式让冻结的线程恢复到运行状态是,这时需要对冻结进行清除。
强制让线程恢复到运行状态中来。这样就可以操作标记让线程结束。Thread类提供该方法 interrupt();
package Thread;

class StopThread implements Runnable
{
	private boolean flag = true;//设置一个flag,用于给changeFlag来改变开关状态,以控制while循环,进而控制线程的执行次数
	public synchronized void run()
	{
		while(flag)
		{
			try {
				wait();
			} catch (InterruptedException e) {
				System.out.println(Thread.currentThread().getName()+"....Exception");
				flag = false;
				/*
				 如果没有flag = false;时的运行情况
				 main.......56
				 main.......57
				 main.......58
				 main.......59
				 main.......60
				 over
				 Thread-0....Exception
				 Thread-0....run
				 */
			}
			System.out.println(Thread.currentThread().getName()+"....run");
		}
	}
	public void changeFlag()
	{
		flag = false;
	}
}


class  StopThreadDemo
{
	public static void main(String[] args) 
	{
		StopThread st = new StopThread();
		
		Thread t1 = new Thread(st);
		Thread t2 = new Thread(st);


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

		int num = 0;

		while(true)
		{
			if(num++ == 60)
			{
				//st.changeFlag();
				t1.interrupt();//强制将处于冻结状态的线程重新回到运行状态中
				t2.interrupt();
				break;
			}
			System.out.println(Thread.currentThread().getName()+"......."+num);
		}
		System.out.println("over");
	}
}


/*
运行结果:
main.......56
main.......57
main.......58
main.......59
main.......60
over

 */

join,yield,setPriority,setDaemon

join方法
当A线程执行到了b线程的join方法时,A线程等待,一直等到B线程都执行完,A线程才会再执行。
yield方法
暂停当前正在执行的线程对象,并执行其他线程。
setPriority方法
用来设置优先级
MAX_PRIORITY 最高优先级10
MIN_PRIORITY   最低优先级1
NORM_PRIORITY 分配给线程的默认优先级
setDaemon方法
Daemon线程(守护线程,后台线程)。如果普通线程结束了,则后台线程自动终止。如垃圾回收线程就是后台线程,使用setDaemon(true)方法。

 
 








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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值