Java中多线程

一、概述

1、多线程和单线程

多线程即一个程序中有多个线程在同时执行,单线程和多线程:

单线程:若有多个任务,只有当上一个任务执行结束后,下一个任务才开始执行

多线程:若有多个任务,可以同时执行

2、程序执行原理

在操作系统中,有很多种调度方式,这里介绍分时调度和抢占式调度,在Java中使用的是抢占式调度,所以主要介绍抢占式调度方式

分时调度:所有线程轮流使用 CPU 的使用权,平均分配每个线程占用 CPU 的时间

抢占式调度:每个线程都有其优先级,优先让优先级高的进程使用 CPU,如果线程优先级相同,则会随机选择去执行

(1) CPU 使用抢占式调度模式在多个线程间进行着高速的切换

(2) 对于 CPU 的一个核而言,某个时刻只能执行一个线程,而 CPU 在多个线程间切换速度相对我们的感觉要快,看上去就是在同一时刻执行

(3) 多线程程序并不能提高程序的运行速度,但能提高程序运行效率,让 CPU 的使用率更高

3、主线程

Java程序在执行过程中,先启动 JVM,并加载对应的 class 文件,JVM 并会从 main 方法开始执行我们的程序代码,一直把 main 方法的代码执行结束。在 JVM 启动后,必然有一个执行路径(线程)从 main 方法开始,一直执行到 main 方法结束,这个线程在 Java 中称之为主线程。当程序的主线程执行时,如果遇到了循环而导致程序在指定位置停留时间过长,则无法马上执行下面的程序,需要等待循环结束后才能够执行,那么,能否实现一个主线程负责执行其中一个循环,再由另一个线程负责其他代码的执行,最终实现多部分代码同时执行的效果呢?办法总比困难多,多线程便孕育而生,很好的解决了这个问题!

二、线程的创建

创建新执行线程有两种方法

方法一:

类声明为 Thread 的子类,该子类应重写 Thread 类的 run 方法,然后创建对象,开启线程。run 方法相当于其他线程的 main 方法


方法二:

声明一个实现 Runnable 接口的类,该类然后实现 run 方法,然后创建Runnable的子类对象,传入到某个线程的构造方法中,开启线程

1、继承 Thread 类创建线程(方法一)

(1) 创建步骤及分析

定义一个类继承 Thread 类

重写 run 方法

创建子类对象,即创建线程对象

调用 star 方法,开启线程并让线程执行,同时还会告诉 JVM 去调用 run 方法

自定义线程类:

public class myThread extends Thread {

    //重写run方法,完成该线程执行的逻辑

    public void run()

    {

        for(int i = 0;i < 50;i++)

        {

            System.out.println("新线程正在执行" + i);

        }

    }

}

在主线程中调用:

public static void main(String[] args)

{

    //创建自定义线程对象

    myThread mT = new myThread();

    //开启新线程,让新的线程执行程序,jvm调用线程中的run

    mT.start();

    //在main方法中执行

    for(int i = 0;i < 50;i++)

    {

        System.out.println("main线程正在执行" + i);

    }

}

分析:

在主线程中创建自定义线程对象,调用star方法开启新线程,让新的线程执行程序,jvm再调用线程中的run方法

创建新的线程后,会产生两个执行路径,都会被CPU执行,CPU有自己的选择执行权力,所以会出现随机的执行结果

可以理解为两个线程在抢夺CPU的资源(时间)

注:线程对象调用 run 方法和调用 star 方法的区别:

(1) 线程对象调用 run 方法不开启线程,仅仅是对象调用方法

(2) 线程对象调用 star 方法开启线程,并让 JVM 调用 run 方法在开启的线程中执行

问题分析:

为什么要继承 Thread 类,并调用 star 方法才能开启线程,为什么不像下面代码一样直接创建 Thread 类的对象呢?

Thread t1 = new Thread();

t1.star();

创建线程的目的是为了建立程序单独的执行路径,让多部分代码实现同时执行,而以上代码,直接创建 Thread 类的对象,虽然没有语法错误,不会报错,但该 star 调用的是Thread 类中的 run 方法,而这个方法什么也没做,更重要的是这个 run 方法中并没有定义我们需要让线程执行的代码

Thread 类 run 方法中的任务并不是我们所需要的,只有重写这个 run 方法,Thread 类已经定义了线程任务的编写位置(run 方法),那么只要在 run 方法中定义任务代码即可,即重写 run 方法

(2) 多线程内存理解

多线程在执行的时候,是在栈内存中的,每一个执行线程都有一片自己的所属栈内存空间,进行方法的压栈和出栈

当执行线程的任务结束了,线程自动在栈内存中释放,当所有的执行线程都结束了,进程也就结束了

(3) 获取和设置线程名称

String getName():返回该线程的名称(同 Thread.currentThread().getName())

Thread.currentThread():获取当前线程对象

Thread.currentThread().getName():获取当前线程对象的名称

setName():设置线程名称

public static void main(String[] args)

{

    myThread mT = new myThread();

    mT.start();

    mT.setName("oneStar");    //设置线程名称

    //System.out.println(getName());  //使用getName()方法获取线程名称 在新线程中可以这样使用

    System.out.println(Thread.currentThread().getName());  //使用Thread.currentThread().getName()获取线程名称

    System.out.println(Thread.currentThread()); //获取当前线程对象

}

(4) Thread 类 sleep 方法

sleep 方法是一个静态的方法,可以通过 Thread 类直接调用,会有异常抛出

public static void main(String[] args) {

    myThread mT = new myThread();

    mT.start();

    //使用sleep方法延时打印for循环

    for(int i = 0;i < 5;i++)

    {

        try {

            Thread.sleep(1000);

        } catch (InterruptedException e) {

            e.printStackTrace();

        }

        System.out.println(i);

    }

}

2、实现 Runnable 接口创建线程(方法二)

Runnable 接口用来指定每一个线程要执行的任务,包含了一个 run 的无参数抽象方法,需要由接口实现重写该方法。此创建线程的方法是声明实现 Runnable 接口的类,该类实现 run 方法,然后创建 Runnable 的子类对象,传入到某个线程的构造方法中,开启线程

(1) 创建步骤:

定义类实现 Runnable 接口

重写接口中的 run 方法

创建 Thread 类的方法

将 Runnable 接口的子类对象作为参数传递给 Thread 类的构造函数

调用 Thread 类的 star 方法开启线程

定义实现类接口

//定义实现类接口

public class myRunnable implements Runnable {

    //重写run方法

    public void run()

    {

        for(int i = 0;i < 5;i++)

        {

            System.out.println("myRunnable线程正在执行!");

        }

    }

}

在主线程中调用

public static void main(String[] args)

{

    //创建线程执行目标类对象

    myRunnable mR = new myRunnable();

    //将Runnable接口的子类对象作为参数传递给Thread类的构造函数

    Thread t1 = new Thread(mR);

    Thread t2 = new Thread(mR);

    //开启线程

    t1.start();

    t2.start();

    for(int i = 0;i < 5;i++)

    {

        System.out.println("main线程正在执行!");

    }

}

(2) 实现 Runnable 接口的好处

避免了单继承的局限性,所以此方法较为常用

将线程分为两部分,一部分线程对象,一部分线程任务,更加符合面向对象思想(继承 Thread 类线程对象和线程任务耦合在一起)

将线程任务单独分离出来封装成对象,类型就是 Runnable 接口类型

Runnable 接口对线程对象和线程任务进行解耦,降低紧密性或依赖性,创建线程和执行任务不绑定

三、线程的匿名内部类

使用线程的匿名内部类方式,可以方便的实现每个线程执行不同线程任务操作

方法一:重写 Thread 类中的方法创建线程

new Thread() {

    public void run() {

        for (int x = 0; x < 40; x++) {

            System.out.println(Thread.currentThread().getName()

                    + "...X...." + x);

        }

    }

}.start();

方法二:使用匿名内部类的方式实现 Runnable 接口,重写 Runnable 接口中的 run 方法

Runnable r = new Runnable() {

    public void run() {

        for (int x = 0; x < 40; x++) {

            System.out.println(Thread.currentThread().getName()

                    + "...Y...." + x);

        }

    }

};

new Thread(r).start();

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,546评论 6 507
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,224评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,911评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,737评论 1 294
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,753评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,598评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,338评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,249评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,696评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,888评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,013评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,731评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,348评论 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,929评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,048评论 1 270
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,203评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,960评论 2 355