详解Thread多线程

作者 : 开心源码 本文共5804个字,预计阅读时间需要15分钟 发布时间: 2022-05-12 共181人阅读

如何创立线程

Java 中创立线程的方法有三种:
1. 继承 Thread 类创立线程

新建一个类继承 Thread 类,并重写 Thread 类的 run() 方法。
创立 Thread 子类的实例。
调用该子类实例的 start() 方法启动该线程。

代码举例如下:

public class HelloThread1 {    static class ThreadDemo extends Thread {        @Override        public void run() {            System.out.println("Hello Thread");        }    }    public static void main(String[] args) {        ThreadDemo threadDemo = new ThreadDemo();        threadDemo.start();    }}

2.实现 Runnable 接口创立线程:

创立一个类实现 Runnable 接口,并重写该接口的 run() 方法。
创立该实现类的实例。
将该实例传入 Thread(Runnable r) 构造方法中创立 Thread 实例。
调用该 Thread 线程对象的 start() 方法。

代码举例如下:

public class HelloThread1 {    static class ThreadDemo extends Thread {        @Override        public void run() {            System.out.println("Hello Thread");        }    }    public static void main(String[] args) {        ThreadDemo threadDemo = new ThreadDemo();        threadDemo.start();    }}

3. 使用 Callable 和 FutureTask 创立线程:

创立一个类实现 Callable 接口,并重写 call() 方法。
创立该 Callable 接口实现类的实例。
将 Callable 的实现类实例传入 FutureTask(Callable<V> callable) 构造方法中创立 FutureTask 实例。
将 FutureTask 实例传入 Thread(Runnable r) 构造方法中创立 Thread 实例。
调用该 Thread 线程对象的 start() 方法。
调用 FutureTask 实例对象的 get() 方法获取返回值。

代码举例如下:

public class HelloThread3 {    static class ThreadDemo implements Callable<String> {        @Override        public String call() {            System.out.println("Hello Thread");            return "Callable return value";        }    }    public static void main(String[] args) {        ThreadDemo threadDemo = new ThreadDemo();        FutureTask<String> futureTask = new FutureTask<String>(threadDemo);        Thread thread = new Thread(futureTask);        thread.start();        try {            System.out.println(futureTask.get());        } catch (Exception e) {            e.printStackTrace();        }    }}

线程的生命周期

关于Java中线程的生命周期,首先看一下下面这张图:

上图中基本上囊括了Java中多线程各重要知识点。掌握了上图中的各知识点,Java中的多线程也就基本上掌握了。

线程的基本状态

新建状态(New):当线程对象对创立后,即进入了新建状态,如:Thread t = new MyThread();

就绪状态(Runnable):当调用方法t.start()时,线程即进入就绪状态。处于就绪状态的线程,只是说明此线程已经做好了准备,随时等待CPU调度执行,并不是说执行了t.start()此线程立即就会执行;同样还有几种情况会进行就绪状态,请参见上图。

运行状态(Running):当CPU开始调度处于就绪状态的线程时,此时线程才得以真正执行,即进入到运行状态。注:就绪状态是进入到运行状态的唯一入口,也就是说,线程要想进入运行状态执行,首先必需处于就绪状态中;

阻塞状态(Blocked):处于运行状态中的线程因为某种起因,暂时放弃对CPU的使用权,中止执行,此时进入阻塞状态,直到其进入到就绪状态,才 有机会再次被CPU调用以进入到运行状态。根据阻塞产生的起因不同,阻塞状态又可以分为三种:请参见上图。

死亡状态(Dead):线程执行完了或者者因异常退出了run()方法,该线程结束生命周期。

线程的基本操作

sleep:使当前线程休眠指定毫秒

yield:使当前线程让出CPU,从运行状态转到可运行状态。注意仅仅是让出,让出之后也会加入到抢占资源的队伍中。

join:把指定的线程加入到当前线程,比方在线程B中调用了线程A的Join()方法,直到线程A执行完毕后,才会继续执行线程B

线程中止:

Thread本身提供了一个stop方法,但是这个不推荐使用。由于使用stop的时候会暴力终止线程,从而造成数据不一致。

优雅的中止线程的代码举例如下:

public class StopThread {    static class ThreadDemo extends Thread {        volatile boolean stopMe = false;        public void stopMe(){            this.stopMe=true;        }        @Override        public void run() {            while (true) {                if (stopMe) {                    System.out.println("程序结束");                    break;                }                try {                    Thread.sleep(1000);                } catch (InterruptedException e) {                    e.printStackTrace();                }            }        }    }    public static void main(String[] args) {        ThreadDemo threadDemo = new ThreadDemo();        threadDemo.start();        threadDemo.stopMe();    }}

以一个volatile修饰的变量stopMe来控制线程的中止。

线程中断:

线程中断的相关方法分别是这三个

public void interrupt() ; //中断线程public boolean isInterrupted(); //判断线程能否被中断public static boolean interrupted(); //判断线程能否被中断,并清理当前中断状态

线程中断的代码举例如下:

public class InterruptThread {    static class ThreadDemo extends Thread {        @Override        public void run() {            while (true) {                if (Thread.currentThread().isInterrupted()) {                    System.out.println("程序结束");                    break;                }                try {                    Thread.sleep(1000);                } catch (InterruptedException e) {                    e.printStackTrace();                }            }        }    }    public static void main(String[] args) {        ThreadDemo threadDemo = new ThreadDemo();        threadDemo.start();        threadDemo.interrupt();    }}

wait 和notify

这两个方法是JDK为了支持多线程之间的协作而提供的。

当在线程A中调用了obj.wait()方法时,线程A会中止执行进入等待状态。直到其余线程调用obj.notify()时才会进入阻塞状态继而等待获取锁。

请看下方示例代码

package cn.shiyujun.thread.hellothread;/** * d * * @author syj * CreateTime 2019/03/19 * describe:WaitNotify测试demo */public class WaitNotifyThread {    public static Object obj = new Object();    static class WaitThreadDemo extends Thread {        @Override        public void run() {            synchronized (obj) {                try {                    System.out.println("WaitThread wait,time=" + System.currentTimeMillis());                    obj.wait();                } catch (InterruptedException e) {                    e.printStackTrace();                }                System.out.println("WaitThread end,time=" + System.currentTimeMillis());            }        }    }    static class NotifyThreadDemo extends Thread {        @Override        public void run() {            synchronized (obj) {                System.out.println("NotifyThread notify,time=" + System.currentTimeMillis());                obj.notify();                try {                    Thread.sleep(2000);                } catch (InterruptedException e) {                    e.printStackTrace();                }                System.out.println("NotifyThread end,time=" + System.currentTimeMillis());            }        }    }    public static void main(String[] args) {        WaitThreadDemo waitThreadDemo = new WaitThreadDemo();        NotifyThreadDemo notifyThreadDemo = new NotifyThreadDemo();        waitThreadDemo.start();        try {            Thread.sleep(100);        } catch (InterruptedException e) {            e.printStackTrace();        }        notifyThreadDemo.start();    }}

在上方的代码中,Wait线程会首先获取到obj的锁,当它执行wait方法时就会释放obj的锁并进入等待状态。这个时候Notify线程可以获取到obj的锁,并且唤醒Wait线程,但是由于此时Notify线程是睡眠了2秒钟之后才释放的obj的锁,所以Wait线程获取锁的时候Notify线程已经执行完毕了。

此程序的运行结果:

WaitThread wait,time=1553088237753NotifyThread notify,time=1553088237862NotifyThread end,time=1553088239867WaitThread end,time=1553088239867

suspen和resume

它们两个的功能是挂起线程和继续执行,被suspen挂起的线程必需被resume唤醒才可以继续执行。乍看起来 可以实现wait和notify的功能,不过我可不推荐你使用它们,和wait之后会释放锁不同,suspen挂起之后仍然会持有锁,这个可就非常危险了。

线程组

在一个系统中假如线程数量众多而又功能比较一致,即可以把这些线程放到一个线程组里。

线程组示例代码:

public class ThreadGroupDemo {    static class ThreadDemo extends Thread {        @Override        public void run() {            while (true){                System.out.println("I am "+Thread.currentThread().getThreadGroup().getName()+"-"+Thread.currentThread().getName());                try {                    sleep(2000);                } catch (InterruptedException e) {                    e.printStackTrace();                }            }        }    }    public static void main(String[] args) {        ThreadGroup threadGroup=new ThreadGroup("groupDemo");        Thread t1=new Thread(threadGroup,new ThreadDemo(),"t1");        Thread t2=new Thread(threadGroup,new ThreadDemo(),"t2");        t1.start();        t2.start();    }}

守护线程

在线程的世界里,由我们自己创立的线程叫客户线程。而少量系统创立的线程,如垃圾回收线程等被称之为守护线程,假如想要把一个客户线程设置为守护线程可以在线程的调用线程的start方法前设置线程的daemon属性为true;

t1.setDaemon(true);

当一个程序中只有守护线程的时候这个程序也就要结束了。

线程的优先级

Java中的线程可以有自己的优先级,优先级高的线程在进行资源抢占的时候往往会更有优势。线程饥饿现象就是因为线程的优先级低无法抢占资源而引起的。

在Java中线程的优先级可以设置的范围是1到10之间,数字越大优先级越高。Java线程创立默认的优先级是5,我们可以线程start之前通过如下方式设置线程的优先级。

t1.setPriority(1);

喜欢这篇文章的朋友可以点个喜欢,也可以关注一下我的个人专题:Java成长之路

针对于上面所涉及到的知识点我总结出了有1到5年开发经验的程序员在面试中涉及到的绝大部分架构面试题及答案做成了文档和架构视频资料免费分享给大家(包括Dubbo、Redis、Netty、zookeeper、Spring cloud、分布式、高并发等架构技术资料),希望能帮助到您面试前的复习且找到一个好的工作,也节省大家在网上搜索资料的时间来学习,也可以关注我一下以后会有更多干货分享。

资料获取方式: QQ群搜索“708-701-457” 就可免费领取



说明
1. 本站所有资源来源于用户上传和网络,如有侵权请邮件联系站长!
2. 分享目的仅供大家学习和交流,您必须在下载后24小时内删除!
3. 不得使用于非法商业用途,不得违反国家法律。否则后果自负!
4. 本站提供的源码、模板、插件等等其他资源,都不包含技术服务请大家谅解!
5. 如有链接无法下载、失效或广告,请联系管理员处理!
6. 本站资源售价只是摆设,本站源码仅提供给会员学习使用!
7. 如遇到加密压缩包,请使用360解压,如遇到无法解压的请联系管理员
开心源码网 » 详解Thread多线程

发表回复