黑马程序员__多线程

黑马程序员——多线程

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

1.进程和线程的概念

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

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

  总之,一个进程中至少有一个线程。

2.自定义线程的方式

  (1)继承Thread类,覆盖run方法。

      步骤:<1>定义类继承Thread,目的是将自定义的代码存储在run方法中让线程运行。

           <2>覆写Thread类中的run方法,该方法有两个作用:启动线程和调用run方法。

      代码示例:

 

     

//创建线程的第一种方式
//需求:创建两个线程,和主线程交替进行
class Demo extends Thread
{
	public Demo(String name)
	{
		super(name);
	}
	//覆盖run方法
	public void run()
	{
		for(int i=0;i<50;i++)
		{
			System.out.println(this.getName()+"在运行......."+i);
		}
	}
}
class ThreadDemo 
{
	public static void main(String[] args) 
	{
		//创建线程对象
		Demo d1=new Demo("线程一");
		Demo d2=new Demo("线程二");
		d1.start();
		d2.start();
		for(int i=0;i<50;i++)
		{
			System.out.println("主线程在运行........."+i);
		}
	}
}

   (2)实现Runnable接口

       步骤:<1>定义类实现Runnable接口;

            <2>覆盖Runnable接口中的run方法;

            <3>通过Thread类建立线程对象;

            <4>将Runnable接口的子类对象作为实际参数传递给Thread类的构造函数;

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

      代码示例:

      

/*
需求:简单的买票程序,多个窗口同时买票。
*/
class SaleTicket implements Runnable
{
	private int ticket=100;
	//覆盖run方法
	public void run()
	{
		while(true)
		{
			if(ticket>0){
				try{
				Thread.sleep(10);
				}
				catch(Exception e)
				{
					System.out.println(Thread.currentThread().getName()+"-----Exception------");
				}
				System.out.println(Thread.currentThread().getName()+"卖了第有"+ticket--+"张票");
			}
		}

	}
}
class SaleTicketDemo2
{
	public static void main(String[] args) 
	{
		//创建Runnable子类的实例对象
		SaleTicket st=new SaleTicket();
		//创造线程对象,将Runnable子类的实例对象作为参数进行传递
		Thread t1=new Thread(st);
		Thread t2=new Thread(st);
		Thread t3=new Thread(st);
		//启动线程
		t1.start();
		t2.start();
		t3.start();



	}
}

以上两种方式,实现接口的方式避免了单继承的局限性,所以在定义线程时,建议使用实现接口的方式。

3.线程的运行状态:

  如下图所示:

 

4.多线程安全问题

  出现此问题的原因:当多条语句在操作同一个线程中的共享数据时,一个线程多条语句只执行了一部分,还没有执行完时,另一个线程参与进来执行。从而导致了共享数据的错误。

  解决方法有两种:同步代码块或者同步方法。

  (1)同步代码块:synchronized(对象){需要被同步的代码;}

      代码示例:

    

/*
需求:简单的买票程序,多个窗口同时买票。
*/
class SaleTicket implements Runnable
{
	private int ticket=100;
	Object obj=new Object();
	public void run()
	{
		while(true)
		{
			//同步代码块
			synchronized(obj)
			{
				if(ticket>0){
					try{
					Thread.sleep(10);
					}
					catch(Exception e)
					{
					}
					System.out.println(Thread.currentThread().getName()+"卖了第有"+ticket--+"张票");
				}
			}
		}

	}
}
class SaleTicketDemo3
{
	public static void main(String[] args) 
	{
		SaleTicket st=new SaleTicket();
		Thread t1=new Thread(st);
		Thread t2=new Thread(st);
		Thread t3=new Thread(st);
		t1.start();
		t2.start();
		t3.start();



	}
}


 

  (2)同步方法:只需要将方法进行同步就可以。

       代码示例:

       

/*
同步代码块与同步函数互相切换。
*/
class SaleTicket implements Runnable
{
	private int ticket=100;
	//定义标记用于切换
	boolean flag=true;
	public void run()
	{
		if(flag)
		{
			while(true)
			{
				//同步代码块
				synchronized(this)
				{
					if(ticket>0){
						try{
						Thread.sleep(10);
						}
						catch(Exception e)
						{
						}
						System.out.println(Thread.currentThread().getName()+"卖了第有"+ticket--+"张票");
					}
				}
			}
		}
		else
			while(true)
			method();

	}
	//同步方法
	public synchronized void method()
	{
		if(ticket>0){
					try{
					Thread.sleep(10);
					}
					catch(Exception e)
					{
					}
					System.out.println(Thread.currentThread().getName()+"卖了第有"+ticket--+"张票");
				}
	}
}
class SaleTicketDemo4
{
	public static void main(String[] args) 
	{
		SaleTicket st=new SaleTicket();
		Thread t1=new Thread(st);
		Thread t2=new Thread(st);
		t1.start();
		try{
		Thread.currentThread().sleep(10);
		}
		catch(Exception e)
		{
			System.out.println(Thread.currentThread().getName()+"-----Exception------");
		}
		st.flag=false;
		t2.start();



	}


 

 注意:同步的前提条件:

      <1>必须要有两个或者两个以上的线程;

      <2>必须是多个线程使用同一个锁;

      <3>必须保证同步中只能有一个线程在运行;

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

 同步的弊端:多个线程需要判断对象锁,较为浪费资源。

 静态同步方法使用的对象锁是该方法所在类的字节码文件对象,即类名.class.

 代码实例:

 

      

    


class SaleTicket implements Runnable
{
 private static int ticket=100;
 //定义标记用于切换
 boolean flag=true;
 public void run()
 {
  if(flag)
  {
   while(true)
   {
    //同步代码块
    synchronized(SaleTicket.class)
    {
     if(ticket>0){
      try{
      Thread.sleep(10);
      }
      catch(Exception e)
      {
      }
      System.out.println(Thread.currentThread().getName()+"卖了第有"+ticket--+"张票");
     }
    }
   }
  }
  else
   while(true)
   method();

 }
 //静态同步方法
 public static synchronized void method()
 {
  if(ticket>0){
     try{
     Thread.sleep(10);
     }
     catch(Exception e)
     {
     }
     System.out.println(Thread.currentThread().getName()+"卖了第有"+ticket--+"张票");
    }
 }
}
class SaleTicketDemo5
{
 public static void main(String[] args) 
 {
  SaleTicket st=new SaleTicket();
  Thread t1=new Thread(st);
  Thread t2=new Thread(st);
  t1.start();
  try{
  Thread.currentThread().sleep(10);
  }
  catch(Exception e)
  {
  }
  st.flag=false;
  t2.start();

 

 }
}


 

5.线程间的通信

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

  代码示例:

 

//线程之间的通信,通过等待唤醒机制来实现
class Information
{
 private String name;
 private String sex;
 boolean flag=false;
 //同步方法
 public synchronized void set(String name,String sex)
 {
  //判断标记,如果为真表示已经赋值,应该等待被打印
  if(this.flag)
   {
    try{
     this.wait();
     }
    catch(Exception e){}
   }
  //赋值
  this.name=name;
  this.sex=sex;
  //改变标记,表示赋值成功
  this.flag=true;
  //唤醒等待的线程
  this.notify();
 }
 public synchronized void get()
 {
  //判断标记,如果为假,表示未被赋值,应该等待被赋值
  if(!this.flag)
   {
    try
    {
     this.wait();
    }
    catch(Exception e)
    {
	System.out.println(Thread.currentThread().getName()+".......Exception....");
    }
   }
  System.out.println(name+"----"+sex);
  //改变标记,表示打印完毕
  this.flag=false;
  //唤醒等待的线程
  this.notify();
 }
}
//赋值线程类
class Input implements Runnable
{
 private Information infor;
 public Input(Information infor)
 {
  this.infor=infor;
 }
 public void run()
 {
  boolean flag=true;
  while(true)
  {
    if(flag)
    {
	//调用方法,完成赋值操作
     infor.set("Apple","girl");
     flag=false;
    }
    else
    {
     infor.set("小明","男孩");
     flag=true;
    }
    
   
  }
 }
} 
//输出线程类
class Output implements Runnable
{
 private Information infor;
 public Output(Information infor)
 {
  this.infor=infor;
 }
 public void run()
 {
  while(true)
  {
  //调用方法完成输出操作
   infor.get();
  }
 }
}
class ThreadComDemo 
{
 public static void main(String[] args) 
 {
  Information infor=new Information();
  new Thread(new Input(infor)).start();
  new Thread(new Output(infor)).start();
 }
}

6.生产者与消费者模型

  使用同步方法的形式来实现:

  代码示例:

  

//多个生产者与消费者
class ProCusDemo 
{
	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(pro);
		Thread t3=new Thread(pro);

		Thread t11=new Thread(con);
		Thread t22=new Thread(con);
		Thread t33=new Thread(con);


		t1.start();
		t2.start();
		t3.start();
		t11.start();
		t22.start();
		t33.start();
	}
}

class Resource
{
	private String name;
	private int count=1;
	private boolean flag=false;
	
	public synchronized void set(String name)
	{
		//循环判断标记
		while(flag)
		{
			try
			{
				wait();
			}
			catch (Exception e)
			{
				System.out.println("线程被终止!!!!!!");
			}
		}
		this.name=name+"--"+count++;
		System.out.println(Thread.currentThread().getName()+"---生产者----"+this.name);
		flag=true;
		//唤醒所有
		this.notifyAll();
	}
	public synchronized void out()
	{
		//循环判断
		while(!flag)
		{
			try
			{
				wait();
			}
			catch (Exception e)
			{
				System.out.println("线程被终止!!!!!!");
			}
		}
		System.out.println(Thread.currentThread().getName()+"---消费者----"+this.name);
		flag=false;
		//唤醒所有
		this.notifyAll();
	}

}
//生产者
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();
		}
	}
}


JDK1.5中提供了多线程升级的解决方案:

 代码示例:

 

import java.util.concurrent.locks.*;
//多个生产者与消费者
class ProCusDemo2 
{
	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(pro);
		Thread t3=new Thread(pro);

		Thread t11=new Thread(con);
		Thread t22=new Thread(con);
		Thread t33=new Thread(con);


		t1.start();
		t2.start();
		t3.start();
		t11.start();
		t22.start();
		t33.start();
	}
}

class Resource
{
	private String name;
	private int count=1;
	private boolean flag=false;
	private Lock lock=new ReentrantLock();
	private Condition pro_condition=lock.newCondition();
	private Condition con_condition=lock.newCondition();

	public  void set(String name)
	{
		lock.lock();
		try
		{
			//循环判断标记
			while(flag)
				//生产者线程等待
				pro_condition.await();
			this.name=name+"--"+count++;
			System.out.println(Thread.currentThread().getName()+"---生产者----"+this.name);
			flag=true;
			//唤醒消费者线程
			con_condition.signalAll();
		}
		catch(InterruptedException e)
		{
			System.out.println("线程被终止!!!!!!");
		}
		finally
		{
			//释放锁
			lock.unlock();
		}
		
		
	
	}
	public  void out()
	{
		lock.lock();
		try
		{
			//循环判断
			while(!flag)
				//消费者线程等待
				con_condition.await();
			System.out.println(Thread.currentThread().getName()+"---消费者----"+this.name);
			flag=false;
			//唤醒生产者线程
			pro_condition.signalAll();
		}
		catch (InterruptedException e)
		{
			System.out.println("线程被终止!!!!!!");
		}
		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();
		}
	}
}

升级之后的解决方案,显示的对象所机制、显示的等待唤醒机制,一个锁可以对应多个Condition对象,可以实现在唤醒对方锁的同时不用唤醒己方锁,较之前的解决方案更好一些。

7.停止线程。

  解决思路:结束run方法。当开启多线程运行,运行代码通常是循环结构,只要控制住循环,就可以让run方法结束,也就是线程结束。

  代码示例:

 

class Test 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) 
	{
		Test test=new Test();

		Thread t1=new Thread(test);
		Thread t2=new Thread(test);

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

		int number=0;
		while(true)
		{
			if(number==50)
			{
				test.changeflag();
				break;
			}
			number++;
			System.out.println(Thread.currentThread().getName()+"...run");
		}
	}
}


特殊情况:当线程处于冻结状态,就不会读取到标记,此时可以通过调用interrupt()方法将处于冻结状态的线程强制恢复到运行状态。

 代码示例:

 

class Test implements Runnable
{
	private boolean flag=true;
	public synchronized void run()
	{
		while(flag)
		{
			try
			{
				wait();
			}
			catch (InterruptedException e)
			{
				System.out.println(Thread.currentThread().getName()+"...Excption");
				flag=false;
			}
			System.out.println(Thread.currentThread().getName()+"...run");

		}
	}
	
}
class StopThreadDemo2 
{
	public static void main(String[] args) 
	{
		Test test=new Test();

		Thread t1=new Thread(test);
		Thread t2=new Thread(test);

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

		int number=0;
		while(true)
		{
			if(number==50)
			{
				//清除线程的冻结状态
				t1.interrupt();
				t2.interrupt();
				break;
			}
			number++;
			System.out.println(Thread.currentThread().getName()+"...run");
		}
	}
}


8.线程池

  使用线程池来执行线程任务的步骤如下:

  (1)调用Executors类的静态方法创建一个ExecutorsService对象,该对象就代表一个线程池。

  (2)创建Runnable实现类的实例,作为线程执行任务。

  (3)调用ExecutorsService对象的submit()方法来提交Runnable示例。

  (4)当不想提交任何任务时,调用ExecutorsService对象的shutdown()方法来关闭线程池。

  代码示例:

 

import java.util.concurrent.*;
class ThreadPoolDemo 
{
	public static void main(String[] args) throws Exception
	{
		//创建一个具有固定线程数的线程池
		ExecutorService pool=Executors.newFixedThreadPool(5);
		//向线程池中提交两个线程
		 pool.submit(new Demo());
		 pool.submit(new Demo());
		//关闭线程池
		pool.shutdown();
	}
}
//线程任务类
class Demo implements Runnable
{
	public void run()
	{
		for(int i=0;i<100;i++)
		{
			System.out.println(Thread.currentThread().getName()+"----"+i+"-------");
		}
	}
}


 

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

 

 


 

 

 

  

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值