栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

Surpass Day26——Java 多线程1

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Surpass Day26——Java 多线程1

目录

1、关于多线程

1.1 什么是进程?什么是线程?

1.2 关于java程序的多线程

1.3 进程和线程的关系

1.4 关于多线程机制

1.5 关于单核的CPU

2、如何实现多线程

2.1 继承Thread

2.2 实现Runnable接口

2.3 匿名内部类

2.4 关于线程对象的生命周期

3、对线程的操作

3.1 怎么获取当前线程对象?

3.2 获取线程对象的名字

3.3 修改线程对象的名字

3.4 默认的名字的规律

3.5 关于线程的sleep方法

3.7 终止线程的睡眠

3.8 强行终止程序

3.9 *关于线程的调度

3.9.1 常见的线程调度模型有哪些?

3.9.2 java中提供了哪些方法是和线程调度有关系的呢?


1、关于多线程

1.1 什么是进程?什么是线程?

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

1.2 关于java程序的多线程

当在DOS命令窗口中输入:java HelloWorld 回车之后。会先启动JVM,而JVM就是一个进程。JVM再启动一个主线程调用main方法。同时再启动一个垃圾回收线程负责看护,回收垃圾。最起码,现在的java程序中至少有两个线程并发,一个是垃圾回收线程,一个是执行main方法的主线程。

1.3 进程和线程的关系

阿里巴巴:进程 马云:阿里巴巴的一个线程 童文红:阿里巴巴的一个线程

京东:进程 强东:京东的一个线程 妹妹:京东的一个线程

进程可以看做是现实生活当中的公司。 线程可以看做是公司当中的某个员工。

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

线程A和线程B呢? 在java语言中: 线程A和线程B,堆内存和方法区内存共享。 但是栈内存独立,一个线程一个栈。 假设启动10个线程,会有10个栈空间,每个栈和每个栈之间, 互不干扰,各自执行各自的,这就是多线程并发。

火车站,可以看做是一个进程。 火车站中的每一个售票窗口可以看做是一个线程。 我在窗口1购票,你可以在窗口2购票,你不需要等我,我也不需要等你。 所以多线程并发可以提高效率。 java中之所以有多线程机制,目的就是为了提高程序的处理效率。

1.4 关于多线程机制

使用了多线程机制之后,main方法结束,是不是有可能程序也不会结束。

main方法结束只是主线程结束了,主栈空了,其它的栈(线程)可能还在压栈弹栈。

1.5 关于单核的CPU

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

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

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

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

2、如何实现多线程
public class ThreadTest02 {
    public static void main(String[] args){
        //这里是main方法,这里的代码属于主线程,在主栈中运行。
        //新建一个分支线程对象
        MyThread myThread = new MyThread();
        //启动线程
        //myThread.run();//不会启动线程,不会分配新的分支栈。
​
        //start()方法的作用是:启动一个分支线程,在JVM中开辟一个新的栈空间,这段代码任务完成之后,瞬间就结束了。
        //这段代码的任务只是为了开启一个新的栈空间,只要新的栈空间开出来,start()方法就结束了。线程就启动成功了。
        //启动成功的线程会自动调用run方法,并且run方法在分支栈的栈底部(压栈)。
        // run方法在分支栈的栈底部,main方法在主栈的栈底部。run和main是平级的。
        myThread.start();
        //这里的代码还是运行在主线程中。
        for(int i = 0; i < 1000;i++){
            System.out.println("主线程--->"+i);
        }
        //java代码逐行执行
    }
}
class MyThread extends Thread {
    @0verride
    public void run(){
    //编写程序,这段程序运行在分支线程中(分支栈)。
        for(int i = 0;i < 1000;i++){
            System.out.println("分支线程--->"+i);
        }
    }
}

手动调用run方法

并没有启动线程,不会分配新的分支栈,还是在主线程中进行的

 

JVM调用的run方法

 

通过start方法启动线程,然后分配新的分支栈,jvm自动调用run方法

2.1 继承Thread

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

//java支持多线程机制。并且java已经将多线程实现了,我们只需要继承就行了。
//
//定义线程类
public class MyThread extends Thread{
    public void run(){
        
    }
}
    //创建线程对象
    MyThread t = new MyThread() ;
    //启动线程。
    t.start();

2.2 实现Runnable接口

第二种方式:编写一个类,实现java.lang.Runnable接口,实现run方法。

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

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

2.3 匿名内部类
public class ThreadTest04{
    public static void main(String[] args){
        //创建线程对象,采用匿名内部类方式。
        //这是通过一个没有名字的类,new出来的对象。
        Thread t = new Thread(new Runnable(){
            @Override
            public void run(){
                for(int i = 0; i < 100; i++){
                    System.out.println("t线程--->"+i);
                }
            }
        });
        //启动线程
        t.start();
        for(int i =0; i < 100; i++){
            System.out.println("main线程--->"+i);
        }
    }
}

2.4 关于线程对象的生命周期

新建状态、就绪状态、运行状态、阻塞状态、死亡状态

 

3、对线程的操作

3.1 怎么获取当前线程对象?

Thread t = Thread.currentThread();

可以使用this/super.的形式进行访问,但有一定的局限性,必须在类当中

返回值t就是当前线程。

3.2 获取线程对象的名字

String name = 线程对象:getNane();

3.3 修改线程对象的名字

线程对象.setName(“线程名字”);

3.4 默认的名字的规律

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

3.5 关于线程的sleep方法

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

public class ThreadTest06 {
    public static void main(String[] args){
        //让当前线程进入休眠,睡眠5秒
        //当前线程是主线程!!!
        
        //5秒之后执行这里的代码
        //System.out.println("hello world!");
        for(int i = 0; i < 10; i++){
            System.out.println(Thread.currentThread().getName() + "--->" + i);
            try {
                Thread.sleep( millis: 1000);
            } catch (InterruptedException e){
                e.printStackTrace();
            }
        }
    }
}

关于sleep的面试题

public class ThreadTest07 {
    public static void main(String[] args){
        //创建线程对象
        Thread t = new MyThread3();
        t.setName("t");
        t.start();
        //调用sleep方法
        try {
        //问题:这行代码会让线程t进入休眠状态吗?
            t.sleep(millis:1000*5);//在执行的时候还是会转换成:Thread.sleep(1000*5);
        //这行代码的作用是:让当前线程进入休眠,也就是说main线程进入休眠。
        //这样代码出现在main方法中,main线程睡眠。|
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        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);
        }
    }
}

3.7 终止线程的睡眠
public class ThreadTest08 {
    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();//开扰,一盆冷泼过去!
    }
}
​
class MyRunnable2 implements Runnable {
    //重点:run()当中的异常不能throws,只能try catch
    //因为run()方法在父类中没有抛出任何异常,子类不能比父类抛出更多的异常。
    @Override
    public void run(){
        System.out.println(Thread.currentThread().getName() + "---> begin");
        try {
            Thread.sleep( millis: 1000 * 60 * 60 * 24 * 365);//睡眠1年
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + "---> end");
    }
}

3.8 强行终止程序
public class ThreadTest08 {
    public static void main(String[] args){
        Thread t = new Thread(new MyRunnable2());
        t.setName("t");
        t.start();
        try{
            Thread.sleep(1000 * 5);
        } catch (InterruptedException e){
            e.printStackTrace();
        }
        
        
        t.stop();//强行终止程序
        
        
    }
}
​
class MyRunnable2 implements Runnable {
    @Override
    public void run(){
        System.out.println(Thread.currentThread().getName() + "---> begin");
        try {
            Thread.sleep( millis: 1000 * 60 * 60 * 24 * 365);//睡眠1年
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + "---> end");
    }
}

在java中怎么强行终止一个线程的执行。 这种方式存在很大的缺点:容易丢失数据。因为这种方式是直接将线程杀死了, 线程没有保存的数据将会丢失。不建议使用。

怎么合理的终止一个线程的执行?(打一个布尔标记)

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( millis: 5000);
        } catch (InterruptedException e){
            e.printStackTrace();
        }
        //终止线程
        //你想要什么时候终止t的执行,那么你把标记修改为false,就结束了。
        r.pun = false;
    }
}
​
class MyRunable4 implements Runnable {
    //打一个布尔标记
    boolean run = true;
    @Override
    public void run(){
        for (int i = 0; i < 10; i++){
            if(run){
                System.out.println(Thread.currentThread().getName() + "--->" + i);
                try {
                    Thread.sleep( millis: 1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }else{
                //终止当前线程
                return;
            }
        }
    }
}

3.9 *关于线程的调度

3.9.1 常见的线程调度模型有哪些?

抢占式调度模型: 那个线程的优先级比较高,抢到的CPU时间片的概率就高一些/多一些。 java采用的就是抢占式调度模型。

均分式调度模型: 平均分配CPU时间片。每个线程占有的CpU时间片时间长度一样。 平均分配,一切平等。 有一些编程语言,线程调度模型采用的是这种方式。

3.9.2 java中提供了哪些方法是和线程调度有关系的呢?

实例方法: void setPriority(int newPriority)设置线程的优先级 int getPriority()获取线程优先级 最低优先级1 默认优先级是5 最高优先级10 优先级比较高的获取CPU时间片可能会多一些。(但也不完全是,大概率是多的。)

静态方法: static void yield() 让位方法 暂停当前正在执行的线程对象,并执行其他线程 yield()方法不是阻塞方法。让当前线程让位,让给其它线程使用。 yield()方法的执行会让当前线程从“运行状态"回到“就绪状态”。 注意:在回到就绪之后,有可能还会再次抢到。

实例方法: void join()合并线程

class MyThreadl extends Thread {
    public void dosome(){
        MyThread2 t = new MyThread2 () ;
        t.join();//当前线程进入阻塞,t线程执行,直到t线程结束。当前线程才可以执行
    }
}
class MyThread2 extends Thread{
    
}

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/737644.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号