Java多线程概述与实现

1. 进程与线程

1.1 进程与线程定义

  • 进程:一个程序的执行周期;

  • 线程:一个程序执行多个任务,每一个任务就叫做一个线程;

  • 两者区别

  1. 开销:进程:当打开一个应用程序,比如打开多个IDEA,每次打开都需要消耗内存和耗费时间;线程:打开一个应用程序,线程是在这个进程中消耗资源,所以开销较小;
  2. 数据共享:线程是在一个进程中创建的,所以线程之间可以共享数据,通信更加方便、有效;进程之间进行通信需要通过网络中的TCP协议;
  • 多线程的应用:比如打开一个浏览器,你可以边下载东西,边浏览网页;
  • 高并发(多个线程):一个系统有大量用户访问;
  • 高并发带来的问题:服务器内存不够用,程序资源竞争,无法处理新的请求。

1.2 线程状态

在这里插入图片描述
(1)当创建一个线程,不会立即执行,因为它要获得CPU的时间片,这要依靠操作系统的系统调度;
(2)比如:当创建一个线程,先进入就绪状态,若CPU时间片为1微妙,这时候线程没有执行完,则由运行状态转换为就绪状态继续等待系统调度;
(3)当系统缺少某个资源时,比如存在输入输出时,线程则由运行状态变为阻塞状态,阻塞解除变为就绪状态,等待CPU分配时间片。

2. Java多线程的实现

2.1 直接继承Thread类启动线程

java.lang.Thread是一个线程操作的核心类。新建一个线程最简单的方法就是直接继承Thread类,然后覆写该类中的run()方法。

public class MyThread extends Thread{

    private String message;
    public MyThread(String message){
        this.message = message;
    }

    @Override
    public void run() {					//覆写Thread中的run()方法
        System.out.println(message);
    }

    public static void main(String[] args) {
        MyThread myThreadA = new MyThread("线程A");
        MyThread myThreadB = new MyThread("线程B");
        MyThread myThreadC = new MyThread("线程C");

        myThreadA.start();				//调用run()方法
        myThreadB.start();
        myThreadC.start();
    }

}
/*
线程A
线程C
线程B
*/

== 为什么要通过start()方法来调用run()方法,而不是run()直接执行?==
通过查看start源码分析:
在这里插入图片描述

  1. start()方法中先调用了start0()方法,而这个方法是一个只声明未实现的方法,并且使用native关键字进行定义;native指的是调用本机的原生系统函数。
	private native void start0();
  1. Thread类有个registerNatives 本地方法,该方法主要的作用就是注册一些本地方法供Thread使用,如start0(),stop()等。这个方法放在一个static语句块中,当该类被加载到JVM中的时候,它就会被调用,进而注册相应的本地方法。
    所以在Java线程调用run()方法,实际上会调用JVM_StartThread方法
  2. 因此,通过以上,可知Java线程创建流程如下:
    在这里插入图片描述

2.2 Runnable()接口实现多线程

Runnable接口中有一个抽象方法run(),Thread类中的run()方法就是实现的Runnable接口。Thread类提供了构造方法可以接受Runnable接口对象。
在这里插入图片描述
举例:使用Runnable方法实现多线程

public class MyRunnable implements Runnable{
    @Override
    public void run() {
        System.out.println("自定义的Runnable接口实现类:"+LocalDateTime.now());
    }

    public static void main(String[] args) {
        Runnable runnable = new MyRunnable();
        Thread threadA = new Thread(runnable);
        threadA.start();

        Thread threadB = new Thread(runnable);
        threadB.start();
    }
}
/**
自定义的Runnable接口实现类:2019-03-25T22:31:15.476
自定义的Runnable接口实现类:2019-03-25T22:31:15.476
*/

通常情况下,对于Runnable接口对象可以采用匿名内部类或者Lambda表达式来定义
举例:使用匿名内部类和Lambda表达式定义

public class MyRunnable implements Runnable{
    @Override
    public void run() {
        System.out.println("自定义的Runnable接口实现类:"+LocalDateTime.now());
    }

    public static void main(String[] args) {
    Thread thread1 = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("Thread1");
            }
        });
        thread1.start();

        Thread thread2 = new Thread(()-> System.out.println("Thread2"));
        thread2.start();
    }
}
/**
Thread1
Thread2
*/

2.3 Thread与Runnable区别

  1. 使用Thread类 :只能单继承
  2. 使用Runnable,可以解决单继承的缺陷,因为Runnable是一个接口,所以可以实现数据共享

举例:利用Thread实现一个买票

public class MyTickThread extends Thread {

    private int tick = 10;

    @Override
    public void run() {
        while (this.tick > 0){
            this.tick--;
            System.out.println("剩余票数:"+this.tick);
        }
    }

    public static void main(String[] args) {
        /*
         * 每实例化一个对象,调用本类方法都是独立的,不能实现数据共享
         */
        new MyTickThread().start();
        new MyTickThread().start();
        new MyTickThread().start();
    }
}

举例:利用Runnable实现买票的例子

public class MyTickRunnable implements Runnable{
    private int tick = 10;

    @Override
    public synchronized void run() {
        while(this.tick > 0){
            this.tick--;
            System.out.println("剩余票的数量:"+this.tick);
        }
    }

    /**
     * 因为target是Runnable类型,每次访问的是同一个类中的方法,实现数据共享
     */
    public static void main(String[] args) {
        Runnable target = new MyTickRunnable();
        new Thread(target).start();
        new Thread(target).start();
        new Thread(target).start();

    }
}

猜你喜欢

转载自blog.csdn.net/mi_zhi_lu/article/details/88784902
今日推荐