Thread 类及常见方法

1.Thread 类及常见方法

1.1线程创建方式

1.继承 Thread, 重写 run

/**
 * 继承Thread创建线程
 */
public class ThreadDemo1 {
    public static void main(String[] args) {
        //获得当前线程
        Thread mainthread=Thread.currentThread();
        System.out.println("主线程名称: "+mainthread.getName());
        Thread thread=new MyThread();
        //开启线程
        thread.start();
    }
}
class MyThread extends Thread{
    @Override
    public void run(){
        //具体业务执行代码
        Thread thread=Thread.currentThread();
        System.out.println("线程的名称:"+thread.getName());
    }
}

缺点:java是单继承,如果继承了Thread之后,就不能继承其他类
2.实现 Runnable, 重写 run

public class ThreadDemo2 {
    public static void main(String[] args) {
        //创建Runnable
        MyThread2 myThread2=new MyThread2();
        //创建一个线程
        Thread thread=new Thread(myThread2);
        //启动线程
        thread.start();
    }
}
class MyThread2 implements Runnable{

    @Overridepublic void run() {
        Thread thread=Thread.currentThread();
        System.out.println("线程名称:"+thread.getName());
    }
}

3.继承 Thread, 重写 run, 使用匿名内部类

public class ThreadDemo5 {public static void main(String[] args) {
        Thread thread=new Thread(){

            @Override
            public void run(){
                Thread t=Thread.currentThread();
                System.out.println("任务执行:"+t.getName());
            }
        };
        thread.start();
    }
}

4.实现 Runnable, 重写 run, 使用匿名内部类

public class ThreadDemo3 {public static void main(String[] args) {

        Thread thread=new Thread(new  Runnable(){

            @Override
            public void run() {
                //业务代码
                Thread t=Thread.currentThread();
                System.out.println("执行任务:"+t.getName());
            }
        });
        thread.start();
    }
}

5.使用 lambda 表达式(推荐使用)

public class ThreadDemo4 {public static void main(String[] args) {
        Thread thread=new Thread(() -> {
            //具体业务
           Thread t = Thread.currentThread();
            System.out.println("任务执行:"+t.getName());
        });
        thread.start();
    }
}

以上5种创建线程的方法有一个共同问题,那就是没有返回值,也就是当线程执行完之后,主线程没有办法拿到新线程的执行结果的
6.Callable+Future

import java.util.Random;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

/**
 * 实现Callable新建线程
 */
public class ThreadDemo7 {
    // 测试方法
    public static void main(String[] args) throws ExecutionException,
            InterruptedException {
// 创建 Callable 子对象
        MyCallable myCallable=new MyCallable();
// 使用 FutureTask 配合 Callable 子对象得到执行结果
        FutureTask<Integer> futureTask = new FutureTask<>(myCallable);
// 创建线程
        Thread thread = new Thread(futureTask);
// 启动线程
        thread.start();
// 得到线程执行的结果
        int result = futureTask.get();
        System.out.println("主线程中拿到子线程执行结果:" + result);
    }
}

/**
 * Callable<V>泛型里面可以是任意数据类型
 */
class MyCallable implements Callable<Integer>{
    public Integer call() throws Exception {
        //随机生成0-9
        int randomNum=new Random().nextInt(10);
        System.out.println(Thread.currentThread().getName()+"随机数:"+randomNum);
        return randomNum;
    }
}

7.匿名 Callable

import java.util.Random;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

public class ThreadDemo8 {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        FutureTask<String> futureTask=new FutureTask<>(new Callable<String>() {
            @Override
            public String call() throws Exception {
                String[] arrs=new String[]{"JAVA","mysql","Thread"};
                //随机返回一个字符串
                String result=arrs[new Random().nextInt(3)];
                System.out.println(Thread.currentThread().getName()+"--字符串:"+result);
                return result;
            }
        });
        //创建新线程
        Thread thread=new Thread(futureTask);
        //启动线程
        thread.start();
        String result=futureTask.get();
    }
}

小结:

创建线程有 3 大类实现方式、7 种实现方法,如果是 JDK 1.8 以上版本,在不需要获得线程执行结果的情况下,推荐使用 Lambda 方式来创建线程,因为它的写法足够简洁;如果想要获取线程执行结果,可使用 FutureTask + Callable 的方式来实现。

1.2 Thread 的常见构造方法

在这里插入图片描述
创建线程并命名

public class ThreadDemo9 {
    public static void main(String[] args) {
        //创建线程并设置线程名
        Thread t=new Thread("线程1"){
            @Override
            public void run() {
                //休眠线程
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        };
        //启动线程
        t.start();
    }
}

使用 Runnable 对象创建线程对象,并命名

public class ThreadDemo10 {
    public static void main(String[] args) {
        //创建线程并命名
        Thread thread=new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    //休眠1秒
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"Runnable-Thread");
        //启动线程
        thread.start();
    }
}

线程可以被用来分组管理,分好的组即为线程组

import java.util.Random;

public class ThreadDemo11 {
    public static void main(String[] args) {
        //创建一个线程分组(100米赛跑)
        ThreadGroup group=new ThreadGroup("thread-group");
        //定义一个公共的任务(线程的任务)
        Runnable runTask=new Runnable() {
            @Override
            public void run() {//任务
                //生成一个1-3的随机数
                int num=(1+new Random().nextInt(3));
                try {
                    //跑了n秒之后到达了终点
                    Thread.sleep(num*1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                //得到执行此方法的线程
                Thread t=Thread.currentThread();
                System.out.println(t.getName()+"--选手到达终点:"+num+"s");
            }
        };
        //3.线程(运动员)
        Thread t1=new Thread(group,runTask);//创建选手1
        Thread t2=new Thread(group,runTask);//创建选手2
        Thread t3=new Thread(group,runTask);//创建选手3

        //开跑
        t1.start();
        t2.start();
        t3.start();
        //所有人全部到达终点之后宣布成绩
        while(group.activeCount()!=0) {
        }
        System.out.println("宣布比赛成绩");
    }
}

1.3 Thread 的几个常见属性

在这里插入图片描述
ID 是线程的唯一标识,不同线程不会重复;名字是可以重复的

public class ThreadDemo12 {
    public static void main(String[] args) throws InterruptedException {
        Runnable runnable=new Runnable() {
            @Override
            public void run() {
                //得到(执行当前任务)当前线程
                Thread t=Thread.currentThread();
                //打印线程ID
                System.out.println("线程ID:"+t.getId());
                //打印线程名称
                System.out.println("线程名称:"+t.getName() );
            }
        };
        Thread thread=new Thread(runnable,"线程1");
        thread.start();
        Thread.sleep(500);
        System.out.println();
        Thread thread2=new Thread(runnable,"线程1");
        thread2.start();
    }
}

名称是各种调试工具用到
状态表示线程当前所处的一个情况,下面我们会进一步说明

/**
 * 线程状态的流转
 */
public class ThreadDemo123 {
    public static void main(String[] args) throws InterruptedException {
        Thread t=new Thread(new Runnable() {
            @Override
            public void run() {
                //拿到当前线程并打印线程状态
                Thread thread=Thread.currentThread();
                System.out.println("线程状态2:"+thread.getState());
            }
        });
        //打印线程的状态
        System.out.println("线程状态:"+t.getState());
        t.start();
        //再次打印线程状态
        Thread.sleep(500);
        System.out.println("线程状态3:"+t.getState());
    }

}

优先级高的线程理论上来说更容易被调度到,优先级是int类型,线程优先级1-10,最小的优先级是1,最大的优先级是10,默认的优先级是5
获取线程优先级
thread.getPriority()

/**
 * 获取线程优先级
 */
public class ThreadDemo14 {
    public static void main(String[] args) throws InterruptedException {
        Thread thread=new Thread(new Runnable() {
            @Override
            public void run() {
                //得到当前线程并打印线程的优先级
                Thread t=Thread.currentThread();
                System.out.println("线程优先级:"+t.getPriority());
            }
        });
        System.out.println("线程优先级2:"+thread.getPriority());
        thread.start();
        Thread.sleep(500);
        System.out.println("线程优先级3:"+thread.getPriority());
    }
}

设置线程的优先级:
t3.setPriority(10);

/**
 * 设置优先级
 */
public class ThreadDemo15 {
    private  final static int MAXCOUNT=1000;
    public static void main(String[] args) {
        Thread t1=new Thread(new Runnable() {
            @Override
            public void run() {
                //得到当前线程
                Thread t =Thread.currentThread();
                int priorit=t.getPriority();//得到线程优先级
                for (int i = 0; i < MAXCOUNT; i++) {
                    System.out.println(t.getName()+"--优先级:"+priorit);
                }
            }
        },"线程1");
        //t1.setPriority(10);//设置线程优先级方法一
        t1.setPriority(Thread.MAX_PRIORITY);//方法二(枚举)
        Thread t2=new Thread(new Runnable() {
            @Override
            public void run() {
                //得到当前线程
                Thread t = Thread.currentThread();
                int priorit = t.getPriority();//得到线程优先级
                for (int i = 0; i < MAXCOUNT; i++) {
                    System.out.println(t.getName() + "--优先级:" + priorit);
                }
            }
        },"线程2");
        t2.setPriority(1);//设置线程优先级方法一
        //t2.setPriority(Thread.MIN_PRIORITY);//方法二(枚举)

        Thread t3=new Thread(new Runnable() {
            @Override
            public void run() {
                //得到当前线程
                Thread t = Thread.currentThread();
                int priorit = t.getPriority();//得到线程优先级
                for (int i = 0; i < MAXCOUNT; i++) {
                    System.out.println(t.getName() + "--优先级:" + priorit);
                }
            }
        },"线程3");
        //t3.setPriority(5);
        t3.setPriority(Thread.NORM_PRIORITY);


        //启动线程
        t2.start();
        t1.start();
        t3.start();
    }
}

注意事项:
同时启动多个线程,多个线程设置了不同的优先级,并不是优先级最高的就一定先执行,执行完之后在执行低优先级的线程,而是高优先级的线程获取到CPU时间片的概率更多,整个的执行符合高优先级最先执行完。

关于后台线程,需要记住一点:JVM会在一个进程的所有非后台线程结束后,才会结束运行。
是否存活,即简单的理解,为 run 方法是否运行结束了

/**
 * 线程是否存活
 */
public class ThreadDemoByAlive {
    public static void main(String[] args) throws InterruptedException {
        Thread t=new Thread(()->{
           //得到线程
           Thread thread=Thread.currentThread();
            System.out.println("线程是否存活2:"+thread.isAlive());
        });
        System.out.println("线程是否存活:"+t.isAlive());
        t.start();
        System.out.println("线程是否存活3:"+t.isAlive());
        Thread.sleep(1000);
        System.out.println("线程是否存活4:"+t.isAlive());
    }
}

2.线程分类:

1.用户线程(main默认用户线程)
2.守护线程(后台线程)
为用户线程服务,当一个程序中所有的用户线程结束,那么守护线程也会结束
结论:
1.main线程(主线程)默认是非守护线程(用户线程);
2.在用户线程中创建的子线程也是用户线程。
3.在守护线程中创建的在子线程默认情况下也是守护线程

/**
 * 1.守护线程示例
 */
public class ThreadDemo16 {
    public static void main(String[] args) {
        Thread thread=Thread.currentThread();
        System.out.println("是否是守护线程:"+thread.isDaemon());
    }
}
/**
 * 2.守护线程示例
 */
public class ThreadDemo16 {
    public static void main(String[] args) {
        Thread thread=Thread.currentThread();
        System.out.println("是否是守护线程:"+thread.isDaemon());

        Thread t1=new Thread(()->{
            //得到当前线程
            Thread cTherad=Thread.currentThread();
            System.out.println(cTherad.getName()+"--是否为守护线程"+cTherad.isDaemon());
        },"子线程1");
        t1.start();
    }
/**
 * 3.守护线程示例
 */
public class ThreadDemo16 {
    public static void main(String[] args) throws InterruptedException {
        Thread thread=Thread.currentThread();
        System.out.println("是否是守护线程:"+thread.isDaemon());

        Thread t1=new Thread(()->{
            //得到当前线程
            Thread cTherad=Thread.currentThread();
            System.out.println(cTherad.getName()+"--是否为守护线程"+cTherad.isDaemon());


            Thread tt1=new Thread(()->{
               Thread cThread2=Thread.currentThread();
                System.out.println(cThread2.getName()+"--是否守护线程"+cThread2.isDaemon());
            },"子线程的子线程1");
            tt1.start();
        },"子线程1");
        //手动指定线程为守护线程
        t1.setDaemon(true);
        t1.start();


        //主线程休眠1s
        Thread.sleep(1000);
    }
}

设置守护线程
t1.setDaemon(true);

/**
 * 守护线程示例
 */
public class ThreadDemo16 {
    public static void main(String[] args) {
        Thread thread=Thread.currentThread();
        System.out.println("是否是守护线程:"+thread.isDaemon());

        Thread t1=new Thread(()->{
            //得到当前线程
            Thread cTherad=Thread.currentThread();
            System.out.println(cTherad.getName()+"--是否为守护线程"+cTherad.isDaemon());
        },"子线程1");
        //手动指定线程为守护线程
        t1.setDaemon(true);
        t1.start();
    }
}

守护线程注意事项:

1.守护线程不能再start()之后设置
2.守护线程中创建的线程默认是守护线程。
JVM会等待所有的用户线程全部执行完成之后才退出,但JVM不会等待守护线程执行完再退出

守护线程VS用户线程

用户线程在java程序中很重要,JVM一定要等所有的用户线程执行完成之后才能自行结束,而守护线程是为了用户线程服务的,当所有的用户线程执行完成之后,不论守护线程是否在执行,JVM都会退出。

2.线程常用方法

2.1线程等待 join

等待某个线程执行完之后,在执行后续的代码
在这里插入图片描述
优势:
写法更优雅,运行时所用资源更少

/**
 * join示例
 */
public class ThreadByJoin {
    public static void main(String[] args) throws InterruptedException {
        Thread t1=new Thread(()->{
            //1.张三开始上班
            System.out.println("1.张三开始上班");
            //2.张三正在上班
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            //3.张三下班
            System.out.println("3.张三下班");
        });
        //启动程序
        t1.start();

        //等待t1执行完之后在执行后面的代码
        t1.join();

        Thread t2=new Thread(()->{
            //1.李四开始上班
            System.out.println("1.李四开始上班");
            //2.李四正在上班
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            //3.李四下班
            System.out.println("3.李四下班");
        });
        //启动程序
        t2.start();
    }
}

2.2线程终止

目前常见的有以下两种方式:

  1. 通过自定义标记符来进行中断。
  2. 调用 interrupt() 方法来中断。
    使用 Thread.interrupted() 或者 Thread.currentThread().isInterrupted() 代替自定义标志位.
    在这里插入图片描述
/**
 * 使用自定义标识符终止线程
 */
public class ThreadInterrupet {
    //声明一个自定义的标识符
    private volatile static boolean flag=false;
    public static void main(String[] args) throws InterruptedException {
        Thread thread=new Thread(()->{
            while (!flag){
                System.out.println("正在转帐....");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println("啊,差点误了大事");
        });
        thread.start();

        Thread.sleep(3000);
        //终止线程
        System.out.println("有内鬼,终止交易");
        flag=true;
    }
}

interrupt()需要配合Thread.interrupted()或Thread.currentThread().isInterrupted()一块使用,从而实现线程的终止。

/**
 * 使用interrupt终止线程
 */
public class ThreadInterrupet2 {
    public static void main(String[] args) throws InterruptedException {
        Thread thread=new Thread(()->{
           while (!Thread.interrupted()){
               System.out.println("正在转帐....");
           }
            System.out.println("啊,差点误了大事");
        });
        thread.start();
        Thread.sleep(100);
        //终止线程
        thread.interrupt();
        System.out.println("有内鬼,终止交易");
    }
}
/**
 * 使用isInterrupted终止线程
 */
public class ThreadInterrupet2 {
    public static void main(String[] args) throws InterruptedException {
        Thread thread=new Thread(()->{
           while (!Thread.currentThread().isInterrupted()){
               System.out.println("正在转帐....");
           }
            System.out.println("啊,差点误了大事");
        });
        thread.start();

        Thread.sleep(100);
        //终止线程

        thread.interrupt();
        System.out.println("有内鬼,终止交易");
    }
}

2.3 isInterrupted VS interrupted

1.interrupted属于静态方法,所有的程序都可以直接调用的全局方法;而isInterrupted属于某个实例的方法。
2.interrupted在使用后会重置中断标识符,而isInterrupted不会重置中断标识。

2.4.yield 让出执行权

yield方法会出让CPU的执行权,让线程调度器重新调度线程,但是还有一定的记录再一次调用到出让CPU的线程上,这一次他就会执行线程的方法,因为yield已经执行过了。

/**
 *Yield方法演示(让出CPU执行权)
 */
public class ThreadYield {
    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(() -> {
            Thread cThread=Thread.currentThread();
            for (int i = 0; i < 100; i++) {
                //让出CPU执行权
                Thread.yield();
                System.out.println("执行了线程:" + cThread.getName());
            }
        }, "张三");
        t1.start();

        Thread t2 = new Thread(() -> {
            Thread cThread=Thread.currentThread();
            for (int i = 0; i < 100; i++) {
                System.out.println("执行线程:" + cThread.getName());
            }
        }, "李四");

        t2.start();
    }
}

2.5.获取当前线程

在这里插入图片描述

/**
 * 线程状态
 */
public class ThreadDemo123 {
    public static void main(String[] args) throws InterruptedException {
        Thread t=new Thread(new Runnable() {
            @Override
            public void run() {
                //拿到当前线程并打印线程状态
                Thread thread=Thread.currentThread();
                System.out.println("线程状态2:"+thread.getState());
            }
        });
        //打印线程的状态
        System.out.println("线程状态:"+t.getState());
        t.start();
        //再次打印线程状态
        Thread.sleep(500);
        System.out.println("线程状态3:"+t.getState());
    }

}

2.6 休眠当前线程

也是我们比较熟悉一组方法,有一点要记得,因为线程的调度是不可控的,所以,这个方法只能保证实际休眠时间是大于等于参数设置的休眠时间的。
休眠线程有两种实现:
使用 sleep 休眠
使用 TimeUnit 休眠

使用sleep休眠

在这里插入图片描述

/**
 * 1.休眠演示
 */
public class ThreadSleep {
    public static void main(String[] args) throws InterruptedException {
        Thread thread=new Thread(()->{
            try {
                Thread.sleep(60*60*1000);
            } catch (InterruptedException e) {
                //e.printStackTrace();
                System.out.println("我接受到了终止执行的通知");
            }
        });
        thread.start();
        Thread.sleep(1000);
        System.out.println("终止子线程 thread");
        thread.interrupt();
    }
}

使用TimeUnit 休眠

import java.util.concurrent.TimeUnit;
TimeUnit.DAYS.sleep(1);//天
TimeUnit.HOURS.sleep(1);//小时
TimeUnit.MINUTES.sleep(1);//分
TimeUnit.SECONDS.sleep(1);//秒
TimeUnit.MILLISECONDS.sleep(1000);//毫秒
TimeUnit.MICROSECONDS.sleep(1000);//微妙
TimeUnit.NANOSECONDS.sleep(1000);//纳秒
import java.time.LocalDateTime;
import java.util.concurrent.TimeUnit;

/**
 * 2.休眠演示
 */
public class ThreadTimeUtil {
    public static void main(String[] args) throws InterruptedException {
        System.out.println("主线程开始执行了:"+ LocalDateTime.now());
        TimeUnit.SECONDS.sleep(3);//休眠一天
        //Thread.sleep(1000*60*60*24);
        System.out.println("主线程又开始执行了:"+ LocalDateTime.now());
    }
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值