java多线程详解

目录

一、概述

1、进程与线程的关系

2、多线程并发

二、实现线程的两种方式

1、实现线程的第一种方式

 2、实现线程的第二种方式

采用匿名内部类方式实现线程:

 

run()和start():

三、线程生命周期

1、新建状态

2、就绪状态

3、运行状态

4、阻塞状态

5、死亡状态

四、获取线程的名字

获取线程名字:

修改线程名字:

五、获取当前线程对象

六、线程的sleep方法

 七、终止线程的休眠

八、强行终止线程的执行

第一种方法

第二种方法


一、概述

1、进程与线程的关系

进程是一个应用程序,一个进程就是一个软件;

线程是一个进程中的执行单元,一个进程可以启动多个线程;

如果把一个公司比作一个进程,公司的保洁阿姨是一个线程,公司的生产部是一个线程,公司的销售部是一个线程

2、多线程并发

假设启动了10个线程,内存中就会有10个"栈"空间,每个栈与每个栈之间互不干扰,各自执行个自的,这就是多线程并发;

真正的多线程并发:

t1线程执行t1的;

t2线程执行t2的;

t1不会影响t2,t2也不会影响t1。这就叫做多线程并发;

例如:4核CPU表示在同一个时间点上,可以真正的有四个进程并发执行

单核CPU表示只有一个大脑,不能够做到真正的多线程并发,但是可以做到给人一种多线程并发的感觉;

注意:使用多线程并发时,是有可能支线程还没结束主线程就已经结束的现象

例如:以下代码只有一个main线程

public class Test01 {
    public static void main(String[] args) {
        System.out.println("主线程开始");
        m1();
        System.out.println("主线程结束");
    }
    private static void m1() {
        System.out.println("m1开始");
        m2();
        System.out.println("m1结束");
    }
    private static void m2() {
        System.out.println("m2开始");
        m3();
        System.out.println("m2结束");
    }
    private static void m3() {
        System.out.println("m3运行");
    }
}

二、实现线程的两种方式

1、实现线程的第一种方式

编写一个类,直接继承java.lang.Thread,然后重写run()方法;

主类: 

public class Test01 {
    public static void main(String[] args) {
        //这是main方法,这里的代码属于主线程,在主栈中运行
        //创建一个分支线程对象
        MyThread myThread=new MyThread();

        //调用start()方法启动线程
        //这个语句执行瞬间线程启动后语句立马结束,然后向下执行语句
        myThread.start();

        //以下语句还是在主线程中
        for(int i=1;i<100;i++){
            System.out.println("主栈--->"+i);
        }


    }
}

支栈类:

//创建类继承Thread
public class MyThread extends Thread{
    //重写run()方法
    public void run(){
        //这里面的语句就会运行在支栈中,和main线程不相干
        for(int i=1;i<100;i++){
            System.out.println("支栈--->"+i);
        }
    }
}

运行结果是两个线程同时启动:

 2、实现线程的第二种方式

编写一个类,实现java.lang.Runnable.接口;

主类:

public class Test01 {
    public static void main(String[] args) {
        //创建一个MyRunnable可运行对象
        MyRunnable myRunnable=new MyRunnable();
        //将可运行对象封装成一个线程对象
        Thread thread=new Thread(myRunnable);
        //启动线程
        thread.start();

        //以下语句还是在主线程中
        for(int i=1;i<100;i++){
            System.out.println("主栈--->"+i);
        }


    }
}

支栈类:

//编写类实现Runnable接口
public class MyRunnable implements Runnable{
    //实现run方法
    @Override
    public void run() {
        //这里面的语句就会运行在支栈中,和main线程不相干
        for(int i=1;i<100;i++){
            System.out.println("支栈--->"+i);
        }
    }
}

运行结果交替输出:

采用匿名内部类方式实现线程:

public class Test01 {
    public static void main(String[] args) {
        //创建线程对象
       Thread t=new Thread(new Runnable() {
           @Override
           public void run() {
               //这里面的语句就会运行在支栈中,和main线程不相干
               for(int i=1;i<100;i++){
                   System.out.println("支栈--->"+i);
               }
           }
       });
       //启动线程
        t.start();

        //以下语句还是在主线程中
        for(int i=1;i<100;i++){
            System.out.println("主栈--->"+i);
        }


    }
}

匿名内部类学习入口

run()和start():

run是不需要手动调用的,由JVM线程调度机制来运作的

start()方法栈帧:开辟一个新的栈空间称为分支栈,只要开完,方法就结束;

三、线程生命周期

1、新建状态

也就是刚new出来的线程对象

2、就绪状态

就绪状态又叫做可运行状态,表示当前线程有抢夺CPU时间片的权利,当一个线程抢夺到时间片之后,就开始执行run方法。

3、运行状态

run方法执行就进入了 运行状态,当之前抢夺的时间片用完之后就会重新回到就绪状态,继续参与抢夺时间片;

4、阻塞状态

当一个线程遇到阻塞事件,或者使用sleep方法,线程就会进入阻塞状态,阻塞状态会放弃线程之前占有的时间片;

阻塞解除需要再次回到就绪状态参与抢夺时间片;

5、死亡状态

四、获取线程的名字

获取线程名字:

使用getName();方法

public class Test01 {
    public static void main(String[] args) {
        //创建线程对象
        Thread thread=new MyThread();
        //获取线程名字
        String threadName=thread.getName();
        System.out.println(threadName);//Thread-0

    }
}

修改线程名字:

使用setName()方法

public class Test01 {
    public static void main(String[] args) {
        //创建线程对象
        Thread thread=new MyThread();
        //修改线程名字
        thread.setName("zhangsan");
        //获取线程名字
        String threadName=thread.getName();
        System.out.println(threadName);//zhangsan

    }
}

五、获取当前线程对象

使用currentThread()来获取当前对象

支栈类: 

public class MyThread extends Thread{
    //重写run()方法
    public void run(){
        //这里面的语句就会运行在支栈中,和main线程不相干

        for(int i=1;i<100;i++){
            //下面的“t“就是当前线程
            //若是t1线程执行run()方法t就是t1线程
            //若是t2线程执行run()方法t就是t2线程
            Thread t=Thread.currentThread();
            System.out.println(t.getName()+"-->"+i);
        }
    }
}

主类:

public class Test01 {
    public static void main(String[] args) {
        //创建线程对象
        Thread t1=new MyThread();
        Thread t2=new MyThread();
        //修改线程名字
        t1.setName("t1");
        t2.setName("t2");
        //启动线程
        t1.start();
        t2.start();
      
    }
}

 运行结果:

六、线程的sleep方法

1、static void sleep(long millis)方法

2、参数是毫秒

3、作用,让当前线程进入休眠,就是进入阻塞状态。

例如:

public class Test01 {
    public static void main(String[] args) {
        //创建线程对象
        Thread t1=new MyThread();
        Thread t2=new MyThread();
        //修改线程名字
        t1.setName("t1");
        t2.setName("t2");
        //启动线程
        t1.start();
        //间隔5秒在往下执行
        try {
            Thread.sleep(1000*5);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        //t1启动5秒后t2才开始启动
        t2.start();

    }
}

 七、终止线程的休眠

使用interrupt()来终止

例如:

支类:

public class MyThread extends Thread{
    //重写run()方法
    public void run(){
        //休眠一个小时
        try {
            Thread.sleep(1000*60*60);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        for(int i=1;i<100;i++){
            //下面的“t“就是当前线程
            //若是t1线程执行run()方法t就是t1线程
            //若是t2线程执行run()方法t就是t2线程
            Thread t=Thread.currentThread();
            System.out.println(t.getName()+"-->"+i);
        }
    }
}

主类:

public class Test01 {
    public static void main(String[] args) {
        //创建线程对象
        Thread t1=new MyThread();

        //修改线程名字
        t1.setName("t1");

        //启动线程
        t1.start();
        //间隔5秒在往下执行
        try {
            Thread.sleep(1000*5);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        //唤醒线程
        t1.interrupt();

    }
}

八、强行终止线程的执行

第一种方法

使用stop();

public class Test01 {
    public static void main(String[] args) {
        //创建线程对象
        Thread t1=new MyThread();

        //修改线程名字
        t1.setName("t1");

        //启动线程
        t1.start();
        //间隔5秒在往下执行
        try {
            Thread.sleep(1000*5);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
       //强行终止t1线程的执行
        t1.stop();

    }
}

注意:这种方法直接将线程杀死,线程没有保存的数据会丢失 

第二种方法

例如:

主类:

public class Test01 {
    public static void main(String[] args) {
        //创建对象
        MyThread thread=new MyThread();
        //创建线程对象
        Thread t=new Thread(thread);
        //启动线程
        t.start();
        //间隔5秒在往下执行
        try {
            Thread.sleep(1000*5);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
       //强行终止t1线程的执行
        thread.run=false;
    }
}

支类:

public class MyThread implements Runnable{
    //运行标记
    boolean run=true;
    //重写run()方法
    public void run(){

        for(int i=1;i<100;i++){
            //判断,如果想要终止就在外部修改run的值为false
            if (run) {
                System.out.println("支栈-->" + i);
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }else{
                //终止,想要在终止前运行的代码可以写在此处
                System.out.println("数据已保存!");
                return;
            }
        }
    }
}

运行结果:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

张同学%

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

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

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

打赏作者

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

抵扣说明:

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

余额充值