天天看点

【多线程】的学习总结

关于【多线程】的学习总结

作者:wkz_crystal基本的概念:多线程其实就是进程中一个独立的控制单元或者说是执行路径,线程控制着进程的执行,【重点】一个进程中,至少有一个线程存在。

【1】【线程的创建】怎样继承Thread类,创建多个线程?这个和实现Runnable接口创建的方式有什么区别?

【2】创建线程为什么要覆写run方法和为什么在main方法中执行线程程序,每一次运行的效果都不一样?

【3】怎样获得当前线程的名称?

【4】【重点】线程中存在的安全问题是什么,怎样发现线程安全?

【5】【重点】实例分析:汽车站售票程序【其中涉及了线程同步】

【6】同步的两种表现形式是什么?

【7】懒汉式单例模式分析

【8】多线程的死锁问题

【9】JDK1.5中关于线程的新特性

【10】多线程之间的通信模式分析

【11】停止线程的方法

【12】join和yield方法总结

【13】开发过程中为了提高效率,怎样单独封装?

我根据实例来总结:分享给大家

【1】继承Thread类创建线程和实现Runnable接口来创建线程:

Thread类是一个线程类,我们在具体定义定义多线程开发时,有两种方式创建线程,其中一种就是通过继承的方式来完成,覆写Thread类中的run方法:但是这种创建方式有一定的弊端:那就是被创建的子类不能再继承其他的类;为了解决这种弊端,一般我们都直接去实现Runnable接口去实现多线程的创建,这其实也正是JAVA中解决多态性的具体方案,实现一个接口之后,也能去继承其他的父类或者被子类继承!

原理明白了,好了,写个简单的程序演示演示吧:

class Mythread extends Thread//这里就是一个简单的继承创建线程的方式
{
	public void run()//覆写了父类中的run方法,定义了自定义的函数主体for循环
	{
		for (int i=0;i<50 ;i++ )
		{
			System.out.println("thread hao!="+i);//仅仅是演示线程执行的内容
		}
		
	}
}
//这里就是通过main方法来调用
class ThreadDemo1 
{
	public static void main(String[] args) 
	{
		Mythread mt=new Mythread();//把这个线程对象实例化
		mt.start();//调用父类的start方法,启动并执行线程
	}
}
           
class Cus implements Runnable//这里是通过实现Runnable接口的方式创建多线程的
{
	public void run()//同理也要覆写接口的run方法
	{
		for (int i=0;i<3;i++ )//简单的函数主体
		{
			System.out.println("....."+i);//用于演示而已
		}
	}
}

class SynchronizedDemo4 
{
	public static void main(String[] args) 
	{
		Cus c=new Cus();//创建接口的子类对象
		Thread c1=new Thread(c);//定义了两个线程,把接口的子类对象和线程相关联起来
		Thread c2=new Thread(c);
		c1.start();//启动两个线程并且执行
		c2.start();
	}
}
           

【2】为什么要复写run方法:

复写run方法主要是因为:在Thread类中,run方法是储存线程要运行的重要代码,所以在定义的时候,我们必须根据具体的需求去覆写run方法,从而自定义要实现的功能主体!

start方法是父类开启和执行线程的方法,直接调用就行了,但是我们如果不覆写run方法,直接在子类中调用的话,是毫无意义的,因为这样的话,相当于我们只是创建了线程,而没有运行!

在main方法中,多次运行多线程的结果都不一定一样的原因是:我们知道,多线程在执行的时候,都是在获取cpu的一个执行权,cpu说想把执行权给谁(线程),谁(线程)就去执行相应的操作!但是在某一时刻,只能有一个程序在运行,当然多核除外

这也就反应除了多线程的一个重要特性:那就是随机性!(可以理解为:谁抢到谁就执行)

【3】获取当前线程的名称:

很简单了:用线程类的静态方法就可以:

Thread.currentThread().getName():这样就能够获得当前线程的名称:

线程名称的基本格式是:Thread-编号,编号都是从0开始的

【4】发现线程安全:

可以模拟出线程在运行中出现的问题:在这里卖票系统中就是最好的分析

一般通过下列代码就能找到其安不安全的地方!因为现在只要在这里休眠一下,其他线程自然会获得cpu的执行权进来,这样就没法保证在共享的代码中,不出现问题:在不同步的情况下,售票窗口可能会售出0号,甚至是-1号票,这样就要求我们必须要保证共享数据的同步性:

对于发现线程的安全问题:

具体步骤:

分析:哪里出现了线程的安全问题

如何去找原因:

1.明确哪些代码是多线程运行的代码:

2.明确哪里是共享的数据

3.明确多线程的运行代码中哪里涉及到了要执行共享的数据

<span style="white-space:pre">					</span>//这种简单的方式只是模拟出现问题而已:
           
<span style="white-space:pre">					</span>try
					{
						Thread.sleep(10);
					}
					catch (Exception e)
					{
					}
           

【5】汽车站售票小程序:

模拟卖票窗口,4个窗口同时卖100张共同的票

/*
实现Runnable接口的具体步骤和解决安全性的问题
*/

//1、继承Thread类的做法:
/*
class Ticket extends Thread
{
	private static int tickets=100;  
	//为了确保票的唯一性,必须用static修饰,否则每个线程窗口会各自卖100张票

	//覆写父类的run方法,定义线程卖票方式
	public void run()
	{
		while (true)
		{
			if (tickets>0) //当票大于0时,才能出票
			{
				//打印结果,获取各线程(窗口)卖票情况
				System.out.println(Thread.currentThread().getName()+".....sale:"+tickets --);
			}
		}
	}
}

class ThreadDemo3 
{
	public static void main(String[] args) 
	{
		Ticket t1=new Ticket();
		Ticket t2=new Ticket();
		Ticket t3=new Ticket();
		Ticket t4=new Ticket();  //创建4个窗口(4个线程)
		
		t1.start();
		t2.start();
		t3.start();
		t4.start(); //启动并执行窗口的卖票

		/*
		部分结果:
		Thread-2.....sale:76
		Thread-2.....sale:75
		Thread-2.....sale:74
		Thread-3.....sale:80
		Thread-1.....sale:81
		Thread-1.....sale:71
		Thread-1.....sale:70
		Thread-3.....sale:72
		Thread-2.....sale:73
		Thread-0.....sale:78
		Thread-2.....sale:67
		Thread-2.....sale:65
		Thread-3.....sale:68
		Thread-1.....sale:69
		Thread-3.....sale:63
		Thread-2.....sale:64
		
	}
}
*/
//实现Runnable接口
class Ticket implements Runnable
{
	private int tickets=100;
	Object obj=new Object();
	public void run()
	{
		while (true)
		{
			synchronized(obj)
			{
				if (tickets>0)
				{
					/*这里是模拟cpu让进来的某一个线程没有分配到执行权,必然会售出不正常的票
					try
					{
						Thread.sleep(10);
					}
					catch (Exception e)
					{
					}
					*/

					//为了解决这样的安全问题,要用到同步代码块
					try
					{
						Thread.sleep(10);
					}
					catch (InterruptedException e)
					{
					}
					System.out.println(Thread.currentThread().getName()+"....sale:"+tickets --);
				}else
				{
					break;
				}
			}
		}
	}
}
class ThreadDemo3 
{
	public static void main(String[] args) 
	{
		Ticket t=new Ticket();
		Thread t1=new Thread(t);
		Thread t2=new Thread(t);
		Thread t3=new Thread(t);
		Thread t4=new Thread(t);

		/*自定义线程名称的方式:
		Thread t1=new Thread(t,"窗口1");
		Thread t2=new Thread(t,"窗口2");
		Thread t3=new Thread(t,"窗口3");
		Thread t4=new Thread(t,"窗口4");
		
		部分结果:
		窗口1....sale:44
		窗口1....sale:43
		窗口1....sale:42
		窗口1....sale:41
		窗口1....sale:40
		窗口4....sale:80
		窗口2....sale:81
		窗口2....sale:37
		窗口2....sale:36
		窗口2....sale:35
		窗口2....sale:34
		窗口2....sale:33
		窗口2....sale:32
		窗口2....sale:31
		*/

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

	}
}
           

【6】同步的两种表现形式:

一种是代码块同步,一种是函数同步:

/*
练习:
银行有一个小金库,它有两个顾客,每个顾客向金库中各存300元钱
,分三次存入,一次是存入100元


【注意】
分析:哪里出现了线程的安全问题
如何去找原因:
1.明确哪些代码是多线程运行的代码:
2.明确哪里是共享的数据
3.明确多线程的运行代码中哪里涉及到了要执行共享的数据


*/


/*
//定义银行类
class Bank
{
	//定义金库
	private int sum=0;

	Object obj=new Object(); //定义了一个万能的上帝对象,去实现同步代码块

	//定义银行中的存钱方式
	public void add(int n)
	{
		synchronized(obj)
		{
			sum += n; //相当于sum=sum+n;(1)
			try
			{
				Thread.sleep(10);
			}
			catch (InterruptedException i)
			{
			}
			System.out.println(Thread.currentThread().getName()+"sum="+sum);//(2)

			//综上所述的分析方法:得知(1)和(2)是多线程中都要执行的共享数据
			//所以对此代码块必须实现同步,上面的sleep方法是模拟了出问题的位置
		}
		
	}
}
*/

/*
看上述的代码块知道:其实现同步的代码其实就是银行中的add方法代码主体
所以既然定义了同步该方法主体的同步,同理也可以定义add方法函数的同步、
所以便有了下列的定义函数的同步的方法,很简单易行
*/
class Bank
{
	private int sum=0;
	public synchronized void add(int n)
	{
			sum += n;
			try
			{
				Thread.sleep(10);
			}
			catch (InterruptedException i)
			{
			}
			System.out.println(Thread.currentThread().getName()+": sum="+sum);
	}
}

class Cus implements Runnable
{
	private Bank b=new Bank();
	public void run()
	{
		for (int i=0;i<3;i++ )
		{
			b.add(100);
		}
		//为什么不在for循环上加同步,主要考虑到加了同步,意思就是要等到进来的某一线程(即顾客)
		//依次存完之后,才能让其他的顾客存钱,那样的话就是有序的了
	}
}

class SynchronizedDemo4 
{
	public static void main(String[] args) 
	{
		Cus c=new Cus();
		Thread c1=new Thread(c);
		Thread c2=new Thread(c);
		c1.start();
		c2.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
		*/
	}
}
           

【7】懒汉式单例模式具体分析:

/*
写两种方式的单例模式:注意其区别
*/

/*
	饿汉式的单例模式

class Single
{
	private static final Single s=new Single();
	private Single(){}
	public static Single getInstance()
	{
		return s;
	}

}

*/

/*
	懒汉式的单例模式(延迟加载)
*/
class Single 
{
	private static Single s=null;
	private Single(){}
	//1.为了解决这种安全问题,可以同步函数的方法解决,但是每个线程进来都必须判断锁,效率比较低
	//private static synchronized Single getInstance()

	//2.为了提高效率,可以用同步代码块的方法解决
	public static Single getInstance()
	{
		if (s==null) //只要有一个线程执行成功,那么以后进来的线程就不再判断锁了,
		//因为s已经不为null了,直接不能创建单例的对象了,直接用就行了
		{
			synchronized(Single.class)
			{
				if (s==null)
				{
					//--0线程
					//--1线程
					//当0线程醒了,会创建一个对象
					//当1线程醒了,也会创建一个对象
					//……这就是出现了安全问题,不是单例模式了
					s=new Single();
					System.out.println(Thread.currentThread().getName()+"访问成功!");
				}
			}
		}	
		return s;
	}
}

class SingleDemo6 
{
	public static void main(String[] args) 
	{
		Single.getInstance();//在main方法中通过单例类对外提供的getInstance方法访问
		//结果:main访问成功!
	}
}
           

【8】多线程的死锁问题:

什么是死锁:两个线程彼此之间抢锁的情况,互不相让,就产生死锁

代码分析:

/*
	自己写一个死锁程序
	用锁的嵌套方式来模拟死锁
	模拟了两个线程彼此之间抢锁的情况,互不相让,就产生死锁
*/

class TestDeaDLock implements Runnable
{
	private boolean flag;//设置flag是为了让线程进入不一样的锁中去执行

	TestDeaDLock(boolean flag)//在Runnable子类初始化时就设置flag
	{
		this.flag=flag;
	}
	public void run()
	{
		if (flag)
		{
			synchronized(MyLock.m1)
			{
				System.out.println("true:----lock! m1 ");
				//演示:拿着锁1的线程想进入具备锁2的内容中去,必然要拿到锁2才行
				//此时持有锁2的线程可能会把锁2给持着锁1的线程,但是也可能互不想让,导致死锁的产生
				synchronized(MyLock.m2)
				{
					System.out.println("true:----lock m2 !");

				}
			}
			
		}else
		{
			synchronized(MyLock.m2)
			{
				System.out.println("false:----lock m2 !");
				//演示:拿着锁2的线程想进入具备锁1的内容中去,必然要拿到锁1才行
				synchronized(MyLock.m1)
				{
					System.out.println("false:----lock m1 !");

				}
			}
		}
	}
}

class MyLock //定义自己的锁类,其中有两种锁
{
	static MyLock m1=new MyLock(); //设置锁1
	static MyLock m2=new MyLock();//设置锁2
}

class DeadLockDemo7 
{
	public static void main(String[] args) 
	{
		Thread t1=new Thread(new TestDeaDLock(true)); //一个设置为true
		Thread t2=new Thread(new TestDeaDLock(false));//一个设置为false,主要是为了能够进入不一样的锁中执行
		
		t1.start();
		t2.start();	
			/*
			演示结果可能会出现:
			true:----lock! m1
			false:----lock m2 !
			*/

	}
}
           

【9】JDK1.5中线程的新特性:

我们都知道,在线程中有一个很重要的机制,那就是等待唤醒机制:例如代码分析:

/*
线程的等待唤醒机制
代码的优化
【优化的重点】
1.资源中:包含基本的属性,存入,取出函数
2.存入类:直接调用资源的存入函数
3.取出类:直接调用资源的取出函数

这样看起来非常符合逻辑,层次清楚,便于代码的维护和修改等
*/
//定义了一个共同的资源
class Res
{
	private String name;
	private String sex;
	boolean flag=false;
	public synchronized void set(String name,String sex)
	{
		if (flag)
			try{wait();}catch(Exception e){}
		this.name=name;
		this.sex=sex;
		this.flag=true;
		this.notify();
	}

	public synchronized void out()
	{
		if (!flag)
			try{wait();}catch(Exception e){}
		System.out.println("name="+name+" ....... sex="+sex);
		this.flag=false;
		this.notify();
	}
}
//定义存入资源类
class Input implements Runnable
{
	Res r;
	//存入类在初始化时,就有资源
	Input(Res r)
	{
		this.r=r;
	}
	//覆写run方法
	public void run()
	{	
		int id=0;
		
		while (true)
		{
			//加锁为了保证线程的同步,只能允许一个线程在其中操作直到完成位置
			if (id==0)
				r.set("crystal","man");
			else
				r.set("杨女士","女");
			id=(id+1)%2;
		}
	}
}
//定义取出资源类
class Output implements Runnable
{
	Res r;
	Output(Res r)
	{
		this.r=r;
	}
	public void run()
	{
		while (true)
		{
			r.out();
		}
	}
}
/*
	上述的存入资源中和取出资源中:共享的代码就是存入操作和取出操作,一定要保证通过进行,
	否则,当一个线程进入时,存完一个之后,再进入存第二时(存完名称时,挂了),这是取出资源的线程
	在运行,这时候就会把出现:r.name="杨女士"和r.sex="man";的情况
*/
class InputOutputDemo3
{
	public static void main(String[] args) 
	{
		/*
		Res r=new Res(); //资源实例化

		Input in=new Input(r); //把资源和Runnable子类相关联
		Output out=new Output(r);

		Thread t1=new Thread(in);//定义了两个不同线程,一个存入,一个取出
		Thread t2=new Thread(out);
		
		t1.start(); //开启并执行线程
		t2.start();
		*/

		Res r=new Res();
		new Thread(new Input(r)).start();
		new Thread(new Output(r)).start();

		/*result:
			…………
			…………
			name=crystal ....... sex=man
			name=杨女士 ....... sex=女
			name=crystal ....... sex=man
			name=杨女士 ....... sex=女
			name=crystal ....... sex=man
			name=杨女士 ....... sex=女
			name=crystal ....... sex=man
			name=杨女士 ....... sex=女
			name=crystal ....... sex=man
			name=杨女士 ....... sex=女
			name=crystal ....... sex=man
			name=杨女士 ....... sex=女
			name=crystal ....... sex=man
			name=杨女士 ....... sex=女
			name=crystal ....... sex=man
			name=杨女士 ....... sex=女
			name=crystal ....... sex=man
			name=杨女士 ....... sex=女
			name=crystal ....... sex=man
			…………
			…………
		*/
	}
}
           

那么在这里面用到了同步锁,在JDK1.5中,新特性出现了:

将同步synchronized替换成为了Lock操作

将Object中的wait,notify,notifyAll,替换成了Condition对象

该对象可以Lock锁,进行获取

在该例子中,实现了本方只唤醒对方的操作

用这种新特性:程序会进行得非常完美,完全符合逻辑!不浪费资源

import java.util.concurrent.locks.*;

class Resource
{
	private String name;
	private int id=1;
	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)
	{
		lock.lock(); //进来就获得锁进入
		try
		{
			while (this.flag)
				//condition.await(); //首次进来因为为false,所以直接执行下面的语句
				condition_pro.await();//让自身的线程等待
			this.name=name+"....."+id++;
			System.out.println(Thread.currentThread().getName()+" Producer:----"+this.name);
			this.flag=true;
			//condition.signalAll();//唤醒线程池中所有的线程,为的是让不要出现全部等待的状况
			condition_con.signal();//不用唤醒所有了,只需要唤醒线程池中的对方就行,所以不用All了
		}
		catch (Exception e)
		{
		}
		finally
		{
			lock.unlock(); //最终释放锁
		}	
	}
	
	//定义消费者的同步消费函数
	public void out()
	{
		lock.lock();
		try
		{   
			while(!(this.flag))
				//condition.await();
			//同理:
			condition_con.await(); //本方等待
			System.out.println(Thread.currentThread().getName()+" Consumer:"+name);
			this.flag=false;
			//condition.signalAll();
			condition_pro.signal(); //唤醒对方
		}
		catch (Exception e)
		{
		}
		finally
		{
			lock.unlock();
		}
		
	}
}

//定义生产者具备调用生产功能
class Producer implements Runnable
{	
	private Resource res;
	Producer(Resource res)
	{
		this.res=res;
	}
	public void run()
	{
		while (true)
		{
			res.set("汽车");
		}
		
	}
}
//定义消费者具备消费功能
class Consumer implements Runnable
{	
	private Resource res;
	Consumer(Resource res)
	{
		this.res=res;
	}
	public void run()
	{
		while (true)
		{
			res.out();
		}
	}
}

class ProducerConsumerDemo6
{
	public static void main(String[] args) 
	{
		//把商品资源实例化
		Resource res=new Resource();

		//定义了两个生产者路线(线程)和两个消费者路线(线程)
		//并且同时启动这些线程和执行
		new Thread(new Producer(res)).start();
		new Thread(new Producer(res)).start();
		new Thread(new Consumer(res)).start();
		new Thread(new Consumer(res)).start();

		/*
		结果:
		………………
		Thread-1 Producer:----汽车.....3522
		Thread-2 Consumer:汽车.....3522
		Thread-0 Producer:----汽车.....3523
		Thread-3 Consumer:汽车.....3523
		Thread-1 Producer:----汽车.....3524
		Thread-2 Consumer:汽车.....3524
		Thread-0 Producer:----汽车.....3525
		Thread-3 Consumer:汽车.....3525
		Thread-1 Producer:----汽车.....3526
		Thread-2 Consumer:汽车.....3526
		Thread-0 Producer:----汽车.....3527
		Thread-3 Consumer:汽车.....3527
		Thread-1 Producer:----汽车.....3528
		Thread-2 Consumer:汽车.....3528
		Thread-0 Producer:----汽车.....3529
		Thread-3 Consumer:汽车.....3529
		Thread-1 Producer:----汽车.....3530
		………………
		*/
	}
}
           

【10】多线程之间的通信模式分析:

多线程之间的通信就是:多个线程对象去访问共享的数据空间,但是每个线程具体的功能是不一样的,例如:有一个资源,一个线程是向其中存,另一个线程是往里面取出……

/*
线程之间的通信:
*/
//定义了一个共同的资源
class Res
{
	String name;
	String sex;
}
//定义存入资源类
class Input implements Runnable
{
	Res r;
	//存入类在初始化时,就有资源
	Input(Res r)
	{
		this.r=r;
	}
	//覆写run方法
	public void run()
	{	
		int id=0;
		while (true)
		{
			//加锁为了保证线程的同步,只能允许一个线程在其中操作直到完成位置
			synchronized(r)
			{
				if (id==0)
				{
					r.name="crystal";
					r.sex="man";
				}else
				{
					r.name="杨女士";
					r.sex="女";
				}
				id=(id+1)%2;
			}	
		}
	}
}
//定义取出资源类
class Output implements Runnable
{
	Res r;
	Output(Res r)
	{
		this.r=r;
	}
	public void run()
	{
		while (true)
		{
			//加锁为了保证线程的同步
			//取出资源也是一样的,必须保证同步,只能有一个线程操作
			synchronized(r)
			{
				System.out.println(r.name+"....."+r.sex);
			}
		}
	}
}
/*
	上述的存入资源中和取出资源中:共享的代码就是存入操作和取出操作,一定要保证通过进行,
	否则,当一个线程进入时,存完一个之后,再进入存第二时(存完名称时,挂了),这是取出资源的线程
	在运行,这时候就会把出现:r.name="杨女士"和r.sex="man";的情况
*/
class InputOutputDemo1
{
	public static void main(String[] args) 
	{
		Res r=new Res(); //资源实例化

		Input in=new Input(r); //把资源和Runnable子类相关联
		Output out=new Output(r);

		Thread t1=new Thread(in);//定义了两个不同线程,一个存入,一个取出
		Thread t2=new Thread(out);
		
		t1.start(); //开启并执行线程
		t2.start();
		/*result:
		………………
		………………
		杨女士.....女
		杨女士.....女
		杨女士.....女
		crystal.....man
		crystal.....man
		………………
		………………
		*/
	}
}
           

【11】停止线程的方法总结:

停止线程的方法:

由于stop方法已经过时了

那么怎样才能停止线程呢?

思想只有一种,那就是让run方法结束

一般开启多线程的运行,运行代码通常是循环结构

所以只要控制好循环,就能让run方法结束,也就是让线程结束

让线程停止的3中方法:

1.设置标记:主线程结束之前改变标记让run方法结束

2.使用interrupt():当线程处于wait(必须在同步中进行)或者sleep中断时(在冻结的区域中),用此方法清除冻结,使线程回到运行中,这时会抛出异常,就在异常中设置标记,结束run方法,让线程不再进入去继续等待

3.使用守护线程的方法,setDaemon(true):在【线程启动之前】就设定为守护线程,主要是为了当主线程结束时,后台会自动结束被守护的线程

【其实我们看到的都是前台的线程,后台也有线程在运行,可以理解为后台依赖前台的关系,当前台结束了,后台线程也就over了,这就是守护线程的特点】

class StopThread implements Runnable
{	private boolean flag=true;
	
	//在同步中,当线程进入冻结状况时,就不会读到标记了,线程就不会结束
	public synchronized void run()
	{
		while (flag)
		{
			try
			{
				wait(); //两个线程依次进来都是在这里等待了……没法被唤醒
				//所以该线程没有被停止掉,并且主线程中改变标记的方法,在这里,线程也无法读到了

				//在这个时候就要用到线程中的interrupt方法,主要是为了让线程从冻结状态回到运行状态上
				//但是在运行之后,依然会进入等待状态!

				//当回到运行状态的时候,就需要在抛出的异常中去处理标记,改变标记值就OK了,当看到标记为false的
				//时候两个线程就无法进入该方法区了,这样线程就结束了
			}
			catch (InterruptedException e)
			{
				System.out.println(Thread.currentThread().getName()+".....Exception");
				flag=false;
			}
			System.out.println(Thread.currentThread().getName()+" run .....");
		}
	}

	public void setFlag()
	{
		flag=false;
	}
}
class StopDemo7 
{
	public static void main(String[] args) 
	{
		StopThread st=new StopThread();
		Thread t1=new Thread(st);
		Thread t2=new Thread(st);

		//t1.setDaemon(true);//分别把t1,t2线程都设置为守护线程后再启动
		//t2.setDaemon(true);

		t1.start();
		t2.start();
		int i=0;
		while (true)
		{
			if (i++ == 50)
			{
				st.setFlag(); //主线程中改变了标记,让其run方法主体结束,线程必然也结束了

				//t1.interrupt(); 
				//t2.interrupt();
				//在主线程结束之前把处于等待的线程用interrupt()方法清除掉冻结的线程,让其回到运行状态
				
				break;
			}
			System.out.println(Thread.currentThread().getName()+"  ....."+i);
		}
		/*结果,这里是通过改变标记的方法,结束run方法体,让线程无法进入,直到主线程结束
		,其他两个线程也结束了
		…………
		main  .....41
		main  .....42
		Thread-0 run .....
		main  .....43
		main  .....44
		main  .....45
		main  .....46
		main  .....47
		main  .....48
		main  .....49
		main  .....50
		Thread-1 run .....
		Thread-0 run .....
		*/

		/*
		运用了中断线程,清除了冻结的方法之后:两个线程也结束了,这是在同步中的做法
		main  .....38
		main  .....39
		main  .....40
		main  .....41
		main  .....42
		main  .....43
		main  .....44
		main  .....45
		main  .....46
		main  .....47
		main  .....48
		main  .....49
		main  .....50
		Thread-0.....Exception
		Thread-0 run .....
		Thread-1.....Exception
		Thread-1 run .....
		*/

		/*
		设置为守护线程后运行的结果:不论是不是同步,主线程结束,后台线程就被Over了
		………………
		main  .....35
		main  .....36
		main  .....37
		main  .....38
		main  .....39
		main  .....40
		main  .....41
		main  .....42
		main  .....43
		main  .....44
		main  .....45
		main  .....46
		main  .....47
		main  .....48
		main  .....49
		main  .....50

		*/
	}
}
           

【12】join()和yield()方法的总结:

《1》首先join方法:

join():方法的作用

1.当线程A执行到了线程B的B.join()方法时,A就会释放执行权给B,自己处于等待的冻结状态;

2.当线程B都执行完之后,线程A才能从冻结状态回到运行状态去执行;

3.所以join可以用来临时加入线程执行内容!

了解:

线程A把执行权释放了,让线程B去执行,自己冻结,这时线程B如果被等待了,线程A也没法回到运行状态

那么,这时就要用到interrupt()方法,去中断清除A的冻结,从而回到运行状态,当然也可以中断线程A

只是会受到伤害(抛出异常),在异常中处理就行了实际开发中:一般都是使用匿名内部类来完成的

class TestJoin implements Runnable
{
	public void run()
	{
		for (int i=0;i<80 ;i++ )
		{
			System.out.println(Thread.currentThread().getName()+".....run....."+i);
		}
		
	}
}

class JoinDemo8 
{
	public static void main(String[] args) 
	{
		TestJoin tj=new TestJoin();
		Thread t1=new Thread(tj);
		Thread t2=new Thread(tj);

		t1.start();

		//try{t1.join();}catch(Exception e){} 
		//执行的结果是:线程0全部把run函数执行完之后,主线程mian和线程1才交替执行
		//所以:join的功能是能够临时获得主线程的执行权,此程序中,主线程main把执行权释放给了t1,
		//自己处于了冻结状态,当t1线程结束之后,主线程才回到运行状态和t2线程继续交替执行
		
		t2.start();
		try{t1.join();}catch(Exception e){} 
		//如果t1.join()处于这里的话:主线程仍然是把自己的执行权释放给了t1,自己处于冻结状态,
		//但是t2也是存活的线程,这时候cpu会自动发放执行权给t1或者t2去交替执行!主线程就悲催了,只有等到
		//t1执行完之后,才能拿到自己的执行权,从冻结状态回到运行状态

		for (int i=0; i<90; i++)
		{
			System.out.println(Thread.currentThread().getName()+".....mian....."+i);
		}
	}
}
           

《2》yield()方法:

暂停当前正在执行的线程对象,并执行其他线程。

当线程A进入时,暂停当前正在执行的内容,释放执行权,让其他的线程进来执行,当其他的进来执行

之后,又重新获得执行权,继续执行,这样一来,等同于被共享的线程执行内容是“交替执行的‘

class TestSetPriority implements Runnable
{
	int i=100;
	public void run()
	{
		for (int i=0; i<20 ; i++)
		{
			System.out.println(Thread.currentThread().toString()+"....run...."+i);
			Thread.yield();
			/*
			Thread[Thread-0,5,main]....run....0
			Thread[Thread-1,5,main]....run....0
			Thread[Thread-0,5,main]....run....1
			Thread[Thread-1,5,main]....run....1
			Thread[Thread-0,5,main]....run....2
			Thread[Thread-1,5,main]....run....2
			Thread[Thread-0,5,main]....run....3
			Thread[Thread-1,5,main]....run....3
			Thread[Thread-0,5,main]....run....4
			Thread[Thread-1,5,main]....run....4
			Thread[Thread-0,5,main]....run....5
			Thread[Thread-1,5,main]....run....5
			*/
		}
	}
}

class SetPriorityDemo9 
{
	public static void main(String[] args) 
	{
		TestSetPriority sd=new TestSetPriority();
		Thread t1=new Thread(sd);
		Thread t2=new Thread(sd);
		t1.start();
		t2.start();

		/*
		for (int i=0; i<20; i++)
		{
			System.out.println(Thread.currentThread().toString()+"....mians....");
			/*
			Thread[main,5,main]....mian....
			Thread[main,5,main]....mian....
			Thread[main,5,main]....mian....
			
		}
		*/
		/*结果://这里是toString()方法,该线程类覆写了Object的这个方法
		在其中封装了进了线程名称,线程优先级,线程组
		…………………………
		Thread[Thread-0,5,main]....run.... 
		Thread[Thread-1,5,main]....run....
		Thread[Thread-0,5,main]....run....
		Thread[Thread-1,5,main]....run....
		Thread[Thread-0,5,main]....run....
		Thread[Thread-1,5,main]....run....
		Thread[Thread-0,5,main]....run....
		Thread[Thread-1,5,main]....run....
		Thread[Thread-0,5,main]....run....
		Thread[Thread-1,5,main]....run....
		Thread[Thread-0,5,main]....run....
		Thread[Thread-1,5,main]....run....
		………………………………
		*/

	}
}
           

【13】开发过程中为了提高效率,怎样单独封装?

实际开发中:一般都是使用匿名内部类来完成的 用法:在独立运算中:相互不相干扰的时候,可以单独封装一下,提高了执行效率 下面就是三个线程同时执行,很高效!

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

		//相当于继承的方法
		new Thread(){
			public void run()
			{
				for (int i=0; i<5 ;i++ )
				{
					System.out.println(Thread.currentThread().getName()+"...."+i);
				}
			}
		}.start();


		for (int i=0;i<6;i++ )
		{
			System.out.println(Thread.currentThread().getName()+"...."+i);
		}

		//相当于实现接口的方法
		Runnable r=new Runnable()
		{
			public void run()
			{
				for (int i=0; i<10 ;i++ )
				{
					System.out.println(Thread.currentThread().getName()+"...."+i);
				}
			}
		};
		new Thread(r).start();

		/*
		main....0
		Thread-0....0
		main....1
		Thread-0....1
		main....2
		Thread-0....2
		main....3
		Thread-0....3
		main....4
		Thread-0....4
		main....5
		Thread-1....0
		Thread-1....1
		Thread-1....2
		Thread-1....3
		Thread-1....4
		Thread-1....5
		Thread-1....6
		Thread-1....7
		Thread-1....8
		Thread-1....9
		*/
	}
}