第一种:继承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(); }输出:厨师:生产了火锅顾客 :正在吃 火锅厨师:生产了米饭顾客 :正在吃 米饭厨师:生产了沙拉顾客 :正在吃 沙拉厨师:生产了米饭顾客 :正在吃 米饭
..........
........
新闻热点
疑难解答