JAVA——多线程(上)

编程入门 行业动态 更新时间:2024-10-28 08:31:23

JAVA——<a href=https://www.elefans.com/category/jswz/34/1767532.html style=多线程(上)"/>

JAVA——多线程(上)

目录

线程,进程概念

进程,线程理解

举例理解二者关系

JVM多线程内存图

多线程并发 

多线程并发理解 

举例分析

多线程并发实现

第一种方式:编写一个类,直接继承java.lang.Thread,重写run方法

注意 

 总结格式

实现线程的第二种方式,编写一个类实现java.lang.Runnable接口。

总结格式

对比两种方式

匿名内部类实现

线程生命周期 

获取并修改线程

线程中Sleep方法

Sleep方法使用 

 Sleep面试题

终止程序的睡眠

合理的终止一个线程的执行(很重要)


线程,进程概念

在操作系统OS课程中有提到线程与进程

        进程是一个应用程序(1个进程是一个软件)。
        线程是一个进程中的执行场景/执行单元。
        一个进程可以启动多个线程。 

进程,线程理解

        对于java程序来说,当在DOS命令窗口中输入:
        java HelloWorld 回车之后。
        会先启动JVM,而JVM就是一个进程。
        JVM再启动一个主线程调用main方法。
        同时再启动一个垃圾回收线程负责看护,回收垃圾。

        最起码,现在的java程序中至少有两个线程并发,
        一个是垃圾回收线程,一个是执行main方法的主线程。 

举例理解二者关系

        阿里巴巴:进程
            马云:阿里巴巴的一个线程
            童文红:阿里巴巴的一个线程
        
        京东:进程
            强东:京东的一个线程
            妹妹:京东的一个线程
        
        进程可以看做是现实生活当中的公司。
        线程可以看做是公司当中的某个员工。

        注意:
            进程A和进程B的内存独立不共享。(阿里巴巴和京东资源不会共享的!)
                魔兽游戏是一个进程
                酷狗音乐是一个进程
                这两个进程是独立的,不共享资源。

            线程A和线程B呢?
                在java语言中:
                    线程A和线程B,堆内存和方法区内存共享。
                    但是栈内存独立,一个线程一个栈。
            
                假设启动10个线程,会有10个栈空间,每个栈和每个栈之间,
                互不干扰,各自执行各自的,这就是多线程并发。
            
            火车站,可以看做是一个进程。
            火车站中的每一个售票窗口可以看做是一个线程。
            我在窗口1购票,你可以在窗口2购票,你不需要等我,我也不需要等你。
            所以多线程并发可以提高效率。

java中之所以有多线程机制,目的就是为了提高程序的处理效率。


 思考问题:使用了多线程机制之后,main方法结束,是不是有可能程序也不会结束。
        main方法结束只是主线程结束了,主栈空了,其它的栈(线程)可能还在
        压栈弹栈。

JVM多线程内存图

 可知线程A和线程B,堆内存和方法区内存共享。但是栈内存独立,一个线程一个栈。


多线程并发 

多线程并发理解 

分析一个问题:对于单核的CPU来说,真的可以做到真正的多线程并发吗?

        对于多核的CPU电脑来说,真正的多线程并发是没问题的。
            4核CPU表示同一个时间点上,可以真正的有4个进程并发执行。 

    什么是真正的多线程并发?
            t1线程执行t1的。
            t2线程执行t2的。
            t1不会影响t2,t2也不会影响t1。这叫做真正的多线程并发。

       而单核的CPU表示只有一个大脑:
            不能够做到真正的多线程并发,但是可以做到给人一种“多线程并发”的感觉。
            对于单核的CPU来说,在某一个时间点上实际上只能处理一件事情,但是由于
            CPU的处理速度极快,多个线程之间频繁切换执行,跟人来的感觉是:多个事情
            同时在做!!!!!
                线程A:播放音乐
                线程B:运行魔兽游戏
                线程A和线程B频繁切换执行,人类会感觉音乐一直在播放,游戏一直在运行,
                给我们的感觉是同时并发的。
       

举例:
        电影院采用胶卷播放电影,一个胶卷一个胶卷播放速度达到一定程度之后,
        人类的眼睛产生了错觉,感觉是动画的。这说明人类的反应速度很慢,就像
        一根钢针扎到手上,到最终感觉到疼,这个过程是需要“很长的”时间的,在
        这个期间计算机可以进行亿万次的循环。所以计算机的执行速度很快。

类似操作系统中学习到的“时间轮转片”,给用户一定数量的时间片(很短例如30ms),并且会一直循环,用户每次分到相同大小的时间片,但由于极快的速度给用户的体验是在使用时仿佛是独享该服务器。


举例分析

分析以下程序,有几个线程,除垃圾回收线程之外。有几个线程

public class ThreadTest01 {public static void main(String[] args) {System.out.println("main begin");m1();System.out.println("main over");}private static void m1() {System.out.println("m1 begin");m2();System.out.println("m1 over");}private static void m2() {System.out.println("m2 begin");m3();System.out.println("m2 over");}private static void m3() {System.out.println("m3 execute!");}
}

答案:1个线程。(因为程序只有1个栈。)

main begin
m1 begin
m2 begin
m3 execute!
m2 over
m1 over
main over
    一个栈中,自上而下的顺序依次逐行执行!


多线程并发实现

java语言中,实现线程有两种方式,并且java支持多线程机制。并且java已经将多线程实现了,我们只需要继承就行了。

第一种方式:编写一个类,直接继承java.lang.Thread,重写run方法

void

run()
          如果该线程是使用独立的 Runnable 运行对象构造的,则调用该 Runnable 对象的 run 方法;否则,该方法不执行任何操作并返回。

怎么创建线程对象? new就行了。

class MyThread extends Thread {@Overridepublic void run() {// 编写程序,这段程序运行在分支线程中(分支栈)。for(int i = 0; i < 1000; i++){System.out.println("分支线程--->" + i);}}
}

怎么启动线程呢? 调用线程对象的start()方法。 

    public static void main(String[] args) {// 这里是main方法,这里的代码属于主线程,在主栈中运行。// 新建一个分支线程对象MyThread t = new MyThread();// 启动线程t.start();// 这里的代码还是运行在主线程中。for(int i = 0; i < 1000; i++){System.out.println("主线程--->" + i);}}

注意 

 注意!!!!

        t.run(); // 不会启动线程,不会分配新的分支栈。(这种方式就是单线程。)
         start()方法的作用是:启动一个分支线程,在JVM中开辟一个新的栈空间,这段代码任务完成之后,瞬间就结束了。
         这段代码的任务只是为了开启一个新的栈空间,只要新的栈空间开出来,start()方法就结束了。线程就启动成功了。
         启动成功的线程会自动调用run方法,并且run方法在分支栈的栈底部(压栈)。
         run方法在分支栈的栈底部,main方法在主栈的栈底部。run和main是平级的。

程序部分结果:

主线程--->831
主线程--->832
主线程--->833
主线程--->834
分支线程--->725
分支线程--->726
分支线程--->727
分支线程--->728
分支线程--->729
分支线程--->730

以上程序的输出结果有这样的特点:
    有先有后。
    有多有少。 

 总结格式

         // 定义线程类
        public class MyThread extends Thread{
            public void run(){
            
            }
        }
        // 创建线程对象
        MyThread t = new MyThread();
        // 启动线程。
        t.start();


实现线程的第二种方式,编写一个类实现java.lang.Runnable接口。

对于Thread的一个构造方法:

Thread(Runnable target)
          分配新的 Thread 对象。
// 这并不是一个线程类,是一个可运行的类。它还不是一个线程。
class MyRunnable implements Runnable {@Overridepublic void run() {for(int i = 0; i < 100; i++){System.out.println("分支线程--->" + i);}}
}
        // 创建一个可运行的对象//MyRunnable r = new MyRunnable();// 将可运行的对象封装成一个线程对象//Thread t = new Thread(r);Thread t = new Thread(new MyRunnable()); // 合并代码// 启动线程t.start();

总结格式

        // 定义一个可运行的类
        public class MyRunnable implements Runnable {
            public void run(){
            
            }
        }
        // 创建线程对象
        Thread t = new Thread(new MyRunnable());
        // 启动线程
        t.start();

对比两种方式

第二种方式实现接口比较常用,因为一个类实现了接口,它还可以去继承其它的类,更灵活。 

匿名内部类实现

        Thread t = new Thread(new Runnable(){@Overridepublic void run() {for(int i = 0; i < 100; i++){System.out.println("t线程---> " + i);}}});// 启动线程t.start();

线程生命周期 

线程生命周期有7个,这里只展示其中5个

新建:采用 new语句创建完成

就绪:执行 start 后 运行:占用 CPU 时间

阻塞:执行了 wait 语句、执行了 sleep 语句和等待某个对象锁,等待输入的场合

终止:退出 run()方法


获取并修改线程

1、怎么获取当前线程对象?
    Thread t = Thread.currentThread();
    返回值t就是当前线程。

2、获取线程对象的名字
    String name = 线程对象.getName();

3、修改线程对象的名字
    线程对象.setName("线程名字");

4、当线程没有设置名字的时候,默认的名字有什么规律?(了解一下)
    Thread-0
    Thread-1
    Thread-2
    Thread-3
    .....

对于线程名字:

        // 创建线程对象MyThread2 t = new MyThread2();// 设置线程的名字t.setName("t1");// 获取线程的名字String tName = t.getName();System.out.println(tName); //Thread-0MyThread2 t2 = new MyThread2();t2.setName("t2");System.out.println(t2.getName()); //Thread-1\t2.start();// 启动线程t.start();

对于获取线程对象:

static ThreadcurrentThread()
          返回对当前正在执行的线程对象的引用。

currentThread就是当前线程对象 

        // 这个代码出现在main方法当中,所以当前线程就是主线程。Thread currentThread = Thread.currentThread();System.out.println(currentThread.getName()); //main

线程中Sleep方法

Sleep方法使用 

static void

sleep(long millis)
          在指定的毫秒数内让当前正在执行的线程休眠(暂停执行),此操作受到系统计时器和调度程序精度和准确性的影响。

关于线程的sleep方法:
    static void sleep(long millis)
    1、静态方法:Thread.sleep(1000);
    2、参数是毫秒
    3、作用:让当前线程进入休眠,进入“阻塞状态”,放弃占有CPU时间片,让给其它线程使用。
        这行代码出现在A线程中,A线程就会进入休眠。
        这行代码出现在B线程中,B线程就会进入休眠。
    4、Thread.sleep()方法,可以做到这种效果:
        间隔特定的时间,去执行一段特定的代码,每隔多久执行一次。

        // 让当前线程进入休眠,睡眠5秒// 当前线程是主线程!!!try {Thread.sleep(1000 * 5);} catch (InterruptedException e) {e.printStackTrace();}// 5秒之后执行这里的代码System.out.println("hello world!");

 Sleep面试题

public class ThreadTest07 {public static void main(String[] args) {// 创建线程对象Thread t = new MyThread3();t.setName("t");t.start();// 调用sleep方法try {// 问题:这行代码会让线程t进入休眠状态吗?t.sleep(1000 * 5); } catch (InterruptedException e) {e.printStackTrace();}// 5秒之后这里才会执行。System.out.println("hello World!");}
}class MyThread3 extends Thread {public void run(){for(int i = 0; i < 10000; i++){System.out.println(Thread.currentThread().getName() + "--->" + i);}}
}

问题:这行代码会让线程t进入休眠状态吗?
            t.sleep(1000 * 5);

答案:不会

        在执行的时候  t.sleep(1000 * 5);还是会转换成:Thread.sleep(1000 * 5);这行代码的作用是:让当前线程进入休眠,也就是说main线程进入休眠。这样代码出现在main方法中,main线程睡眠。

终止程序的睡眠

sleep睡眠太久了,如果希望半道上醒来,你应该怎么办?也就是说怎么叫醒一个正在睡眠的线程??
注意:这个不是终断线程的执行,是终止线程的睡眠。

class MyRunnable2 implements Runnable {// 重点:run()当中的异常不能throws,只能try catch// 因为run()方法在父类中没有抛出任何异常,子类不能比父类抛出更多的异常。@Overridepublic void run() {System.out.println(Thread.currentThread().getName() + "---> begin");try {// 睡眠1年Thread.sleep(1000 * 60 * 60 * 24 * 365);} catch (InterruptedException e) {// 打印异常信息//e.printStackTrace();}//1年之后才会执行这里System.out.println(Thread.currentThread().getName() + "---> end");}

调用 interrupt() 方法终断t线程的睡眠(这种终断睡眠的方式依靠了java的异常处理机制。)

    public static void main(String[] args) {Thread t = new Thread(new MyRunnable2());t.setName("t");t.start();// 希望5秒之后,t线程醒来(5秒之后主线程手里的活儿干完了。)try {Thread.sleep(1000 * 5);} catch (InterruptedException e) {e.printStackTrace();}// 终断t线程的睡眠(这种终断睡眠的方式依靠了java的异常处理机制。)t.interrupt(); // 干扰,一盆冷水过去!}

合理的终止一个线程的执行(很重要)

 关键:打一个布尔标记

boolean run = true;

class MyRunable4 implements Runnable {// 打一个布尔标记boolean run = true;@Overridepublic void run() {for (int i = 0; i < 10; i++){if(run){System.out.println(Thread.currentThread().getName() + "--->" + i);try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}else{// return就结束了,你在结束之前还有什么没保存的。// 在这里可以保存呀。//save....//终止当前线程return;}}}
}

 模拟:

public class ThreadTest10 {public static void main(String[] args) {MyRunable4 r = new MyRunable4();Thread t = new Thread(r);t.setName("t");t.start();// 模拟5秒try {Thread.sleep(5000);} catch (InterruptedException e) {e.printStackTrace();}// 终止线程// 你想要什么时候终止t的执行,那么你把标记修改为false,就结束了。r.run = false;}
}

更多推荐

JAVA——多线程(上)

本文发布于:2024-03-13 11:00:30,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1733840.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:多线程   JAVA

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!