Java - 并发编程:创建线程

Java程序总是从一个静态且无返回值的 main() 方法开始从上至下依次执行。在不添加其它线程情况下,程序执行时会占用一个CPU进程,且这个进程中只包含一条主线程。在Java中,创建线程首先要定义线程类,然后将线程类实例化为线程对象后等待后续调用。实现这个过程有三种方式。

一、继承 Thread 类

java.lang.Thread:Java中用于描述线程的类。创建线程的方法之一便是自定义一个类继承 Thread 类。然后重写 Thread 类提供的 run() 方法来设置线程任务。

1. 实现步骤

  1. 定义一个继承 Thread 类的线程类
  2. 在线程类中重写 Thread 类提供的 run() 方法,设置线程任务(开启新线程后要做什么?)
  3. 实例化线程类
  4. 调用 Thread 类提供的 start() 方法,启动新线程,执行 run() 方法

2. 相关方法

  • void start(): 使目标线程开始执行;Java虚拟机调用目标线程的 run() 方法,实现双线程并发执行:即当前线程(main线程)和另一个线程(实例化的自定义线程对象,执行其 run() 方法)
  • String getName():获取线程名称
  • public 自定义线程类 currentThread(): 获取当前正在执行的线程对象的引用
  • public static void sleep(long millis): 使当前正在执行的线程以指定的毫秒数暂停一段时间

通过继承 Thread 类创建线程的示例:

// 1.定义一个Thread类的子类
class MyThread extends Thread {
    public MyThread() { }
    public MyThread(String name) {
        super(name);
    }
    // 2.在Thread类的子类中重写 run() 方法,设置线程任务(开启线程要做什么?)
    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            try {
                Thread.sleep(1);      // 使用Thread.sleep增加线程阻塞几率
            } catch (InterruptedException e) {
                e.printStackTrace();        // sleep() 方法需要处理中断异常
            }
            // 打印当前线程名称的两种方式
            // System.out.println(Thread.currentThread().getName());
            // 通过Thread类的静态方法调用线程名
            System.out.println(this.getName() + ": " + i);
            // 由于继承了Thread类,因此可以使用this直接调用getName()方法
        }
    }
}

class DemoThread {
    public static void main(String[] args) {
        // 3.实例化Thread子类对象
        MyThread mt1 = new MyThread();
        // 4.调用Thread类中的 start() 方法,启动新线程,执行 run() 方法
        mt1.start();
        MyThread mt2 = new MyThread();
        // 4.调用Thread类中的 start() 方法,启动新线程,执行 run() 方法
        mt2.start();
        // 主线程方法
        for (int i = 0; i < 20; i++) {
            try {
                Thread.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("main: " + i);
        }
    }
}

运行结果

Thread-1: 0
main: 0
Thread-0: 0
Thread-1: 1
main: 1
Thread-0: 1
Thread-1: 2
main: 2
Thread-0: 2
Thread-1: 3
main: 3
Thread-0: 3
……

二、实现 Runnable 接口

java.lang.Runnable:Runnable 接口提供了另一种创建线程类的途径,Runnable 的实现类可以在不继承 Thread 类的情况下实例化一个线程实例。通常情况下,如果只是通过重写 run() 方法来设置线程任务,而不打算修改过增强 Thread 类中的相关方法的话,那么通过实现 Runnable 接口就可以满足需求。

1. 实现步骤

  1. 定义一个 Runnable 接口的实现类
  2. 在实现类中重写 Runnable 接口的 run() 方法,设置线程任务
  3. 实例化一个 Runnable 接口的实现类对象 target
  4. 通过 Thread 类的构造方法 Thread(Runnable target) 构造出线程对象
  5. 调用 Thread 类对象的 start() 方法,开启新的线程执行 run() 方法

tips:Runnable 对象仅仅作为 Thread 对象的 target,Runnable 实现类里包含的 run() 方法仅作为线程执行体。
而实际的线程对象依然是 Thread 实例,只是该 Thread 线程负责执行其 target 的 run() 方法。

2. 相关方法

  • Thread(Runnable target):通过传入一个 Runnable 接口的实现类来分配线程对象
  • Thread(Runnable target, String name):重载方法,传入一个 Rnnable 接口的实现类和一个线程名来分配新的线程对象
// 1.定义一个Runnable接口的实现类
class RunnableImpl implements Runnable{
    @Override
    public void run() {
        // 2.在实现类中重写Runnable接口的 run() 方法,设置线程任务
        for (int i = 0; i < 20; i++) {
            try {
                Thread.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + ": " + i);
        }
    }
}

class DemoRunnable {
    public static void main(String[] args) {
        // 3.实例化一个Runnable接口的实现类对象
        RunnableImpl run = new RunnableImpl();
        // 4.实例化一个Thread类对象,向构造方法中传递Runnable接口的实现类对象
        Thread thread = new Thread(run);     // 开启子线程
        // 5.调用Thread类对象的 start() 方法,开启新的线程执行 run() 方法
        thread.start();
        new Thread(new RunnableImpl()).start();    // 传递不同的实现类,实现不同的任务
        // 主线程
        for (int i = 0; i < 20; i++) {
            try {
                Thread.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + ": " + i);
        }
    }
}

运行结果:

main: 0
main: 1
Thread-0: 0
Thread-1: 0
main: 2
Thread-0: 1
Thread-1: 1
Thread-0: 2
Thread-1: 2
main: 3
Thread-0: 3
main: 4
Thread-1: 3
Thread-0: 4
……

3. Runnable 接口与 Thread 类对比

实际上所有的多线程代码都是通过运行 Thread 的 start() 方法来运行的。因此,不管是继承 Thread 类还是实现
Runnable 接口来实现多线程,最终还是通过 Thread 的对象的 API 来控制线程的,熟悉 Thread 类的 API 是进行多线程编程的基础。

实现Runnable接口比继承Thread类所具有的优势在于:如果一个类继承Thread,则不适合资源共享。但是如果实现了Runable接口的话,则很容易的实现资源共享。 具体可以概括为以下几点

  1. 适合多个相同的程序代码的线程去共享同一个资源。
  2. 可以避免java中的单继承的局限性。
  3. 增加程序的健壮性,将设置线程启动线程分离,实现解耦操作,代码可以被多个线程共享,代码和线程独立。
  4. 线程池只能放入实现Runable或Callable类线程,不能直接放入继承Thread的类。
扩充:在java中,每次程序运行至少启动2个线程。一个是main线程,一个是垃圾收集线程。因为每当使用
java命令执行一个类的时候,实际上都会启动一个JVM,每一个JVM其实在就是在操作系统中启动了一个进
程。

三、匿名线程

线程对象也可以使用线程的内匿名内部类方式创建和启动,可以方便的实现每个线程执行不同的线程任务操作。使用匿名内部类实现线程对象的优势在于减少了代码量,并且将许多流程合并:

  • 将 Thread 类继承、重写 run() 方法、继承类实例化、线程启动一步完成
  • 将 Runnable 接口实现、重写 run() 方法、实现类实例化、Thread 对象实例化、线程启动一步完成

1. 继承 Thread 类的匿名线程

class DemoUnnamedThread {
    public static void main(String[] args) {
        // Thread-0
        new Thread() {
            public void run() {
                for (int i = 0; i < 2000; i++) {
                    System.out.println(this.getName() + "-->" + i);
                }
            }
        }.start();
        // Thread-1
        new Thread(
            new Runnable() {
                @Override
                public void run() {
                    for (int i = 0; i < 2000; i++) {
                        System.out.println(Thread.currentThread().getName() + "-->" + i);
                    }
                }
            }
        ).start();
        // main
        for (int i = 0; i < 2000; i++) {
            System.out.println(Thread.currentThread().getName() + "-->" + i);
        }
    }
}

2. 实现 Runnable 接口的匿名线程

class DemoUnnamedRunnable {
    public static void main(String[] args) {
        Runnable run = new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 2000; i++) {
                    System.out.println(Thread.currentThread().getName() + "-->" + i);
                }
            }
        };
        new Thread(run).start();                                        // 实例化线程对象也可以合并到上面
        // 主线程
        for (int i = 0; i < 2000; i++) {
            System.out.println(Thread.currentThread().getName() + "-->" + i);
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值