首页 > 学院 > 开发设计 > 正文

13(多线程)

2019-11-06 08:05:08
字体:
来源:转载
供稿:网友

多线程

1多线程的两种使用方式

第一种:继承Thread类(不建议使用,因为无法多继承)

第二种:实现Runnable接口(建议使用)

栗子:直接用第二种

写一个类实现Runnable接口,并覆盖其中的方法

/* * 1,该类实现Runnable接口 * 2,实现run()方法 * 3,实例化该类,在new个Thread(Runnable),传入该类,用start启动 * */ class MyThreads implements Runnable{	@Override	public void run() {		for(int i=0;i<20;i++){			//线程都是有名字的,可以用Thread.currentThread()返回Thread对象,在掉用getName()获取该线程的名字			System.out.PRintln(Thread.currentThread().getName()+": "+i);		}	}	  }启动这个线程需要start()方法,因为Runnable接口中没有这个方法,所以需要Thread类来启动
	public static void main(String[] args) {		//1先实例化出自己的线程类		MyThreads mt = new MyThreads();				//2要用start()方法启动线程,因为接口没有该方法所以需要Thread		//Threa可以传入Runnable参数,还可以传入一个线程的名字		Thread th = new Thread(mt,"MyThread");				//3要用start()方法启动线程		th.start();				//main自己跑一个线程		for(int i=0;i<20;i++){			System.out.println(Thread.currentThread().getName()+": "+i);		}			}这样两个线程就会交替的跑起来

2线程的调度

看最顶层的图:1,Statr()让线程进入Runnable状态(等待调用),调度表中轮到该线程时,就会进入Running(正在运行)

2,如果这时我不想在跑了,可以调用yield()返回到Runnable(等待调用状态),(注意的是对多核的计算机可能无效)

3,sleep()让该线程去睡觉,进入Blocke(睡觉状态,不会在去竞争),直到睡觉时间结束。

4,join(),让该线程一直运行完,其他的线程才能去运行

5,synchronized(同步)关键字会让一段代码上锁,直到这段代码运行完(线程的同步)

6,wait()方法让该线程挂起来(暂停),不在参与竞争

7,notify()方法会让线程从新运行起来,注意的是要在线程都要挂在同一个对象上(也就是同一把钥匙),并且是随机唤醒其中的任意对象。

3线程的终止

不应该调用stop()方法,这样不安全(并且已经过时),最佳停止方法是自己写一个停止方法,在该方法内释放资源

看栗子:

/* * 最佳停止方法 * 1,定义一个flag * 2,break跳出循环 * 3,写一个停止方法 * */class MyThre implements Runnable{	//1定义一个flag	private boolean flag = true;	@Override	public void run() {		for(int i=0;i<100;i++){			//2判断停止			if(!flag) break;						System.out.println("iiii"+i);		}	}		//停止方法	public void stopThread(){		flag = false;		//可以在这里释放资源	}}条件满足是,掉用停止方法
	public static void main(String[] args) {		MyThre mt = new MyThre();		Thread t = new Thread(mt);		t.start();				//main自己的线程		while(true){			if(mt.i>=30){	//当条件满足时,调用停止方法				mt.stopThread();								break;			}		}			}

4线程的同步

线程同步可以解决多线程竞争同一个资源保证其安全的一种手段

线程同步要用synchronized关键字,并且需要一个任意对象做为钥匙

当方法声明为同步方法是,会用this做为钥匙

栗子:

/* * 线程同步 * */ class MyThreads implements Runnable{	 //定义一把钥匙,也可以用this做钥匙	 Object o = new Object();	 	 int index = 0;	 //实现的方法	@Override	public void run() {		synchronized (o) {			for(index =0;index<1000;index++){				System.out.println(Thread.currentThread().getName()+": "+index);			}		}	}	  }
	public static void main(String[] args) {		MyThreads mt = new MyThreads();				//实例化两个线程		//这样就不会同时修改一个数据了		Thread t1 = new Thread(mt,"T1");		Thread t2 = new Thread(mt,"T2");		t1.start();		t2.start();			}

5同步问题(死锁)

用了线程同步后,如果操作不当会造成死锁等问题

栗子:

/* * 线程死锁 * */ class MyThreads implements Runnable{	 //定义两把钥匙	 Object key1 = new Object();	 Object key2 = new Object();	 //	 boolean flag = true;	 	 //实现的方法	@Override	public void run() {		//1判断是否进入,		 if(flag){			 synchronized (key1) {	//好人拿第一把钥匙进入				 //2进来后就为false				 flag = false;				 System.out.println(Thread.currentThread().getName()+"我拿key1进来了");				 //注意了,万一去睡觉了				 try {					Thread.sleep(100);				} catch (InterruptedException e) {					e.printStackTrace();				}				 				 //拿第二把钥匙第二次进入				 synchronized (key2) {					 System.out.println(Thread.currentThread().getName()+"我拿key2进来了");				}			}		 }else{			 //第二条线程坏人先拿第二把钥匙进入			 synchronized (key2) {				//进来后要设为true,能让线程一第二次进入				 flag = true;				 System.out.println(Thread.currentThread().getName()+"我拿key2进来了");				 //注意了,万一去睡觉了				 try {					Thread.sleep(100);				} catch (InterruptedException e) {					e.printStackTrace();				}				 				 //该线程要拿第一把钥匙再次进入				 synchronized (key1) {					 System.out.println(Thread.currentThread().getName()+"我拿key1进来了");				}			}		 }	 }	  }启动线程
	public static void main(String[] args) {		MyThreads mt = new MyThreads();				//实例化两个线程		//这样就不会同时修改一个数据了		Thread t1 = new Thread(mt,"好人");		Thread t2 = new Thread(mt,"坏人");		t1.start();		t2.start();			}解析:一般都是同步嵌套容易造成死锁问题

第一条线程好人拿着第一把钥匙进去,然后就跑去睡觉(false了),这样第二条线程坏人就会拿着第二把钥匙进去,然后它又睡觉去了(true了),就轮到好人要拿第二把钥匙进去,但是这第二把钥匙在坏人手中,它又在睡觉,好人就永远不可能等到第二把钥匙,这样就会等到死,造成坏人也不不可能拿到第一钥匙。

6生产者和消费者(wait和notify)

假如:一个厨师不停的做食物,做一个顾客就吃一个,直到厨师做完

厨师就是生产者,顾客就是消费者

看例子:注意的是notify需要同一个对象做钥匙

厨师类:

/* * 生产者:厨师 * */ class Cool implements Runnable{	 String[] food;	//一组要生产的食物	 String foods;	//生产好的食物	 	 boolean falg;	//判断是否做了食物	 	 //构造方法	 public Cool() {		 food = new String[]{"米饭","汉堡","沙拉","火锅","排骨"};		 falg = false;	//开始是没做食物的	}	 	 //做食物方法	 public void make(){		 //因为要一直做完才能拿去给顾客,所以要同步		 //用自己做钥匙,应为顾客中也有厨师这个对象,要使用同一个对象做钥匙		 synchronized (this) {			 //判断是否做了食物			if(!falg){				try {					int index = new Random().nextInt(food.length);					foods = food[index];	//这里随机生产食物					System.out.println(Thread.currentThread().getName()+":生产了"+foods);										//生产完就要将falg设为true					falg = true;										//生产完本来是要挂自己,然后等顾客吃完再notify自己					//但是因为notify是随机唤醒线程,所以万一顾客先抢到线程,顾客就会先挂起					//所以这里就先要将顾客线程先唤醒释放掉,然后再挂起自己					this.notify();					//然后再挂起自己					this.wait();	//wait()会抛异常,需要捕获									} catch (InterruptedException e) {					e.printStackTrace();				}			}		}	 }	 //覆盖的方法	@Override	public void run() {		//不停的生产食物		for(int i=0;i<10;i++){			make();		}	}	  }顾客类:
/* *  消费者:顾客 *  */ class Customer implements Runnable{	 //需要厨师这个对象	 Cool c;	 	 //构造方法	 public Customer(Cool c) {		 this.c = c;	}	 	 //吃方法	 public void eat(){		// 一直要吃完才通知厨师去做,所以需要同步		 //注意的要用同意把钥匙做对象		 //这里要用厨师做钥匙		 synchronized (c) {			 //判断厨师是否做了食物			if(c.falg){				try {					System.out.println(Thread.currentThread().getName()+" :正在吃 "+c.foods);					//吃完要叫厨师做,先设为false,让食物为空,在唤醒厨师的线程					c.falg = false;					c.notify();										//吃完将自己挂起来,等厨师去做					c.wait();									} catch (InterruptedException e) {					e.printStackTrace();				}			}else{				//这里如果是顾客先抢到线程,但厨师还没做,这时就要把自己挂起来,等厨师的线程去做				//所以为什么前面先要唤醒线程,在去挂起				try {					c.wait();				} catch (InterruptedException e) {					e.printStackTrace();				}			}		}	 }	 //实现方法	@Override	public void run() {		//不停的吃		while(true){			eat();		}	}	  }启动类:
	public static void main(String[] args) {		Cool c = new Cool();	//实例化厨师		Customer cu = new Customer(c);	//实例化顾客				//创建线程,并未线程命名		Thread ct = new Thread(c,"厨师");		Thread cut = new Thread(cu,"顾客");		//这里注意的是因为顾客是while死循环不停吃食物 		//当厨师生产完所有实物后要终止程序,所以要将顾客线程设为后台线程		//后台线程的意思是:当所有线程结束时,这个线程也就结束了。		//用setDaemon(true)方法,注意这个方法一定要在线程启动之前声明		cut.setDaemon(true);				//然后再启动线程		ct.start();		cut.start();			}输出:

厨师:生产了火锅顾客 :正在吃 火锅厨师:生产了米饭顾客 :正在吃 米饭厨师:生产了沙拉顾客 :正在吃 沙拉厨师:生产了米饭顾客 :正在吃 米饭

..........

........


发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表