多线程

-----------android培训java培训、java学习型技术博客、期待与您交流!------------  

一、何谓多线程?

进程:是一个正在执行的程序。

线程:就是进程中的一个独立的控制单元。

多线程:在一个进程中有多个线程执行的方式,就叫做多线程。


二、多线程的好处

多线程的出现能让程序产生同时运行效果。可以提高程序执行效率。

注意:在一个时刻,单核的cpu只能运行一个程序。而我们看到的同时运行效果,只是cpu在多个进程间做着快速切换动作。cpu执行哪个程序,是毫无规律性的。这也是多线程的一个特性:随机性。


三、如何创建多线程

 创建线程共有两种方式:继承方式和实现方式。

两种方式的区别:

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

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

1、继承方式(继承Thread)

创建步骤:

        a,定义类继承Thread

        b,复写Thread中的run方法。

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

        c,创建定义类的实例对象。相当于创建一个线程。

        d,用该对象调用线程的start方法。该方法的作用是:启动线程,调用run方法。

注:如果对象直接调用run方法,等同于只有一个线程在执行,自定义的线程并没有启动。

示例:

class Thread_1 extends Thread {
	public void run() {
		for(int x=0; x<50; x++)
		System.out.print("线路1");
	}
}
class Thread_2 extends Thread {
	public void run() {
		for(int x=0; x<50; x++)
		System.out.println("线路2");
	}
}
public class ThreadDemo {

	public static void main(String[] args) {
	Thread_1 t1=new Thread_1();
	Thread_2 t2=new Thread_2();
	t1.start();
	t2.start();
	for(int x=0; x<50; x++)
		System.out.print("线路3");
	}

}
2、 实现方式(实现Runnable)

创建步骤:

        a,定义类实现Runnable的接口。

        b,覆盖Runnable接口中的run方法。目的也是为了将线程要运行的代码存放在该run方法中。

        c,通过Thread类创建线程对象。

        d,将Runnable接口的子类对象作为实参传递给Thread类的构造方法。

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

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

        e,调用Thread类中start方法启动线程。start方法会自动调用Runnable接口子类的run方法。

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

示例:

class Tickets implements Runnable {
	private int ticket=100;
	Object obj=new Object();
	public void run() {
		while(true) {
			synchronized(obj){
			if(ticket>0) {
				System.out.println(Thread.currentThread().getName()+":"+ticket);
				ticket--;
			}
			else break;
			}
		}
	}
}
public class TicketDemo {

	public static void main(String[] args) {
		Tickets t=new Tickets();
		Thread t1=new Thread(t);
		Thread t2=new Thread(t);
		Thread t3=new Thread(t);
		Thread t4=new Thread(t);
		t1.start();
		t2.start();
		t3.start();
		t4.start();
	}

}


四、线程的几种状态

被创建:等待启动,调用start启动。

运行状态:具有执行资格和执行权。

 临时状态(阻塞):有执行资格,但是没有执行权。

冻结状态:遇到sleeptime)方法和wait()方法时,失去执行资格和执行权,sleep方法时间到或者调用notify()方法时,获得执行资格,变为临时状态。

 消忙状态:stop()方法,或者run方法结束。


五、线程的同步

1、线程同步的原因

当多条语句在操作同一线程共享数据时,一个线程对多条语句只执行了一部分,还没用执行完,另一个线程参与进来执行。导致共享数据的错误,存在安全隐患。同步可以解决这个问题。

2、同步如何创建

有两种解决方式,一种是同步代码块,还有就是同步函数。(都是利用关键字synchronized来实现)

1)

同步代码块用法:

                  synchronized(对象)

                  {需要被同步的代码}

        同步可以解决安全问题的根本原因就在那个对象上。其中对象如同锁。持有锁的线程可以在同步中执行。没有持有锁的线程即使获取cpu的执行权,也进不去,因为没有获取锁。

示例:

/*	
给卖票程序示例加上同步代码块。
*/
class Ticket implements Runnable
{
	private int tick=100;
	Object obj = new Object();
	public void run()
	{
		while(true)
		{
			//给程序加同步,即锁
			synchronized(obj)
			{
				if(tick>0)
				{
					try
					{	
						//使用线程中的sleep方法,模拟线程出现的安全问题
						//因为sleep方法有异常声明,所以这里要对其进行处理
						Thread.sleep(10);
					}
					catch (Exception e)
					{
					}
					//显示线程名及余票数
					System.out.println(Thread.currentThread().getName()+"..tick="+tick--);
				}
			}	
		}
	}
}

2)同步函数 格式:

                在函数上加上synchronized修饰符即可。

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

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

下面将以上程序中同步代码块改为同步函数如下:

class Ticket implements Runnable
{
	private int tick=100;
	Object obj = new Object();
	public void run()
	{
		while(true)
		{
			show();
		}
	}
  //直接在函数上用synchronized修饰即可实现同步
public synchronized void show()
{
		if(tick>0)
	    {
		try
		{	
			//使用线程中的sleep方法,模拟线程出现的安全问题
			//因为sleep方法有异常声明,所以这里要对其进行处理
			Thread.sleep(10);
		}
		catch (Exception e)
		{
		}
		//显示线程名及余票数
		System.out.println(Thread.currentThread().getName()+"..tick="+tick--);
	}
}	
}
3、同步注意事项:

1)同步的前提

        a,必须要有两个或者两个以上的线程。

        b,必须是多个线程使用同一个锁。

2)同步的利弊

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

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

3)如何寻找多线程中的安全问题

        a,明确哪些代码是多线程运行代码。

        b,明确共享数据。

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


六、静态函数的同步

类名.class 该对象的类型是class,这就是静态函数所使用的锁。而静态的同步方法,使用的锁是该方法所在类的字节码文件对象。类名.class

经典示例

/*
加同步的单例设计模式————懒汉式
*/
class Single
{
	private static Single s = null;
	private Single(){}
	public static void getInstance()
	{
		if(s==null)
		{
			synchronized(Single.class)
			{
				if(s==null)
					s = new Single();
			}
		}
		return s;
	}
}
</pre><strong></strong><p></p><p style="font-size:14px; line-height:26px; font-family:Arial; margin-top:0px; margin-bottom:0px; padding-top:0px; padding-bottom:0px" align="left"><span style="font-family:'Times New Roman';"><strong></strong></span></p>七、死锁<p style="margin-top:0px; margin-bottom:0px; padding-top:0px; padding-bottom:0px" align="left"></p><div class="para" style="font-size:14px; font-family:arial,宋体,sans-serif; line-height:24px; color:rgb(51,51,51); margin:15px 0px 5px; text-indent:2em">产生死锁的原因可归结为如下两点:</div><div class="para" style="font-size:14px; font-family:arial,宋体,sans-serif; line-height:24px; color:rgb(51,51,51); margin:15px 0px 5px; text-indent:2em">(1)竞争资源。当系统中供多个进程共享的资源如打印机、公用队列等,其数目不足以满足进程的需要时,会引起诸进程的竞争而产生死锁。</div><div class="para" style="font-size:14px; font-family:arial,宋体,sans-serif; line-height:24px; color:rgb(51,51,51); margin:15px 0px 5px; text-indent:2em">(2)进程间推进顺序非法。进程在运行过程中,请求和释放资源的顺序不当,也同样会导致产生进程死锁。</div><div class="para" style="font-size:14px; font-family:arial,宋体,sans-serif; line-height:24px; margin:15px 0px 5px; text-indent:2em"><span style="white-space:nowrap"><span style="color:#333333;">(注意:我们应当防止死锁的出现)</span></span></div><div class="para" style="font-size:14px; font-family:arial,宋体,sans-serif; line-height:24px; margin:15px 0px 5px; text-indent:2em"><span style="white-space:nowrap"><span style="color:#333333;">示例:</span></span></div><div class="para" style="font-size:14px; font-family:arial,宋体,sans-serif; line-height:24px; margin:15px 0px 5px; text-indent:2em"><span style="white-space:nowrap"><span style="color:#333333;"></span></span><pre name="code" class="java">class Deadlocker {
 int field_1;
 private Object lock_1 = new int[1];
 int field_2;
 private Object lock_2 = new int[1];

 public void method1(int value) {
  “synchronized” (lock_1) {
   “synchronized” (lock_2) {
    field_1 = 0; field_2 = 0;
   }
  }
 }

 public void method2(int value) {
  “synchronized” (lock_2) {
   “synchronized” (lock_1) {
    field_1 = 0; field_2 = 0;
   }
  }
 }
}
 

八、线程间通信
多个线程在操作同一个资源,但是操作的动作不同。
线程间通信经典示例程序
/*
 * 生产者生产烤鸭,消费者消费
 * 两个或多个生产者,生产一次就等待消费一次
 * 多个消费者,等待生产者生产一次就消费
 *
 */
class Store {
	private boolean flag=true;
	private String name;
	private int count=1;
	public synchronized void set(String name) {
		this.name=name+count;
		while(flag) {
			try{
				wait();
			}
			catch(Exception e) {
			}
		}
			System.out.println(Thread.currentThread().getName()+":"+"生产者生产"+this.name);
		count++;
		flag=false;
		notifyAll();
	}
	public synchronized  void out() {
		while(!flag) {
			try{
				wait();
			}
			catch(Exception e) {
			}
		}
			System.out.println(Thread.currentThread().getName()+":"+"消费者消费"+this.name);
		count++;
		flag=true;
		notifyAll();
    }
}
class Producer implements Runnable {
	Store r;
	Producer(Store r) {
		this.r=r;
	}
	public void run() {
		while(true) {
		r.set("烤鸭");	
		}
	}
}
class Consumer implements Runnable {
	Store r;
Consumer(Store r) {
	this.r=r;
}
public void run() {
	while(true) {
	r.out();	
	}	
  }
}  
public class ProducerConsumer {

	public static void main(String[] args) {
		Store r=new Store();
      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();
	}
}
注意:

1)wait(),sleep()有什么区别?

              wait():时间可以规定也可以不规定,释放cpu执行权,释放锁。

              sleep():必须规定时间,释放cpu执行权,不释放锁。

  2)为甚么要定义notifyAll

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

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

        将同步synchronized替换成显示的Lock操作。将ObjectwaitnotifynotifyAll,替换成了Condition对象。该Condition对象可以通过Lock锁进行获取,并支持多个相关的Condition对象。

示例:(注意与之前的比较)

/*
生产者生产商品,供消费者使用
有两个或者多个生产者,生产一次就等待消费一次
有两个或者多个消费者,等待生产者生产一次就消费掉

*/

import java.util.concurrent.locks.*;

class Resource 
{	
	private String name;
	private int count=1;
	private boolean flag = false;
	
	//多态
	private Lock lock=new ReentrantLock();

	//创建两Condition对象,分别来控制等待或唤醒本方和对方线程
	Condition condition_pro=lock.newCondition();
	Condition condition_con=lock.newCondition();

	//p1、p2共享此方法
	public void setProducer(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.signal();//唤醒对方
		}
		finally
		{
			lock.unlock();//解锁,这个动作一定执行
		}
		
	}

	//c1、c2共享此方法
	public void getConsumer()throws InterruptedException
	{
		lock.lock();
		try
		{
			while(!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;
	}
	//复写run方法
	public void run()
	{
		while(true)
		{
			try
			{
				res.setProducer("商品");
			}
			catch (InterruptedException e)
			{
			}
		}
	}
}

//消费者线程
class Consumer implements Runnable
{
	private Resource res;
	Consumer(Resource res)
	{
		this.res=res;
	}
	//复写run
	public void run()
	{
		while(true)
		{
			try
			{
				res.getConsumer();
			}
			catch (InterruptedException e)
			{
			}
		}
	}

}

class  ProducerConsumer
{
	public static void main(String[] args) 
	{
		Resource res=new Resource();

		new Thread(new Producer(res)).start();//第一个生产线程 p1
		new Thread(new Consumer(res)).start();//第一个消费线程 c1

		new Thread(new Producer(res)).start();//第二个生产线程 p2
		new Thread(new Consumer(res)).start();//第二个消费线程 c2
	}
}

九、停止线程

停止线程就是让run方法结束。

有两种方法:

1)开启多线程运行,运行代码通常是循环结构。只要控制住循环,就可以让run方法结束,也就是线程结束。那么只要在主函数或者其他线程中,在该线程执行一段时间后,将标记flag赋值false,该run方法就会结束,线程也就停止了。

       2) 当线程处于冻结状态。就不会读取到标记。那么线程就不会结束 ,这时需要对冻结进行清除。强制让线程恢复到运行状态中来。这样就可以操作标记让线程结束。 Thread 类提供该方法 interrupt();


十、其他几种需要了解方法:
1join方法
      当A线程执行到了b线程的.join()方法时,A线程就会等待,等B线程都执行完,A线程才会执行。(此时B和其他线程交替运行。)join可以用来临时加入线程执行。
2 setPriority ()方法用来设置优先级
 MAX_PRIORITY  最高优先级 10
  MIN_PRIORITY    最低优先级 1
  NORM_PRIORITY  分配给线程的默认优先级
3 yield ()方法可以暂停当前线程,让其他线程执行。

 













评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值