黑马程序员——Java基础---多线程

                                                                                  ------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------

                                                                                                          Java基础---多线程

一、进程

是一个正在执行中的程序,每一个进程执行都有一个执行顺序,该顺序是一个执行路径,或者叫一个控制单元。

线程:就是进程中的一个独立的控制单元。线程在控制着进程的执行。 一个进程中至少有一个线程。

 java虚拟机启动的时候会有一个进程java.exe。而且这个线程运行的代码存在于main方法中,该线程称为主程。

扩展:其实更细节说明JVM,JVM启动不止一个线程,还有负责垃圾回收机制的线程。

注意:还有一个状态(临时状态) 阻塞 具备执行资格,但没有执行权,可能cpu在执行别的线程。当冻结的线程被唤醒了他最初会到阻塞状态,再判断CPU是否空闲,空闲时到运行状态 。

                             

(线程运行状态) 

                 

二、多线程的创建方式

         Thread线程是程序中的执行线程,如何在自定义代码中,自定义一个线程呢?

通过对API的查找,java已经提供了对线程这类事物的描述,就是Thread类。

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

        步骤:(1)定义类继承Thread

                   (2)复写Thread类中的run方法。

       目的:将自定义代码存储在run方法中,让线程运行。

                    (3) 调用线程的start方法,该方法有两个作用:启动线程,调用run方法。

    (当然,如果你想直接调用run方法也是可以的,但是那就不是多线程了)

//代码示例:
class ThreadTest extends Thread
{ 
	public void run()
	{ 
		线程运行代码; 
	} 
} 
class Test
{ 
	public static void main(String[] args)
	{ 
		ThreadTest threadTest = new ThreadTest(); 
		thread.start(); 
	} 
} 

//简化格式: 
class Test
{ 
	public static void main(String[] args)
	{ 
		new Thread()
		{ 
			public void run()
			{ 
				线程运行代码; 
			} 
		}.start(); 
	} 
} 

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

步骤:(1)定义类实现Runnable接口

       (2)覆盖Runnable接口中的run方法。将线程要运行的代码存放在该run方法中。

       (3)通过Thread类建立线程对象。

       (4)将Runnable接口的子类对象作为实际参数传递给Thread类的构造函数。

     (5)调用Thread类的start方法开启线程并调用Runnable接口子类的run方法。 

//代码示例: 
class RunnableTest implements Runnable
{ 
	public void run()
	{ 
	线程运行代码; 
	} 
}
class Test
{ 
	public static void main(String[] args)
	{ 
		RunnableTest runnableTest = new RunnableTest();
		Thread thread = new Thread(runnableTest);
		thread.start();
		/*
		简化到一步到为,new Thread(new Runnable()).start();
		*/ 
	} 
} 

//简化格式: 
class Test
{ 
	public static void main(String[] args)
	{ 
		new Thread(new Runnable()
		{ 
			public void run()
			{ 
				线程运行代码; 
			} 
		}).start(); 
	} 
} 
为什么要将Runnable接口的子类对象传递给Thread的构造函数。 
因为,自定义的run方法所属的对象是Runnable接口的子接口对象。 所以要让线程去指定指定对象的run方 法。就必须明确该run方法所属对象。 
实现方式和继承方式有什么区别:
(1)、实现方式好处:避免了单继承的局限性 
(2)、在定义线程时:建议使用实现方式
(3)、两种方式区别:
      1.继承Thread:线程代码存放在 Thread 子类 run 方法中 
      2.实现Runnable:线程代码存放在接口的子类的 run 方法中 
     总结建议:建议使用第二种线程创建方法。因为第二种方式更加体现面向对象思想

   多线程练习题

/*继承Thread类*/ 
class ThreadTest extends Thread
{ 
	ThreadTest(){} 
	ThreadTest(String ThreadName)
	{ 
		//把传递进行的线程名字赋值给父类的线程名字 
		super(ThreadName); 
	} 
	/*复写Thread类中的run方法*/ 
	public void run()
	{ 
		/*把代码封装在run方法里面*/ 
		while(true)
		{ 
			/*打印下当前线程的名字*/ 
			System.out.println(Thread.currentThread().getName()+"...run"); 
		} 
	} 
} 
class Test
{ 
	public static void main(String[] args)
	{ 
		/*创建了一个线程对象*/ 
		ThreadTest  t =  new ThreadTest (); 
		t.start();/*开启一个线程,并调用线程的run方法*/ 
		//t.run();可以吗?可以的,只不过没有开启线程哦 
		/*注意:主线程也是一个线程哦。我们把代码定义在main方法中就等于又开启了一个线程哦*/ 
		t.setName("ThreadTest"); 
		while(true)
		{ 
			/*打印下当前线程的名字*/ 
			System.out.println(Thread.currentThread().getName()+"...run"); 
			//Thread-0...run 
		} 
	} 
} 

三、 多线程的特性

       发现运行结果每次都不一样,因为多个线程都获取CPU的执行权,CPU执行到谁,谁就运行,明确一点,在某一

个时刻,只能有一个程序在运行(多核除外)CPU在做着快速的切换,以达到看上去是同时运行的效果。我们可以形

象的把多线程的运行形容为在互相抢夺CPU的执行权,这就是多线程的一个特性:随机性,谁抢到谁知晓,至于执行

多长,CPU说了算。

      为什么要覆盖run方法?

      Thread类用于描述线程,该类就定义了一个功能,用于存储线程要运行的代码,该存储功能就是run方法,也就是

所Thread类中的run方法,用于存储线程要运行的代码。

<span style="font-size:18px;">class Demo extends Thread
{
	public void run()
	{
		for(int x=0;x<60;x++)
		System.out.println("demo run--"+x);
	}
}</span>
<span style="font-size:18px;">class  ThreadDemo
{
	public static void main(String[] args) 
	{
		//System.out.println("Hello World!");
		Demo d=new Demo();//创建好一个线程
		d.start();//开启线程并执行该线程的run方法
		//d.run();仅仅是对象调用方法,而线程创建了,并没有运行。
		for(int x=0;x<60;x++)
		System.out.println("Hello World!--"+x);
	}
}</span>

四、练习

创建两个线程,和主线程交替运行

<span style="font-size:18px;"><span style="font-size:18px;">class Test extends Thread
{
	private String name;
	Test(String name)
	{
		this.name=name;
	}
	public void run()
	{
		for(int x=0; x<60; x++)
		System.out.println(name+"run!--"+x);
	}
}</span></span>
<span style="font-size:18px;"><span style="font-size:18px;">class  ThreadDemo
{
	public static void main(String[] args) 
	{
		Test t1 = new Test("one");
		Test t2 = new Test("two");
		t1.start();
		t2.start();
		for(int x=0; x<60; x++)
		{
			System.out.println("main!--"+x);
		}
	}
}</span></span>


原来线程都有自己默认的名称:Thread-编号,该编号从0开始。


<span style="font-size:18px;">class Test extends Thread
{
	private String name;
	Test(String name)
	{
		this.name=name;
	}
	public void run()
	{
		System.out.println(this.getName());
	}
}</span>
<span style="font-size:18px;">class  ThreadDemo
{
	public static void main(String[] args) 
	{
		Test t1 = new Test("one");
		Test t2 = new Test("two");
		t1.start();
		t2.start();
	}
}</span>

设置线程名称

<span style="font-size:18px;">class Test extends Thread
{
	Test(String name)
	{
		super(name);
	}
	public void run()
	{
		System.out.println(this.getName());
	}
}</span>
<span style="font-size:18px;">class  ThreadDemo
{
	public static void main(String[] args) 
	{
		Test t = new Test();
		t.start();
	}
}</span>

返回正在执行的线程:static Thread currentThread();获取当前线程对象

getName();获取线程名称

设置线程名称:setName()或者构造函数。

<span style="font-size:18px;">class Test extends Thread
{
	Test(String name)
	{
		super(name);
	}
	public void run()
	{
		System.out.println(Thread.currentThread().getName());
	}
}</span>
<span style="font-size:18px;">class  ThreadDemo
{
	public static void main(String[] args) 
	{
		Test t = new Test();
		t.start();
	}
}</span>

五、简单的卖票程序

需求:
简单的卖票程序,多个窗口卖票。 
思路:
票是一个共享数据被多个窗口所操作。 
窗口是一个线程,多个窗口应该是一个多线程。 
步骤:
票,是共享数据,应该被static修饰起来。 
多窗口,是多线程,应该创建多个线程来操作票。 

class Ticket extends Thread
{
	/*把线程创建传递进来的名字丢给父类带参数的构造函数*/ 
	Ticket(StringThreadName)
	{ 
		super(ThreadName); 
	} 
	/*定义票数据,定义为共享数据,因为多个窗口卖的是同一个票资源*/ 
	private static int tick =100;  
	public void run()
	{ 
		while(true)
		{ 
			/*如果还有票就打印下几号客户在买票,然后卖出去一张票,减一张票*/ 
			if(tick>0)
			{ 
				System.out.println(tick+"号客户买票\n"+Thread.currentThread().getName()+"为"+tick+"号客户服务\t...卖第"+tick--+"票"); 
			} 
			/*如果没有票了,就向客户致敬,然后跳出return跳出循环,方法结束*/ 
			else
			{ 
				System.out.println(Thread.currentThread().getName()+":哥们票卖完了,打烊了"); 
				return; 
			} 
		} 
	} 
} 
class Test
{
	public static void main(String[] args)
	{ 
		new Ticket("1号窗口").start();//创建匿名线程对象,调用对象的start()方法。 
		new Ticket("2号窗口").start(); 
		new Ticket("3号窗口").start(); 
		new Ticket("4号窗口").start(); 
	} 
} 

需求: 
简单的卖票程序,多个窗口卖票。 
思路: 
票是一个共享数据被多个窗口所操作。 
窗口是一个线程,多个窗口应该是一个多线程。 
步骤: 
票,是共享数据,应该被static修饰起来。 
多窗口,是多线程,应该创建多个线程来操作票。 

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

  步骤:

(1),定义类实现Runnable 接口

(2),覆盖Runnable接口中的run方法。将线程要运行的代码存放在该run方法中

(3),通过Thread类建立线程对象

(4),将Runnable接口的子类对象作为实际参数传递给Thread类的构造函数

为什么要将Runnable接口的子类对象传递给Thread的构造函数

因为自定义的run方法所属的对象是Runnable接口的子类对象,所以要让线程去指定对象的run方法。就必 须明确run方法所属的对象。

  (5),调用Thread类的start方法开启线程并调用Runnable接口的run方法。

<span style="font-size:18px;">class Ticket implements Runnable
{ 
	/*private Object obj = new Object();*/ 
	/*定义票数据,因为创建的是一个对象资源,所以保证了数据的共享,不用static也可以*/ 
	private int tick =100;  
	public void run()
	{ 
		while(true)
		{ 
			/*如果还有票就打印下几号客户在买票,然后卖出去一张票,减一张票*/ 
			synchronized(this)
			{ 
				if(tick>0)
				{ 
					try
					{ 
					Thread.sleep(10); 
					/*由于考虑到如果客户有可能操作慢而导致延迟,所以加入线程延迟进行测试,我们是程序员必须要做到一些错误的排除,当然不能保证到万无一失,但也要做到尽量避免发生错误*/ 
					} 
					catch(Exception e)
					{ }	 
					System.out.println(tick+"号客户买票\n"+Thread.currentThread().getName()+"为"+tick+"号客户服务\t...卖第"+tick--+"票"); 
				} 
				/*如果没有票了,就向客户致敬,然后跳出return跳出循环,方法结束*/ 
				else
				{ 
					System.out.println(Thread.currentThread().getName()+":哥们票卖完了,打烊了"); 
					return; 
				} 
			} 
		} 
	} 
} 
class Test
{ 
	public static void main(String[] args)
	{ 
		/* 
		这时要设置线程的名字的话,就要用Thread类的方法了,因为Runnable是 
		父接口,而设置线程的方法是在Thread类中的,所以可以直接用Thread对象调用。 
		*/ 
		Ticket t = new Ticket(); 
		Thread t1  = new Thread (t); 
		Thread t2  = new Thread (t);
		Thread t3  = new Thread (t);
		Thread t4  = new Thread (t);
		t1.setName("1号窗口"); 
		t2.setName("2号窗口"); 
		t3.setName("3号窗口"); 
		t4.setName("4号窗口");
		t1.start(); 
		t2.start(); 
		t3.start(); 
		t4.start(); 
		/*创建线程对象,把资源当成参数进行传递(保证了资源的唯一),调用Thread类的start()方法。*/ 
	} 
} 
</span>


六、实现方式和继承方式的区别:

实现好处:避免了单继承的局限性,在定义线程时,建议使用实现方式。

继承Thread:线程代码存放在Thread子类run方法中,

实现Runnable:线程代码存放在接口的子类run方法中

<span style="font-size:18px;">class Ticket implements Runnable//extends Thread
{
	private int tick=100;
	public void run()
	{
		while(true)
		{
			if(tick>0)
			{
				try
				{
					Thread.sleep(10);
				}
				catch(Exception e)
				{
				
				}
				System.out.println(Thread.currentThread().getName()+"sale:"+tick--);
			}
		}
	
	}
}</span>

通过分析发现,打印出0,-1,-2,等错票。

多线程的运行出现安全问题 .

问题的原因:

    当多条语句在操作同一个 线程共享数据时,一个线程对多条语句只执行了一部分,还没执行完,另一个线 程参与执行,导致共享数据的错误。

解决办法:对多条操作共享数据的语句,只能让一个线程都执行完,在执行过程中,其他线程不可以参与执行

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

synchronized(对象)

{
需要同步的代码; 

}

对象如同锁,持有锁的线程可以在同步中执行没有持有锁的线程即使获取CPU的执行权, 也进不去, 因为没 有获取锁。

同步的前提:

(1),必须要有两个及以上的线程。

(2),必须是多个线程使用同一个锁。
必须保证同步中只能有一个线程在执行。

好处:解决了多线程的安全问题

弊端:多个线程都需要判断锁,较为消耗资源,

<span style="font-size:18px;">class Ticket implements Runnable//extends Thread
{
	private int tick=100;
	Object obj = new Object;
	public void run()
	{
		while(true)
		{
			synchronized(obj)
			{
				if(tick>0)
				{
					try{Thread.sleep(10);}catch(Exception e){}
					System.out.println(Thread.currentThread().getName()+"sale:"+tick--);
				}
			}		
		}
	}
}</span>


七、需求:

银行有一个金库,有两个储户分别存300元,每次存100,存3次。

目的:该程序是否有安全问题,若果有,怎么解决?
如何找问题:

(1),哪些代码是多线程运行代码

(2),明确共享数据

(3),明确多线程运行代码中哪些语句是操作共享数据的

<span style="font-size:18px;">class Bank
{
	private int sum;
	Object obj = new Object();
	public void add(int n)
	{
		synchronized(obj)
		{
			sum+=n;
			try
			{
				Thread.sleep(10);
			}
			catch (Exception e)
			{
			}
			System.out.println("sum="+sum);
		}
		
	}
}</span>
<span style="font-size:18px;">class Cus implements Runnable
{
	private Bank b = new Bank();
	public void run()
	{
		for(int x=0; x<3; x++)
		{
			b.add(100);
		}
	}
}</span>
<span style="font-size:18px;">class BankDemo
{
	public static void main(String [] ars)
	{
		Cus c = new Cus();
		Thread t1 = new Thread(c);
		Thread t1 = new Thread(c);
		t1.start();
		t2.start();

	}
}</span>

同步函数

<span style="font-size:18px;">class Bank
{
	private int sum;
	public synchronized void add(int n)
	}
		sum+=n;
		try
		{
			Thread.sleep(10);
		}
		catch (Exception e)
		{
		}
		System.out.println("sum="+sum);
	}</span>
<span style="font-size:18px;">}	</span>

同步函数:用的是哪一个锁呢?

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

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

<span style="font-size:18px;">class Ticket implements Runnable
{
	private int tick=100;
	Object obj = new Object;
	boolean flag=true;
	public void run()
	{
		if(flag)
		{
			while(true)
			{
				synchronized(obj)
				{
					if(tick>0)
					{
					try{Thread.sleep(10);}catch(Exception e){}
					System.out.println(Thread.currentThread().getName()+"---code"+tick--);
					}
				}			
			}
		else
			while(true)
			show();
	 }
	public synchronized void show()
	{
		if(tick>0)
		{
			try{Thread.sleep(10);}catch(Exception e){}
			System.out.println(Thread.currentThread().getName()+"---show---"+tick--);
		}
	}			
}</span>
<span style="font-size:18px;">class TicketDemo
{
	public static void main(String [] args)
	{
		Ticket t = new Ticket();
		Thread t1 = new Thread(t);//创建了一个线程
		Thread t2 = new Thread(t);//创建了一个线程
		t1.start();
		try{Thread.sleep(10);}catch(Exception e){}
		t.flag = false;
		t2.start();
	}
}</span>

如果同步函数被静态修饰后,使用的锁是什么呢?

通过验证,发现不再是this ,因为静态中不可以定义this。静态进内存时,内存中没有本类对象,但是一定有该类对应的字节码文件对象。类名.class ,该对象的类型是Class     .静态的同步方法使用的锁是该方法所在类的字节码文件对象,类名.class

<span style="font-size:18px;">class Ticket implements Runnable
{
	private int tick=100;
	//Object obj = new Object;
	boolean flag=true;
	public void run()
	{
		if(flag)
		{
			while(true)
			{
				synchronized(Ticket.class)
				{
					if(tick>0)
					{
					try{Thread.sleep(10);}catch(Exception e){}
					System.out.println(Thread.currentThread().getName()+"---code"+tick--);
					}
				}			
			}
		else
			while(true)
			show();
	 }
	public static synchronized void show()
	{
		if(tick>0)
		{
			try{Thread.sleep(10);}catch(Exception e){}
			System.out.println(Thread.currentThread().getName()+"---show---"+tick--);
		}
	}			
}</span>
<span style="font-size:18px;">class TicketDemo
{
	public static void main(String [] args)
	{
		Ticket t = new Ticket();
		Thread t1 = new Thread(t);//创建了一个线程
		Thread t2 = new Thread(t);//创建了一个线程
		t1.start();
		try{Thread.sleep(10);}catch(Exception e){}
		t.flag = false;
		t2.start();
	}
}</span>

八、单例设计模式
饿汉式。
<span style="font-size:18px;">class Single
{
	private static final Single s = new Single();
	private Single(){}
	public static Single getInstance()
	{
		return s;
	}
}</span>
懒汉式

<span style="font-size:18px;">class Single
{
	private static Single s = null;
	private Single(){}
	public static synchronized Single getInstance()
	{
		if(s==null)
			s = new Single();
		return s;
	}
}</span>
<span style="font-size:18px;">class Single   //经典
{
	private static Single s = null;
	private Single(){}
	public static  Single getInstance()
	{
		if(s==null)
		{
			synchronized(Single.class)
			 {
				 if(s==null)
					s = new Single();
			  return s;
			}
		}		
	 }
}</span>

九、死锁:同步中嵌套同步,而锁却不同

<span style="font-size:18px;">class Ticket implements Runnable
{
	private int tick=100;
	Object obj = new Object;
	boolean flag=true;
	public void run()
	{
		if(flag)
		{
			while(true)
			{
				synchronized(obj)
				{
					show();
				}			
			}
		else
			while(true)
			   show();
	 }
	public static synchronized void show()//this锁
	{
		synchronized(obj)
		if(tick>0)
		{
			try{Thread.sleep(10);}catch(Exception e){}
			System.out.println(Thread.currentThread().getName()+"---show---"+tick--);
		}
	}			
}</span>
<span style="font-size:18px;">class TicketDemo
{
	public static void main(String [] args)
	{
		Ticket t = new Ticket();
		Thread t1 = new Thread(t);//创建了一个线程
		Thread t2 = new Thread(t);//创建了一个线程
		t1.start();
		try{Thread.sleep(10);}catch(Exception e){}
		t.flag = false;
		t2.start();
	}
}</span>

十、线程间的通讯通信

1、线程间通信

--Input-->资源--Output--->

思考1:wait(),notify(),notify All(),用来操作线程为什么定义在Object类中?

(1),这些方法存在与同步中。

(2),使用这些方法时必须要标识所属的同步锁

(3),锁可以是任意对象,所以任意对象调用的方法一定定义Object类中。
思考2:wait(),sleep(),有什么区别?

(1),wait():释放资源,释放锁。

(2),sleep:释放资源,不释放锁。

2、解决安全问题
class Res
{
	String name;
	String sex;
}
<pre name="code" class="java">class Input implements Runnable
{
	private Res r;
	Input(Res r)
	{
		this.r=r;
	}
	public void run()
	{
		int x = 0;
		while(true)
		{
			synchronized(r)
			{
				if(x==0)
				{
					r.name = "mike";
					r.sex = "man";
				}
				else
				{
					r.name = "丽丽";
					r.sex = "女女女女女";
				}	
				x =(x+1)%2;
			}		
		}
	}
}
 
class Output implements Runnable
{
	private Res r;
	Output(Res r)
	{
		this.r=r;
	}
	public void run()
	{
		while(true)
		{
			synchronized(r)
			{
				System.out.println(r.name+"---"+r.sex);
			}

		}
	}
}
class  
{
	public static void main(String[] args) 
	{
		System.out.println("Hello World!");
	}
}
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){}
		System.out.println(name+"---"+sex);
		flage = 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("mike","man");
			}
			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 InputOutputDemo 
{
	public static void main(String[] args) 
	{
		Res r = new Res();
		new Thread (new Input(r)).start();
		new Thread (new Output(r)).start();
	/*	Input in = new Input(r);
		Output out = new Output(r);

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

		t1.start();
		t2.start();
	*/
	}
}
wait(); n otify();  notifyAll(); 都使用在同步中,因为要对持有监视器(锁)的线程操作。 所以要使用在同步中,因为

只有同步才具有锁。为什么这些操作线程的方法要定义Object类中呢?因为这些方法在操作同步中线程时,都必须要标识它们所操作线程持有的锁,只有同一个锁上的被等待的线程,才可以被同一个锁上的notify唤醒不可以对不同锁中的线程进行唤醒也就是说,等待和唤醒必须是同一个锁。而锁可以是任意对象,所以可以被任意对象调用的方法定义在Object类中notify();存放在线程池中,只有存放在,线程池中的线程才能被唤醒,通常唤醒第一个被等待的线程。

生产者消费者

对于多个生产者和消费者为什么要定义while判断标记

原因:让被唤醒的线程再判断标记。

为什么定义notifyAll,

   因为需要唤醒对方线程,因为只用notify,容易出现只唤醒本方线程的情况,导致程序中的所有线程都等待。

<span style="font-size:18px;">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(con);
		t1.start();
		t2.start();
	}
}</span>
<span style="font-size:18px;">class Resourse
{
	private String name;
	private int count =1;
	private boolean flag =false;
	public synchronized void set(String name)
	{
		while(flag)
			try{this.wait();}catch(Exception e){}
		this.name = name +"--"+ count++;
		System.out.println(Thread.currentThread().getName()+"...生产者.."+this.name);
		flag = true;
		this.notifyAll();
	}
	public synchronized void out()
	{
		while(!flag)
			try{this.wait();}catch(Exception e){}
		System.out.println(Thread.currentThread().getName()+"...消费者.."+this.name);
		flag = false;
		this.notifyAll();
	}
}</span>
<span style="font-size:18px;">class Producer implements Runnable
{
	private Resource res;
	Producer(Resource res)
	{
		this.res = res;
	}
	public void run()
	{
		while(true)
		{
			res.set("++商品++");
		}
	}
}</span>
<span style="font-size:18px;">class Consumer implements Runnable
{
	private Resource res;
	Consumer(Resource res)
	{
		this.res = res;
	}
	public void run()
	{
		while(true)
		{
			res.out();
		}
	}
}
</span>
十一、API 1.5
JDK1.5 中提供了多线程升级解决方案
将同步synchronized替换成现实Lock操作将Object中的wait,notify,notifyAll替换成Condition对象

该对象可以Lock锁进行获取

该示例中,实现了本方只唤醒对方操作。

import java.util.concurrent.locks.*;  
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(con);
		t1.start();
		t2.start();
	}
}
class Resourse  
{  
	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  
	{  
		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  
		{  
			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  
        {  
            lock.unlock();  
        }  
    }  
}  
class Producer implements Runnable  
{  
    private Resource res;  
    Producer(Resource res)  
    {  
        this.res = res;  
    }  
    public void run()  
    {  
        while(true)  
        {  
            try  
            {  
                res.set("++商品++");  
            }  
            catch (InterruptedException e)  
            {  
            }  
        }  
    }  
}  
class Consumer implements Runnable  
{  
    private Resource res;  
    Consumer(Resource res)  
    {  
        this.res = res;  
    }  
    public void run()  
    {  
        while(true)  
        {  
            try  
            {  
                res.out();  
            }  
            catch (InterruptedException e)  
            {  
            }  
        }  
    }  
}  
十二、停止线程

(1),定义循环结束标记

因为线程运行代码一般都是循环,只要控制了循环即可。

(2),使用interrupt(中断)方法

该方法是结束线程的冻结状态,使线程回到运行状态中来

注意:stop方法已经过时不再使用。

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

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

Thread类提供该方法interrupt();  

守护线程setDaemon();在启动线程前调用,当前台线程结束,后天线程自动结束。

class StopThread implements Runnable
{
	private boolean flag = true;
	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.start();
		t2.start();
		int num = 0;
		while(true)
		{
			if(num++==60)
			{
				st.changeFlag();
				break;
			}
			System.out.println(Thread.currentThread().getName()+"...."+num);
	    }
	}
}
(3)join()示例
join 特点:当A线程执行到B线程的.join()方法时,就会等待,等待B线程都执行完,A才会执行。
join可以用来临时加入线程执行。

<span style="font-size:18px;">class Demo implements Runnable
{
	public void run()
	{
		for(int x =0; x<70; x++)
		{
			System.out.println(Thread.currentThread().getName()+"...."+x);
		}
	}
}
class joinDemo
{
	public static void main(String [] args)throws Exception
	{
		Demo d = new Demo()
		Thread t1 = new Thread(d);
		Thread t2 = new Thread(d);
		t1.start();
		t1.join();//抢夺CPU执行权
		t2.start();
		for(int x=0; x<80; x++)
		{
			System.out.prinln("main..."+x);
		}
		System.out.println("over");
	}
}
class ThreadTest
{
	public static void main(String [] args)
	{
		new Thread()
		{
			public void run()
			{
				for(int x=0; x<100; x++)
				{
					System.out.println("...."+x);
				}
			}	
		}.start();
		for(int x=0; x<100; x++)
		{
			System.out.println("...."+x);
		}
		Runnable r = new Runnable()
		{
			public void run()
			{
				for(int x=0; x<100; x++)
				{
					System.out.println("...."+x);
				}
			}	
		};
		new Thread(r).start();
	}
}</span>







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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值