多线程(一)

进程和线程的概述

  • 进程就是应用程序在内存中分配的空间,也可理解为一个正在执行中的程序。每一个进程执行都有一个执行顺序,该顺序就是一个执行路径或者叫一个控制单元。
  • 线程就是进程中负责程序执行的执行单元,也可理解为进程中的一个独立的控制单元。线程在控制着进程的执行。

多线程

多线程的概述

一个进程中至少有一个线程在负责该进程的运行。如果一个进程中出现了多个线程,就称该程序为多线程程序。

多线程解决的问题

多线程这门技术的出现解决了多部分代码同时执行的需求,这样做的好处就是可以提高用户的体验效果。
这里有一个疑问——多线程真的能提高效率吗?显然不是,反倒容易死机,但可合理的使用CPU资源。

JVM中的多线程与垃圾回收

多线程的运行是根据CPU的切换完成的,怎么切换CPU说了算,所以多线程运行有一个随机性(CPU的快速切换造成的)。
本节我首先给出结论——JVM中的多线程至少有两个线程:

  1. 一个是负责自定义代码运行的,这个从main方法开始执行的线程称之为主线程。
  2. 一个是负责垃圾回收的。

然后我通过一个简单的案例来演示JVM中的多线程。例如,有如下实验代码:

class Demo
{
    // 定义垃圾回收方法
    public void finalize()
    {
        System.out.println("demo ok");
    }

}
class FinalizeDemo 
{
    public static void main(String[] args) 
    {
        new Demo();
        new Demo();
        new Demo();
        System.gc(); // 启动垃圾回收器。
        System.out.println("Hello World!");
    }
}

运行FinalizeDemo类,可能在屏幕上打印(截图如下):
这里写图片描述
通过实验会发现每次的结果不一定相同,那是因为随机性造成的。而且每一个线程都有自己的代码内容,这个称之为线程的任务,之所以创建一个线程就是为了去运行指定的任务代码。而线程的任务都封装在特定的区域中,比如:

  1. 主线程运行的任务都定义在main方法中。
  2. 垃圾回收线程在收垃圾时都会运行finalize方法。

创建线程方式一

如何在自定义的代码中,自定义一个线程呢?也即如何建立一个执行路径呢?
答:通过对API的查找,java已经提供了对线程这类事物的描述,即Thread类。该类的描述中创建线程有两种方式,下面我就来讲解其第一种方式。
创建线程的第一种方式:继承Thread类。
步骤:

  1. 继承Thread
  2. 重写Thread类中的run()。目的:将自定义的代码存储在run(),让线程运行
  3. 创建子类对象也即创建线程对象
  4. 调用线程的start()。该方法有2个作用:启动线程,调用run()

例,

class Demo extends Thread {
    public void run() {
        for (int x = 0; x < 60; x++) {
            System.out.println("demo run---"+x);
        }
    }
}

class ThreadDemo {
    public static void main(String[] args) {
        Demo d = new Demo(); // 创建好一个线程
        // d.start(); // 开启线程,并执行该线程的run()
        d.run(); // 仅仅是对象的调用方法,而线程创建了,并没有被运行

        for (int x = 0; x < 60; x++) {
            System.out.println("Hello World!---"+x);
        }
    }
}

发现运行结果每一次都不同。
因为多个线程都在获取CPU的执行权,CPU执行到谁,谁就运行。明确一点,在某一个时刻,只能有一个程序在运行(多核除外)。CPU在做着快速的切换,以达到看上去是同时运行的效果。我们可以形象地把多线程的运行形容为互相抢夺CPU的执行权,这就是多线程的一个特点:随机性。谁抢到谁执行,至于执行多长,CPU说了算。
问题一、为什么要覆盖run()呢?
答:Thread类用于描述线程,该类就定义了一个功能,用于存储线程要运行的代码,该存储功能就是run()。也就是说Thread类中的run()用于存储线程要运行的代码。
问题二、调用start方法和调用run方法的区别?
答:调用start方法会开启线程,让开启的线程去执行run方法中的线程任务;而直接调用run方法,线程并未开启,去执行run方法的只有主线程。
练习:创建两个线程,和主线程交替执行。
解:

class Test extends Thread {
    Test(String name) {
        super(name);
    }
    public void run() {
        for (int x = 0; x < 60; x++) {
            System.out.println((Thread.currentThread()==this)+"..."+this.getName()+" run..."+x);
        }
    }
}
class ThreadTest {
    public static void main(String[] args) {
        Test t1 = new Test("one---");
        Test t2 = new Test("two+++");
        t1.start();
        t2.start();
        for (int x = 0; x < 60; x++) {
            System.out.println("main..."+x);
        }
    }
}

通过上例,可发现原来线程都有自己默认的名称:Thread-编号,该编号从0开始。

  • static Thread currentThread():获取当前线程对象
  • getName():获取线程名称
  • setName()或者构造函数:设置线程名称

多线程的运行状态

多线程的运行状态用图来表示即为:
这里写图片描述

创建线程方式二

以此例引申出创建线程的第二种方式:
例,需求:简单的卖票程序。多个窗口同时卖票。

class Ticket implements Runnable {
    private int tick = 100;
    public void run() {
        while(true) {
            if(tick > 0) {
                try { Thread.sleep(10); } catch(Exception e) {}
                System.out.println(Thread.currentThread().getName()+"...sale:"+tick--);
            }
        }
    }
}
class TicketDemo {
    public static void main(String[] args) {
        Ticket t = new Ticket();

        Thread t1 = new Thread(t); // 创建一个线程
        Thread t2 = new Thread(t); // 创建一个线程
        Thread t3 = new Thread(t); // 创建一个线程
        Thread t4 = new Thread(t); // 创建一个线程
        t1.start();
        t2.start();
        t3.start();
        t4.start();
    }
}

创建线程的第二种方式:实现Runnable接口。
步骤:

  1. 定义类实现Runnable接口
  2. 覆盖Runnable接口中的run()。目的:将线程要运行的代码存放在该run()
  3. 通过Thread类建立线程对象
  4. Runnable接口的子类对象作为实际参数传递给Thread类的构造函数。
    为什么要将Runnable接口的子类对象作为实际参数传递给Thread类的构造函数?
    答:因为自定义的run()所属的对象是Runnable接口的子类对象,所以要让线程去运行指定对象的run(),就必须明确该run()所属的对象。
  5. 调用Thread类的start()开启线程并调用Runnable接口子类的run方法。

实现Runnable接口的好处

现将实现Runnable接口的好处总结如下:

  1. 避免了继承Thread类的单继承的局限性。
  2. Runnable接口的出现更符合面向对象,将线程任务单独进行了对象的封装。
  3. Runnable接口的出现降低了线程任务和线程对象的耦合性。

所以,以后创建线程都使用第二种方式。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

李阿昀

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值