随笔- 105  评论- 17  文章- 3 

多线程初探:三种方式创建多线程详细示例

多线程即在同一时间,可以做多件事情。

创建多线程有3种方式,分别是继承线程类,实现Runnable接口,匿名类

一:线程概念

首先要理解进程(Processor)和线程(Thread)的区别

  • 进程:启动一个LOL.exe就叫一个进程。 接着又启动一个DOTA.exe,这叫两个进程。
  • 线程:线程是在进程内部同时做的事情,比如在LOL里,有很多事情要同时做,比如"盖伦” 击杀“提莫”,同时“赏金猎人”又在击杀“盲僧”,这就是由多线程来实现的。

示例1: 在盖伦杀掉提莫后,赏金猎人才开始杀盲僧

Hero.java

public class Hero {

    public String name;
    public float hp;

    public int damage;

    public void attackHero(Hero h) {
        try {
            //为了表示攻击需要时间,每次攻击暂停1000毫秒
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        h.hp-=damage;
        System.out.format("%s 正在攻击 %s, %s的血变成了 %.0f%n",name,h.name,h.name,h.hp);

        if(h.isDead())
            System.out.println(h.name +"死了!");
    }

    public boolean isDead() {
        return 0>=hp?true:false;
    }
}

 

TestThread.java

public class TestThread {

    public static void main(String[] args) {

        Hero gareen = new Hero();
        gareen.name = "盖伦";
        gareen.hp = 616;
        gareen.damage = 50;

        Hero teemo = new Hero();
        teemo.name = "提莫";
        teemo.hp = 300;
        teemo.damage = 30;

        Hero bh = new Hero();
        bh.name = "赏金猎人";
        bh.hp = 500;
        bh.damage = 65;

        Hero leesin = new Hero();
        leesin.name = "盲僧";
        leesin.hp = 455;
        leesin.damage = 80;

        //盖伦攻击提莫
        while(!teemo.isDead()){
            gareen.attackHero(teemo);
        }

        //赏金猎人攻击盲僧
        while(!leesin.isDead()){
            bh.attackHero(leesin);
        }
    }
}

 

打印日志:

盖伦 正在攻击 提莫, 提莫的血变成了 250
盖伦 正在攻击 提莫, 提莫的血变成了 200
盖伦 正在攻击 提莫, 提莫的血变成了 150
盖伦 正在攻击 提莫, 提莫的血变成了 100
盖伦 正在攻击 提莫, 提莫的血变成了 50
盖伦 正在攻击 提莫, 提莫的血变成了 0
提莫死了!
赏金猎人 正在攻击 盲僧, 盲僧的血变成了 390
赏金猎人 正在攻击 盲僧, 盲僧的血变成了 325
赏金猎人 正在攻击 盲僧, 盲僧的血变成了 260
赏金猎人 正在攻击 盲僧, 盲僧的血变成了 195
赏金猎人 正在攻击 盲僧, 盲僧的血变成了 130
赏金猎人 正在攻击 盲僧, 盲僧的血变成了 65
赏金猎人 正在攻击 盲僧, 盲僧的血变成了 0
盲僧死了!

二:多线程-继承Thread

多线程:继承线程类

使用多线程,就可以做到盖伦在攻击提莫的同时,赏金猎人也在攻击盲僧

设计一个类KillThread,继承Thread,并且重写run方法

启动线程办法:

实例化一个KillThread对象,并且调用其start方法 就可以观察到 赏金猎人攻击盲僧的同时,盖伦也在攻击提莫

KillThread.java

public class KillThread extends Thread{
     
    private Hero h1;
    private Hero h2;
 
    public KillThread(Hero h1, Hero h2){
        this.h1 = h1;
        this.h2 = h2;
    }
 
    public void run(){
        while(!h2.isDead()){
            h1.attackHero(h2);
        }
    }
}

 

TestKillThread.java

public class TestKillThread {
 
    public static void main(String[] args) {
         
        Hero gareen = new Hero();
        gareen.name = "盖伦";
        gareen.hp = 616;
        gareen.damage = 50;
 
        Hero teemo = new Hero();
        teemo.name = "提莫";
        teemo.hp = 300;
        teemo.damage = 30;
         
        Hero bh = new Hero();
        bh.name = "赏金猎人";
        bh.hp = 500;
        bh.damage = 65;
         
        Hero leesin = new Hero();
        leesin.name = "盲僧";
        leesin.hp = 455;
        leesin.damage = 80;
         
        KillThread killThread1 = new KillThread(gareen,teemo);
        killThread1.start();
        KillThread killThread2 = new KillThread(bh,leesin);
        killThread2.start();
         
    }
     
}

 

打印日志:

盖伦 正在攻击 提莫, 提莫的血变成了 250
赏金猎人 正在攻击 盲僧, 盲僧的血变成了 390
赏金猎人 正在攻击 盲僧, 盲僧的血变成了 325
盖伦 正在攻击 提莫, 提莫的血变成了 200
赏金猎人 正在攻击 盲僧, 盲僧的血变成了 260
盖伦 正在攻击 提莫, 提莫的血变成了 150
赏金猎人 正在攻击 盲僧, 盲僧的血变成了 195
盖伦 正在攻击 提莫, 提莫的血变成了 100
赏金猎人 正在攻击 盲僧, 盲僧的血变成了 130
盖伦 正在攻击 提莫, 提莫的血变成了 50
赏金猎人 正在攻击 盲僧, 盲僧的血变成了 65
盖伦 正在攻击 提莫, 提莫的血变成了 0
提莫死了!
赏金猎人 正在攻击 盲僧, 盲僧的血变成了 0
盲僧死了!

三:多线程-实现Runable()接口

创建类Battle,实现Runnable接口 启动的时候,首先创建一个Battle对象,然后再根据该battle对象创建一个线程对象,并启动

Battle battle1 = new Battle(gareen,teemo);
new Thread(battle1).start();

 

battle1 对象实现了Runnable接口,所以有run方法,但是直接调用run方法,并不会启动一个新的线程。 必须,借助一个线程对象的start()方法,才会启动一个新的线程。 所以,在创建Thread对象的时候,把battle1作为构造方法的参数传递进去,这个线程启动的时候,就会去执行battle1.run()方法了。

Battle.java

public class Battle implements Runnable{
     
    private Hero h1;
    private Hero h2;
 
    public Battle(Hero h1, Hero h2){
        this.h1 = h1;
        this.h2 = h2;
    }
 
    public void run(){
        while(!h2.isDead()){
            h1.attackHero(h2);
        }
    }
}

 

TestRunableThread.java

public class TestRunableThread {
 
    public static void main(String[] args) {
         
        Hero gareen = new Hero();
        gareen.name = "盖伦";
        gareen.hp = 616;
        gareen.damage = 50;
 
        Hero teemo = new Hero();
        teemo.name = "提莫";
        teemo.hp = 300;
        teemo.damage = 30;
         
        Hero bh = new Hero();
        bh.name = "赏金猎人";
        bh.hp = 500;
        bh.damage = 65;
         
        Hero leesin = new Hero();
        leesin.name = "盲僧";
        leesin.hp = 455;
        leesin.damage = 80;
         
        Battle battle1 = new Battle(gareen,teemo);
         
        new Thread(battle1).start();
 
        Battle battle2 = new Battle(bh,leesin);
        new Thread(battle2).start();
 
    }
     
}

 

打印日志:

盖伦 正在攻击 提莫, 提莫的血变成了 250
赏金猎人 正在攻击 盲僧, 盲僧的血变成了 390
盖伦 正在攻击 提莫, 提莫的血变成了 200
赏金猎人 正在攻击 盲僧, 盲僧的血变成了 325
盖伦 正在攻击 提莫, 提莫的血变成了 150
赏金猎人 正在攻击 盲僧, 盲僧的血变成了 260
盖伦 正在攻击 提莫, 提莫的血变成了 100
赏金猎人 正在攻击 盲僧, 盲僧的血变成了 195
盖伦 正在攻击 提莫, 提莫的血变成了 50
赏金猎人 正在攻击 盲僧, 盲僧的血变成了 130
盖伦 正在攻击 提莫, 提莫的血变成了 0
提莫死了!
赏金猎人 正在攻击 盲僧, 盲僧的血变成了 65
赏金猎人 正在攻击 盲僧, 盲僧的血变成了 0
盲僧死了!

四:多线程-匿名类

使用匿名类,继承Thread,重写run方法,直接在run方法中写业务代码

匿名类的一个好处是可以很方便的访问外部的局部变量。

前提是外部的局部变量需要被声明为final。(JDK7以后就不需要了)

public class TestThread {
  
    public static void main(String[] args) {
          
        Hero gareen = new Hero();
        gareen.name = "盖伦";
        gareen.hp = 616;
        gareen.damage = 50;
  
        Hero teemo = new Hero();
        teemo.name = "提莫";
        teemo.hp = 300;
        teemo.damage = 30;
          
        Hero bh = new Hero();
        bh.name = "赏金猎人";
        bh.hp = 500;
        bh.damage = 65;
          
        Hero leesin = new Hero();
        leesin.name = "盲僧";
        leesin.hp = 455;
        leesin.damage = 80;
          
        //匿名类
        Thread t1= new Thread(){
            public void run(){
                //匿名类中用到外部的局部变量teemo,必须把teemo声明为final
                //但是在JDK7以后,就不是必须加final的了
                while(!teemo.isDead()){
                    gareen.attackHero(teemo);
                }              
            }
        };
         
        t1.start();
          
        Thread t2= new Thread(){
            public void run(){
                while(!leesin.isDead()){
                    bh.attackHero(leesin);
                }              
            }
        };
        t2.start();
         
    }
      
}

 

总结:

实现多线程的3种方式:

  1. 继承Thread类
  2. 实现Runnable接口
  3. 匿名类的方式

注: 启动线程是start()方法,run()并不能启动一个新的线程

实现Runnable接口比继承Thread类所具有的优势:

  • 1):适合多个相同的程序代码的线程去处理同一个资源
  • 2):可以避免java中的单继承的限制
  • 3):增加程序的健壮性,代码可以被多个线程共享,代码和数据独立
  • 4):线程池只能放入实现Runable或callable类线程,不能直接放入继承Thread的类

知识点

main方法其实也是一个线程。

在java中所以的线程都是同时启动的,至于什么时候,哪个先执行,完全看谁先得到CPU的资源。

在java中,每次程序运行至少启动2个线程。一个是main线程,一个是垃圾收集线程。因为每当使用java命令执行一个类的时候,实际上都会启动一个JVM,每一个jVM实习在就是在操作系统中启动了一个进程。

 

 

公众号:一只阿木木

posted on 2018-08-01 15:56 一只阿木木 阅读(...) 评论(...) 编辑 收藏