13(多线程)

来源:互联网 发布:袁和平 知乎 编辑:程序博客网 时间:2024/06/17 11:21

多线程

1多线程的两种使用方式

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

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

栗子:直接用第二种

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

/* * 1,该类实现Runnable接口 * 2,实现run()方法 * 3,实例化该类,在new个Thread(Runnable),传入该类,用start启动 * */ class MyThreads implements Runnable{@Overridepublic 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定义一个flagprivate boolean flag = true;@Overridepublic 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; //实现的方法@Overridepublic 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;  //实现的方法@Overridepublic 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设为truefalg = true;//生产完本来是要挂自己,然后等顾客吃完再notify自己//但是因为notify是随机唤醒线程,所以万一顾客先抢到线程,顾客就会先挂起//所以这里就先要将顾客线程先唤醒释放掉,然后再挂起自己this.notify();//然后再挂起自己this.wait();//wait()会抛异常,需要捕获} catch (InterruptedException e) {e.printStackTrace();}}} } //覆盖的方法@Overridepublic 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();}}} } //实现方法@Overridepublic 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();}
输出:

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

..........

........




0 0
原创粉丝点击