多线程—Thread类的用法


Java这个生态中最常用的并发编程方式是多线程。

什么是Thread类

在java标准库中,就提供了一个Thread类,来表示/操作线程。Thread类也可以视为Java标准库提供的API,创建好的Thread实例,其实和操作系统中的线程是一一对应的关系,操作系统,提供了一组关于线程的API,Java对于这组API进一步封装,就成了Thread类。

Thread类的基本用法

线程创建

最基本的创建方法

  • 方法一
lass MyThread extends Thread{
    @Override
    public void run() {
        System.out.println("hello thread");
    }
}

//最基本的创建线程的方法
public class Demo1 {
    public static void main(String[] args) {
        Thread t=new MyThread();
        t.start();

    }
}

此方法是最简单的方法,创建子类继承自Thread,并且重写run方法。
在这里插入图片描述

  • 方法二(体现线程的并发执行)
class MyThread2 extends Thread{
    @Override
    public void run() {
        while(true){
            System.out.println("hello thread");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
public class Demo2 {
    public static void main(String[] args) {
        Thread  t=new MyThread2();
        t.start();
        while(true){
            System.out.println("hello main");
            try{
                Thread.sleep(1000);
            }catch(InterruptedException e){
                e.printStackTrace();
            }

        }
    }
}

在这里插入图片描述

实现runnable接口

class MyRunnable implements  Runnable{
    @Override
    public void run() {
        System.out.println("hello");
    }
}

public class Demo3 {
    public static void main(String[] args) {
        Thread t=new Thread(new MyRunnable());
        t.start();
    }
}

通过Runnable来描述任务的内容,进一步把描述好的任务交给Thread实例

使用匿名内部类

public class Demo4 {
    public static void main(String[] args) {
    //创建了一个匿名内部类,继承自Thread类,同时重写run方法,同时在new出这个匿名内部类的实例
        Thread t=new Thread(){
            @Override
            public void run() {
                System.out.println("hello thread");
            }
        };
        t.start();
    }
}
public class Demo5 {
    public static void main(String[] args) {
    //new 的Runnable,针对这个创建的匿名内部类,同时new出的Runnable实例传给Thread的构造方法
       Thread t=new Thread(new Runnable() {
           @Override
           public void run() {
               System.out.println("hello thread");
           }
       });
       t.start();
    }
}

lambda表达式

public class Demo6 {
    public static void main(String[] args) {
        //使用lambda代替了Runnable而已
        Thread t=new Thread(() -> {
            System.out.println("hello thread");
        });
        t.start();
    }
}

多线程能够提高任务完成的效率
有两个整数变量,分别对这两变量自增10亿次。
分别使用1个线程和2个线程

//多线程能够提高任务完成的效率
public class Demo7 {
    private static final long count=10_0000_0000;
    public static void serial(){
        long beg=System.currentTimeMillis();
        long a=0;
        for(long i=0;i<count;i++){
            a++;
        }
        long b=0;
        for(long i=0;i<count;i++){
            b++;
        }
        long end=System.currentTimeMillis();
        System.out.println("消耗时间"+(end-beg)+"ms");
    }
    public static void concurrent() throws InterruptedException {
        long beg=System.currentTimeMillis();
        Thread t1=new Thread(()->{
            long a=0;
            for(long i=0;i<count;i++){
                a++;
            }
        });
        t1.start();
        Thread t2=new Thread(()->{
            long b=0;
            for(long i=0;i<count;i++){
                b++;
            }
        });
        t2.start();
        //此处不能直接记录结束时间,现在这个求结束时间的代码是在main线程中
        //main和t1  t2是并发执行的关系,此处 t1   t2还没执行完,这里就开始记录结束时间,显然不准确
        //正确的做法是让main等待 t1  t2跑完了,在来记录结束时间
        //join的效果就是等待线程结束,t1.join就是让main等待t1结束,t2.join就是让main等待t2结束
        t1.join();
        t2.join();
        long end=System.currentTimeMillis();
        System.out.println("消耗时间:"+(end-beg)+"ms");
    }

    public static void main(String[] args) throws InterruptedException {
        serial();
        concurrent();
    }
}

多线程不是万能的,并不是上了多线程,速度一定能提高。还得看具体的场景。
多线程特别适合那种CPU密集型的程序,程序要进行大量的计算,使用多线程可以更充分的利用CPU的多核资源。

Thread中的一些重要方法

启动一个线程

start决定了系统中是不是真的创建出线程。

t.run单纯的只是一个普通的方法,描述了任务的内容
t.start则是一个特殊的方法,内部会在系统中创建线程

public static void main(String[] args) {
        Thread t=new Thread(() -> {
            while(true){
                System.out.println("hello thread");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        t.start();

        while(true){
            System.out.println("hello main");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

    }

在这里插入图片描述
如果把t.start 换成t.run 效果是不同的

public static void main(String[] args) {
        Thread t=new Thread(() -> {
            while(true){
                System.out.println("hello thread");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        t.run();

        while(true){
            System.out.println("hello main");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

    }

在这里插入图片描述
start和run的区别

 public static void main(String[] args) {
        Thread t=new Thread(() -> {
            for(int i=0;i<3;i++){
                System.out.println("hello thread");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        t.run();

        while(true){
            System.out.println("hello main");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

    }

执行效果
在这里插入图片描述

中断线程

手动的设置一个标志位

设置一个标志位(自己创建的,Boolean),来控制线程是否要执行结束。

 private static boolean isQuite=false;
    public static void main(String[] args) {
        Thread t=new Thread(() ->{
            while(!isQuite){
                System.out.println("hello thread");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        t.start();

    //只要把这个isQuite设为true,此时这个循环就退出了
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        isQuite=true;
        System.out.println("终止t进程");
    }

执行效果
在这里插入图片描述
此处因为多个线程共用一个虚拟地址空间,因此main线程修改的isQuite和t线程判定的isQuite是同一个值

使用Thread中内置的一个标志位进行判断

在这里插入图片描述

public static void main(String[] args) {
        Thread  t=new Thread(() ->{
            while(!Thread.currentThread().isInterrupted()){
                System.out.println("hello thread");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

        }) ;
        t.start();

        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        //在主线程中,调用interrupt方法,来中断这个线程
        //t.interrupt的意思就是让t线程被中断
        t.interrupt();

    }

在这里插入图片描述
当下的代码,一旦触发了异常之后,就进入catch语句,在catch中,就只是打了一个日志。printStackTrace是打印当前异常位置的代码调用栈,打完日志之后,就直接继续执行。

那么,上述的情况该如何解决呢?
加一个break即可。

public static void main(String[] args) {
        Thread  t=new Thread(() ->{
            while(!Thread.currentThread().isInterrupted()){
                System.out.println("hello thread");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                    //当触发异常之后,立即退出循环
                    break;
                }
            }

        }) ;
        t.start();

        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        //在主线程中,调用interrupt方法,来中断这个线程
        //t.interrupt的意思就是让t线程被中断
        t.interrupt();

    }

执行效果
在这里插入图片描述

线程等待

多个线程之间,调度顺序是不确定的。
线程之间的执行是按照调度器来安排的,这个 过程可以视为是“无序,随机”,但是这样不太好,有些时候,我们需要能够控制线程之间的顺序。
线程等待,就是其中一种控制线程执行顺序的手段。
此处的线程等待,主要是控制线程结束的先后顺序。

public static void main(String[] args) {
        Thread t=new Thread(() -> {
           for(int i=0;i<5;i++){
               System.out.println("hello thread");
               try {
                   Thread.sleep(1000);
               } catch (InterruptedException e) {
                   e.printStackTrace();
               }
           }
        });
        t.start();

        //在主线程中就可以使用一个等待操作,来等待t线程执行结束
        try {
            t.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
       
    }

在这里插入图片描述

join操作默认情况下,是“死等”,这是不合理的。
因此join提供了另外一个版本,就是可以执行等待时间,最长等多久,如果等不到,就不等了。

public static void main(String[] args) {
        Thread t=new Thread(() -> {
           for(int i=0;i<5;i++){
               System.out.println("hello thread");
               try {
                   Thread.sleep(1000);
               } catch (InterruptedException e) {
                   e.printStackTrace();
               }
           }
        });
        t.start();

        //在主线程中就可以使用一个等待操作,来等待t线程执行结束
        try {
            t.join(10000);//进入join也会产生阻塞,这个阻塞不会一直持续下去
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }

获取当前线程引用

public static void main(String[] args) {
       Thread t=new Thread(){
           @Override
           public void run() {
               System.out.println(Thread.currentThread().getName());
           }
       };
       t.start();
        
        
         //这个操作是在main线程之后调用的,因此拿到的就是main这个线程的实例
        System.out.println(Thread.currentThread().getName());
    }
}

另一种方法

 public static void main(String[] args) {
       Thread t=new Thread(){
           @Override
           public void run() {
               //System.out.println(Thread.currentThread().getName());
               System.out.println(this.getName());
               
           }
       };
       t.start();


         //这个操作是在main线程之后调用的,因此拿到的就是main这个线程的实例
        System.out.println(Thread.currentThread().getName());
    }

在这里插入图片描述

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值