室友打了一把王者,我就学会了Java创建线程的三种方式

Java创建线程的三种方式

Java中创建线程的方式有以下三种:

  • 继承Thread类实现run()方法
  • 实现Runable接口run()方法
  • 实现Callable接口call()方法

对于很多初学者来说,多线程的概念很抽象,不好理解,比如:

  • 进程是操作系统分配资源的最小单位,一个进程可以包含多个线程。每个进程都有独立的代码和数据空间(程序上下文),并且通过进程间通信机制(IPC)进行通信。每个进程在内存中拥有独立的地址空间,一个进程崩溃或异常不会影响其他进程的正常运行。
  • 线程是一种轻量级的进程,也可称为执行路径。线程是进程内的一个实体,更确切的说,线程是CPU调度的最小单位。同一个进程内的多个线程共享代码和数据空间,每个线程还有自己的栈空间和寄存器上下文,并且可以通过共享内存或互斥量等方式与其他线程进行通信,因此线程之间的开销远低于进程。

很抽象,对不对?打个比喻,你在打一把王者:

进程可以比作是你开的这一把游戏
线程可以比作是你所选的英雄或者是游戏中的水晶野怪等之类的。
带着这个比喻来理解进程和线程的一些关系,一个进程可以有多个线程就叫多线程。是不是感觉非常好理解了?

❤1、线程在进程下进行

(单独的英雄角色、野怪、小兵肯定不能运行)

❤2、进程之间不会相互影响,主线程结束将会导致整个进程结束

(两把游戏之间不会有联系和影响。你的水晶被推掉,你这把游戏就结束了)

❤3、不同的进程数据很难共享

(两把游戏之间很难有联系,有联系的情况比如上把的敌人这把又匹配到了)

❤4、同进程下的不同线程之间数据很容易共享

(你开的那一把游戏,你可以看到每个玩家的状态——生死,也可以看到每个玩家的出装等等)

❤5、进程使用内存地址可以限定使用量

(开的房间模式,决定了你可以设置有多少人进,当房间满了后,其他人就进不去了,除非有人退出房间,其他人才能进)

具体实现

搞清楚上面这些概念之后,我们来看一下多线程创建的三种方式:

继承Thread类
public class MyThread extends Thread{

    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println(getName()+"补刀==》 "+ i);
        }
    }
}

测试用例

public static void main(String[] args) {
       MyThread a = new MyThread();
       MyThread b = new MyThread();
       //给两个线程设置名字,调用的是父类Thread的方法
       a.setName("张三");
       b.setName("李四");
       a.start();
       b.start();
   }

在这里插入图片描述

继承Runable接口
public class MyRunnable implements Runnable{
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println(Thread.currentThread().getName() + "补刀==》 " + i);
        }
    }
}

测试方法:

 public static void main(String[] args) {
        MyRunnable a = new MyRunnable();
        MyRunnable b = new MyRunnable();
        Thread t1 = new Thread(a, "张三");
        Thread t2 = new Thread(b, "李四");
        t1.start();
        t2.start();
    }

在这里插入图片描述

实现callable接口重写call()方法,可以通过FutureTask获取任务执行的返回值。
public class MyCallAble implements Callable {
    @Override
    public Object call() throws Exception {
        return Thread.currentThread().getName();
    }
}

测试代码:

public static void main(String[] args) {
        //创建异步任务
        FutureTask<String> task=new FutureTask<String>(new MyCallAble());
        //启动线程
        new Thread(task,"我是张三").start();
        try {
            //等待执行完成,并获取返回结果
            String result=task.get();
            System.out.println(result);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }

在这里插入图片描述

总结

1、为什么要重写run方法?

因为run方法是用来封装被线程执行的代码,线程启动(调用start()方法后),会执行run()方法中的代码。

2、run()方法和start()方法有什么区别?

run():封装线程执行的代码,直接调用相当于调用普通方法。
start():启动线程,然后由JVM 调用此线程的 run() 方法。

3、通过继承 Thread 的方法和实现 Runnable 接口的方式创建多线程,哪个好?

实现Runable接口好,原因有两个:

①、避免了Java单继承的局限性
②、适合多个相同的程序代码去处理同一资源的情况,把线程、代码和数据有效的分离,更符合面向对象的设计思想。