JAVA自学教程之(多线程的创建方式二 :实现Runnable接口(常用))



JAVA自学教程之(多线程的创建方式二 :实现Runnable接口(常用))。当一个类有父亲,但是其中的功能还希望实现线程,那么就不能采用继承Thread的方式创建线程 那么就可以通过接口的方式完成
准备扩展Demo类的功能,让其中的内容可以作为线程的任务执行 实现Runnable接口,Runnable接口中只有一个方法run

一、创建线程的第二种方法

 

Runnable的出现仅仅是将线程的任务进行了对象的封装

 

/* * 创建线程的第二种方法 * 1.定义类实现Runnable接口 * 2.覆盖接口中的fun方法,将线程的任务代码封装到run方法中 * 3.通过Thread类创建线程对象,并将Runnable接口的子类对象作为构造函数的参数进行传递 * 为什么?因为线程的任务都封装在Runnable接口子类对象的run方法中,所以要在线程对象创建 * 时就要明确要运行的任务 * 4.调用线程的start方法开启线程

*/

 

  1. class Demo implements Runnable
  2. {
  3.     public void run()
  4.     {
  5.         show();
  6.     }
  7.     public void show()
  8.     {
  9.         for(int i = 0;i<10;i++)
  10.         {
  11.             System.out.println(Thread.currentThread().getName()+”i = “+i);
  12.         }
  13.     }
  14. }
  15. public class Main
  16. {
  17.     public static void main(String[] args)
  18.     {
  19.         //定义线程的方式
  20.         Demo jo = new Demo();
  21.         //Thread中有一个Thread(Runnable t)的方法
  22.         Thread aThread = new Thread(jo);
  23.         //如果不传递,start只会执行自己的方法
  24.         Thread bThread = new Thread(jo);
  25.         aThread.start();
  26.         bThread.start();
  27.     }
  28. }
class Demo implements Runnable
{
	public void run()
	{
		show();
	}
	public void show()
	{
		for(int i = 0;i<10;i++)
		{
			System.out.println(Thread.currentThread().getName()+"i = "+i);
		}
	}
}
public class Main
{
	public static void main(String[] args)
	{
		//定义线程的方式
		Demo jo = new Demo();
		//Thread中有一个Thread(Runnable t)的方法
		Thread aThread = new Thread(jo);
		//如果不传递,start只会执行自己的方法
		Thread bThread = new Thread(jo);
		aThread.start();
		bThread.start();
	}
}

二、实现Runnable接口和继承Thread类的区别

 

 

实现Runnable接口的好处:

1.将线程的任务同线程的子类中分离出来,进行了单独的封装,也就是将任务封装成了对象

2.避免了单继承的局限性

所以,创建线程的第二种方式较为常用。

 

三、代码实例:

 

第一种线程的创建方式

 

  1. /*
  2. * 需求:4个窗口进行买票,票数100张,编号1-100
  3. *
  4. */
  5. class Ticket extends Thread
  6. {
  7.     private int num = 100;
  8.     //private static int num = 100;
  9.     public void run()
  10.     {
  11.         while(true)
  12.         {
  13.             if(num>0)
  14.             {
  15.                 System.out.println(Thread.currentThread().getName()+”..sale..”+num–);
  16.             }
  17.         }
  18.     }
  19. }
  20. public class Main
  21. {
  22.     public static void main(String[] args)
  23.     {
  24.         Ticket j1 = new Ticket();
  25.         Ticket j2 = new Ticket();
  26.         Ticket j3 = new Ticket();
  27.         Ticket j4 = new Ticket();
  28.         j1.start(); j2.start();
  29.         j3.start(); j4.start();
  30.     }
  31. }
/*
 * 需求:4个窗口进行买票,票数100张,编号1-100
 * 
 */
class Ticket extends Thread
{
	private int num = 100;
	//private static int num = 100;
	public void run()
	{
		while(true)
		{
			if(num>0)
			{
				System.out.println(Thread.currentThread().getName()+"..sale.."+num--);	
			}	
		}
	}
}
public class Main 
{
	public static void main(String[] args)
	{
		Ticket j1 = new Ticket();
		Ticket j2 = new Ticket();
		Ticket j3 = new Ticket();
		Ticket j4 = new Ticket();
		j1.start(); j2.start(); 
		j3.start(); j4.start();
	}
}

PS:用第一种方式创建线程,可能出现有多个窗口卖同一号票的情况(1号窗口卖10号票,3号窗口也卖10号票)当然可以将票定义为静态的,但是仅限于是一种票,如果多种票就不适用了

 

 

 

第二种创建线程的方式

 

  1. /*
  2. * 需求:4个窗口进行买票,票数100张,编号1-100
  3. *
  4. */
  5. class Ticket implements Runnable
  6. {
  7.     private int num = 100;
  8.     //private static int num = 100;
  9.     public void run()
  10.     {
  11.         while(true)
  12.         {
  13.             if(num>0)
  14.             {
  15.                 System.out.println(Thread.currentThread().getName()+”..sale..”+num–);
  16.             }
  17.         }
  18.     }
  19. }
  20. public class Main
  21. {
  22.     public static void main(String[] args)
  23.     {
  24.         /*
  25.         Ticket t = new Ticket();//将 卖票这一行为封装成对象
  26.         Thread j1 = new Thread(t);
  27.         Thread j2 = new Thread(t);
  28.         Thread j3 = new Thread(t);
  29.         Thread j4 = new Thread(t);
  30.         j1.start(); j2.start();
  31.         j3.start(); j4.start();
  32.         */
  33.         //两种票,站票、坐票
  34.         Ticket zhanpiao = new Ticket();
  35.         Ticket zuopiao = new Ticket();
  36.         //1 2窗口卖站票,3 4 窗口卖坐票
  37.         Thread j1 = new Thread(zhanpiao);
  38.         Thread j2 = new Thread(zhanpiao);
  39.         Thread j3 = new Thread(zuopiao);
  40.         Thread j4 = new Thread(zuopiao);
  41.         j1.start(); j2.start();
  42.         j3.start(); j4.start();
  43.     }
  44. }
/*
 * 需求:4个窗口进行买票,票数100张,编号1-100
 * 
 */
class Ticket implements Runnable
{
	private int num = 100;
	//private static int num = 100;
	public void run()
	{
		while(true)
		{
			if(num>0)
			{
				System.out.println(Thread.currentThread().getName()+"..sale.."+num--);	
			}	
		}
	}
}
public class Main 
{
	public static void main(String[] args)
	{
		/*
		Ticket t = new Ticket();//将 卖票这一行为封装成对象
		Thread j1 = new Thread(t);
		Thread j2 = new Thread(t);
		Thread j3 = new Thread(t);
		Thread j4 = new Thread(t);
		j1.start(); j2.start(); 
		j3.start(); j4.start();
		*/
		//两种票,站票、坐票
		Ticket zhanpiao = new Ticket();
		Ticket zuopiao = new Ticket();
		//1 2窗口卖站票,3 4 窗口卖坐票
		Thread j1 = new Thread(zhanpiao);
		Thread j2 = new Thread(zhanpiao);
		Thread j3 = new Thread(zuopiao);
		Thread j4 = new Thread(zuopiao);
		j1.start(); j2.start(); 
		j3.start(); j4.start();
	}
}


 

为什么继承Thread类 和 实现RUnnable接口会出现不同的结果?

上述的卖票行为:

继承Thread类:4个窗口100票的行为,可以理解为:每个窗口都有卖100张票的任务,自然会出现1号窗口卖10号票,3号窗口也卖10号票的行为

 

实现Runnable接口:可以理解为4个窗口同时卖100张票的任务,那么就不会出现同时卖同号票的问题:

如图:

 

四、线程的安全问题

 

 

  1. public void run()
  2.     {
  3.         while(true)
  4.         {
  5.             if(num>0)
  6.             {
  7.                 System.out.println(Thread.currentThread().getName()+”..sale..”+num–);
  8.             }
  9.         }
  10.     }
public void run()
	{
		while(true)
		{
			if(num>0)
			{
				System.out.println(Thread.currentThread().getName()+"..sale.."+num--);	
			}	
		}
	}

 

 

这段代码是存在安全隐患的,理想状态下不会出现卖0号票的可能,但是一旦出现,进程就会出事

 

  1. class Ticket implements Runnable
  2. {
  3.     private int num = 100;
  4.     //private static int num = 100;
  5.     public void run()//此处不能throws,因为Runnable没有声明异常
  6.     {
  7.         while(true)
  8.         {
  9.             if(num>0)
  10.             {
  11.                 //睡10毫秒,sleep可能存在异常
  12.                 //所以只能try,catch,不能抛
  13.                 try
  14.                 {
  15.                     Thread.sleep(10);
  16.                 }
  17.                 catch (InterruptedException e)
  18.                 {
  19.                     // TODO: handle exception
  20.                 }
  21.                 System.out.println(Thread.currentThread().getName()+”..sale..”+num–);
  22.             }
  23.         }
  24.     }
  25. }
  26. public class Main
  27. {
  28.     public static void main(String[] args)
  29.     {
  30.         Ticket t = new Ticket();
  31.         Thread j1 = new Thread(t);
  32.         Thread j2 = new Thread(t);
  33.         Thread j3 = new Thread(t);
  34.         Thread j4 = new Thread(t);
  35.         j1.start(); j2.start();
  36.         j3.start(); j4.start();
  37.     }
  38. }
class Ticket implements Runnable
{
	private int num = 100;
	//private static int num = 100;
	public void run()//此处不能throws,因为Runnable没有声明异常 
	{
		while(true)
		{
			if(num>0)
			{
				//睡10毫秒,sleep可能存在异常
				//所以只能try,catch,不能抛
				try 
				{
					Thread.sleep(10);
				} 
				catch (InterruptedException e) 
				{
					// TODO: handle exception
				}

				System.out.println(Thread.currentThread().getName()+"..sale.."+num--);	
			}	
		}
	}
}
public class Main 
{
	public static void main(String[] args)
	{
		Ticket t = new Ticket();
		Thread j1 = new Thread(t);
		Thread j2 = new Thread(t);
		Thread j3 = new Thread(t);
		Thread j4 = new Thread(t);
		j1.start(); j2.start(); 
		j3.start(); j4.start();

	}
}

 

这就出现了安全隐患,所以在写多线程时,必须考虑安全问题

五、线程安全问题产生的原因:

 

1.多个线程在操作共享的数据(4个窗口操作共享的num)

2.操作共享数据的线程代码有多条

 

当一个线程在执行操作共享数据的多条代码过程中,其他线程参与运算,就会导致线程安全问题的产生

(举个简单的例子就是,1号窗口在卖1号票的时,还没卖完,2号窗口就把1号票卖完了,这就会出现卖0号票的情况,如果不try。。直接卖一般不会出事)