Thead类与Runnable接口

Java的线程,即一个Thread实例。

Java的线程执行过程有两种实现方式:

  • 子类继承Thread类,并且重写void run()方法。
  • 自定义类实现Runnable接口,并且实现void run()方法。并在Thead构造时,将Runnable实例放入Thead。

Thread类

创建一个新线程必须实例化一个Thread对象。

使用方法:

  1. 子类继承Thread类。重写Thread的run()方法。
  2. 实例化该子类。
  3. 执行Thread的start()方法启动一个线程。

例:Thread使用方法

/**
*  Thread类。
*/
class MyThread extends Thread {    public void run() {//...  线程执行
    }
}
public class TestThread1 {public static void main(String args[]) {MyThread thread = new MyThread ();    //新建线程。thread .start();                    //线程开始执行。//主线程其他方法
    }
}

Runnable接口

实现Runnable接口需要实现run()方法.run()方法代表线程需要完成的任务.因此把run方法称为线程执行体.

使用方法:

  1. 类实现Runnable接口,并且实现run()方法。
  2. 实例化该类。Runnable runnable=new MyRun();
  3. 把该类注入到Thread对象中,即通过Thread的构造方法注入。Thread thread=new Thread(runnable);
  4. 调用Thread实例的start()方法。启动线程。thread.start();

例:Runnable使用方法

class MyRunner implements Runnable {    //实现Runnable接口public void run() {// ......    线程执行
    }
}public class TestThread1 {public static void main(String args[]) {MyRunner runner= new Runner1(); Thread t = new Thread(runner);    //新建线程。t.start();                   //线程开始执行。// ......    主线程继续执行
    }}

两种方式所创建线程的对比

实现Runnable接口方式的多线程:

  • 编程稍复杂。
  • 如果需要访问当前线程,必须使用Thread.currentThread()方法。
  • 线程只是实现了Runnable接口,还可以继承其他类。
  • 在这种方式下,可以多个线程共享同一个target对象,所以非常适合多个相同线程来处理同一份资源的情况,从而可以将CPU,代码,数据分开,形成清晰的模型,较好地体现了面向对象的思想。

继承Thread类方式的多线程:

  • 编程简单。
  • 如果需要访问当前线程直接使用this。
  • 已经继承了Thread类,无法再继承其他父类。

Thread的常用API

1  Thread类的构造方法

  • Thread()
  • Thread(Runnable target)
  • Thread(Runnable target, String name)
  • Thread(String name)
  • Thread(ThreadGroup group, Runnable target)
  • Thread(ThreadGroup group, Runnable target, String name)
  • Thread(ThreadGroup group, Runnable target, String name, long stackSize)
  • Thread(ThreadGroup group, String name)

2常用方法

(1) 启动线程

  • void run() :如果该线程是使用独立的 Runnable 运行对象构造的,则调用该 Runnable 对象的 run 方法;否则,该方法不执行任何操作并返回。
  • void start() :使该线程开始执行;Java 虚拟机调用该线程的 run 方法。

(2) 线程状态控制

  • boolean isAlive()    测试线程是否处于活动状态。
  • int getPriority()    返回线程的优先级。
  • void setPriority(int newPriority)    更改线程的优先级。
  • static void sleep(long millis)
  • static void sleep(long millis, int nanos)    当前线程暂停运行指定的毫秒数(加指定的纳秒数),但此线程不失去已获得的锁旗标.
  • void join()     等待该线程终止。
  • void join(long millis)    等待该线程终止的时间最长为 millis 毫秒。
  • void join(long millis, int nanos)    等待该线程终止的时间最长为 millis 毫秒 + nanos 纳秒。
  • void interrupt()    中断线程。 调用该方法引发线程抛出InterruptedException异常。
  • static boolean interrupted()    测试当前线程是否已经中断。
  • boolean isInterrupted()    测试线程是否已经中断。
  • static void yield()    暂停当前正在执行的线程对象,并执行其他线程。
  • Thread.State getState()    返回该线程的状态。

(3) 当前线程

  • static Thread currentThread() : 返回对当前正在执行的线程对象的引用。
  • String getName() : 返回该线程的名称.
  • void setName(String name) : 设置线程名称.
  • void setDaemon(boolean on) 将该线程标记为守护线程或用户线程。
  • boolean isDaemon() :测试该线程是否为守护线程。
  • String toString() : 返回该线程的字符串表示形式,包括线程名称 优先级和线程组。
  • long getId() :返回该线程的标识符。
  • void checkAccess() :判定当前运行的线程是否有权修改该线程。
  • ClassLoader getContextClassLoader() :返回该线程的上下文 ClassLoader。
  • static Thread.UncaughtExceptionHandler getDefaultUncaughtExceptionHandler() :返回线程由于未捕获到异常而突然终止时调用的默认处理程序。
  • Thread.UncaughtExceptionHandler getUncaughtExceptionHandler() :返回该线程由于未捕获到异常而突然终止时调用 的处理程序。

(4) 线程组

  • static int enumerate(Thread[] tarray) :将当前线程的线程组及其子组中的每一个活动线程复制到指定的数组中。
  • StackTraceElement[] getStackTrace() :返回一个表示该线程堆栈转储的堆栈跟踪元素数组。
  • static int activeCount() :返回当前线程的线程组中活动线程的数目。
  • ThreadGroup getThreadGroup() :返回该线程所属的线程组。

(5) 其他

  • static Map<Thread,StackTraceElement[]> getAllStackTraces() 返回所有活动线程的堆栈跟踪的一个映射。

线程的生命周期

1  新建和就绪状态

当程序使用new关键字创建了一个线程后,该线程就处于新建状态。JVM为Thread对象分配内存。初始化其成员变量的值。线程对象调用start()方法之后,该线程处于就绪状态。 JVM会为其创建方法调用栈和程序计数器。
    就绪状态的线程并没有开始运行,它只是表示该线程可以运行了。JVM的线程调度器调度该线程运行。

注意:

  • 调用start()启动线程之后,run()方法才会被看作线程执行体。
  • 直接调用run()方法,则run()方法就只是一个普通方法。

2  运行和阻塞状态

就绪状态的线程获得了CPU,开始执行run方法的线程执行体。则该线程处于运行状态。线程在执行过程中可能会被中断,以使其他线程获得执行的机会,线程调度取决于底层平台采用的策略。

现代桌面和服务器操作系统一般都采用抢占式策略。一些小型设备如手机则可能采用协作式调度。 抢占式策略的系统:系统给每个可执行的线程一个小时间段来处理任务;当该时间段用完,系统会剥夺该线程所占有的资源,让其他线程获得执行机会.在选择下一个线程时,系统会考虑线程的优先级.

3  线程进入阻塞状态

  1. 线程调用sleep方法主动放弃所占用的处理器资源。
  2. 线程调用了一个阻塞式的IO方法,该方法返回之前,该线程被阻塞。
  3. 线程试图获得一个同步监视器,但同步监视器正被其他线程所持有。
  4. 线程在等待某个通知(notify)。
  5. 线程调用了线程的suspend方法将线程挂起。不过这个方法容易导致死锁,所以程序应该尽量避免使用该方法。

4  阻塞线程重写进入就绪状态

  1. 调用sleep方法的线程经过了指定的时间。
  2. 线程调用的阻塞式IO方法已经返回。
  3. 线程成功地获得了试图取得同步监视器。
  4. 线程正在等待某个通知时,其他线程发出了一个通知。
  5. 处于关闭状态的线程被调用了resume恢复方法。

5  线程死亡

线程在以下情况下死亡:

  • run()方法执行完成,线程正常结束.
  • 线程抛出一个未捕获的Exception或Error.
  • 直接调用该线程的stop()方法来结束该线程.该方法容易导致死锁,通常不推荐使用.

Thread对象的isAlive()方法,查看线程是否死亡。

注意:

  • 死亡的线程不能再用start()方法重新启动。
  • 一个线程的start()方法不能两次调用。

6  线程睡眠:sleep

sleep方法将线程转入阻塞状态。时间到即再转入就绪状态。

线程的操作与特性

1  线程让步: yeild

yeild() 方法是静态方法。该方法使当前线程让出CPU资源,继续参与线程竞争。

2  join线程

public final void join() throws InterruptedException
public final synchronized void join(long millis) throws InterruptedException

join()方法表示当前线程等待指定线程的结束,等待的线程结束后继续执行当前线程。

例:join方法示例。

public class JoinMain {public volatile static int i=0; public static class AddThread extends Thread{ @Override public void run() {for(i=0;i<10000000;i++); }}public static void main(String[] args) throws InterruptedException { AddThread at=new AddThread(); at.start(); at.join();     // 当前线程(主线程)等待at线程运行结束。
        System.out.println(i); }
}

join的本质

查看join方法的源码,可以了解到,join即相当于以下代码:

while ( 指定的线程.isAlive()) {wait(0);
}

当前线程会一直调用wait()方法,所以当前线程会一直处于等待状态。当被等待的线程结束(即指定的线程结束),JVM会自动调用notifyAll()方法来通知当前线程wait已经结束。注意,此处不要手动调用thread.notifyAll()方法。(关于join方法,JDK文档有更详细的说明)

3  线程优先级

每个线程都有优先级。优先级高的线程获得较多的执行机会。默认情况下,main线程具有普通优先级。每个线程默认优先级与创建它的父线程具有同样的优先级。

Java提供的优先级范围是1~10。默认优先级为5。

Thread提供静态常量:

  • static int MAX_PRIORITY 线程可以具有的最高优先级。 值为10。
  • static int MIN_PRIORITY 线程可以具有的最低优先级。值为1。
  • static int NORM_PRIORITY 分配给线程的默认优先级。值为5。

注意:

  • 不同操作系统的优先级不同.应尽量避免直接为线程指定优先级,而应使用以上三个静态常量类设置优先级.

4  后台线程

运行在后台,用于为用户线程提供服务。又称为“守护线程”。所有用户线程都结束后,后台线程也会结束,JVM退出。

  • main方法的主线程是前台线程。
  • 前台线程创建的线程默认是前台线程。后台线程创建的线程默认是后台线程。

比较:

  • 普通线程:若主线程结束,普通线程不会结束,JVM不会退出。
  • 守护线程:若主线程以及所有普通线程都结束,则后台线程会直接死亡,JVM退出。

常用API

  • 调用Thread对象的setDeamon(true)方法可以将指定线程设置成后台线程。
  • Thread对象的isDeamon()方法用于判断指定线程是否为后台线程。

5  停止线程

若需要停止线程,不推荐使用stop()方法。stop方法强制线程停止,切线程会释放所有monitor。由于stop方法过于粗暴,已经被废弃。

例:现在有两条记录。两条线程的其中一个线程要写对象,另一个线程要读对象。两个线程对对象加锁。

若使用stop方法,可能导致数据一致性的错误。

public class StopThreadTest {static Student stu = new Student();public static void main(String[] args) throws InterruptedException {Thread writeThread = new Thread(){@Overridepublic void run() {// 假设writeThread比readThread先拿到stu的锁。synchronized(stu){stu.id = 2;    // 可能该语句执行完后,主线程中 writeThread.stop(); 开始产生作用。// 若主线程执行 writeThread.stop();则该线程在此处将强行结束。
                java_label_stop:stu.name = "小王";}}};Thread readThread = new Thread(){@Overridepublic void run() {// 假设writeThread比readThread先拿到stu的锁。synchronized(stu){// 若在java_label_stop处writeThread被结束,则该语句将打印出 2 ; 小明 。从而导致数据一致性错误System.out.println(stu.id + ";" + stu.name);}}};// 主线程设置stu。stu.id = 1;stu.name = "小明";// 假设writeThread比readThread先拿到stu的锁。
        writeThread.start();readThread.start();// 强制停止写线程。
        writeThread.stop();}}class Student{public int id;public String name;
}

6 线程中断

public void Thread.interrupt()               // 中断线程。修改线程的中断状态,但线程本身不会有任何响应,依旧运行。public boolean Thread.isInterrupted()        // 判断是否被中断。public static boolean Thread.interrupted()   // 判断是否被中断,并清除当前中断状态

调用线程中断方法只是给线程“打个招呼”,线程不会本身不会做任何相应。

例:调用interrupt()方法,线程依旧运行。

public class InterruptThreadTest {public static void main(String[] args) {Thread t1 = new Thread(){@Overridepublic void run(){ while(true){ Thread.yield(); } }};t1.start();        // 启动线程。t1.interrupt();    // 终端线程,线程会依旧运行。
    }
}

线程中断方法可以用于结束线程,非常优雅方便。

例:线程外部使用中断方法,结束线程。

public class InterruptStopThreadTest {public static void main(String[] args) {Thread t1 = new Thread() {@Overridepublic void run() {while (true) {// 检测线程被中断if (Thread.currentThread().isInterrupted()) {// 若线程中断,则推出run方法。System.out.println("Interruted!");break;}Thread.yield();}}};t1.start();     // 启动线程。t1.interrupt(); // 终端线程,但线程依旧运行。t1.isInterrupted()方法将返回true。
    }
}

InterruptedException异常

大部分线程的等待方法都会抛出InterruptedException异常,中断标志位将会被清空。Java方法中默认的等待线程一旦被interrupt(),则等待方法会立即抛出InterruptedException异常。

例:处于sleep的线程被interrupt,立即抛出InterruptedException。

public class InterruptedExceptionTest {public static void main(String[] args) throws InterruptedException {Thread t1 = new Thread() {@Overridepublic void run() {while (true) {if (Thread.currentThread().isInterrupted()) {System.out.println("Interruted!");break;}try {Thread.sleep(2000);} catch (InterruptedException e) {System.out.println("Interruted When Sleep");// 抛出异常后会清除中断标记位。所以重新设置中断状态。
                        Thread.currentThread().interrupt();}Thread.yield();}}};t1.start();// 让主线程在恰当的时间去中断t1。Thread.sleep(1000);// 此时t1处于sleep状态。interrupt() 方法会使t1中的sleep方法抛出InterruptedException异常。
        t1.interrupt();}
}

打印输出:

Interruted When Sleep
Interruted!

7 suspend与resume

suspend() 方法表示将线程挂起。resume() 方法表示继续执行挂起的线程。

但需要注意suspend()不会释放锁。如果加锁发生在resume()之前,则发生死锁。

这两个方法都已经被废弃,不要使用。

例:运行以下程序,程序将被锁死。

package sjq.thread.suspend_resume;
package sjq.thread.suspend_resume;public class SuspendResumeThreadTest {public static Object u = new Object();public static ChangeObjectThread t1 = new ChangeObjectThread("t1");public static ChangeObjectThread t2 = new ChangeObjectThread("t2");public static class ChangeObjectThread extends Thread {public ChangeObjectThread(String name){super.setName(name);}@Overridepublic void run() {synchronized(u){System.out.println("in " + super.getName());Thread.currentThread().suspend();}}}public static void main(String[] args) throws InterruptedException {t1.start();Thread.sleep(100);t2.start();t1.resume();  // 主线程通知t1要释放resume,但t1线程可能还未执行suspend,当t1执行了suspend后,则t1将永远被挂起。且不会释放资源。t2.resume();  // 主线程通知t2要释放resume,但t2线程可能还未执行suspend,当t2执行了suspend后,则t2将永远被挂起。且不会释放资源。
        t1.join();t2.join();}
}

控制台显示:

该程序通过jstack查看线程情况,发现t2线程处于RUNNABLE状态,被suspend0方法挂起。且t2拥有Object的锁,只要t2线程不结束,Object的锁就不会被释放。

转载于:https://www.cnblogs.com/shijiaqi1066/p/3369836.html

Java 并发——多线程基础相关推荐

  1. Java并发编程基础--ThreadLocal

    Java并发编程基础之ThreadLocal ​ ThreadLocal是一个线程变量,但本质上是一个以ThreadLocal对象为键.任意对象为值的存储结构,这个结构依附在线程上,线程可以根据一个T ...

  2. Java并发编程的艺术-Java并发编程基础

    第4章 Java并发编程基础 ​ Java从诞生开始就明智地选择了内置对多线程的支持,这使得Java语言相比同一时期的其他语言具有明显的优势.线程作为操作系统调度的最小单元,多个线程能够同时执行,这将 ...

  3. java线程知乎_全网独家!知乎20K点赞的Java并发多线程笔记,简直堪称神仙级文档...

    有很多小伙伴都问过我,头条号里的关于java多线程的文章有pdf版本吗?我其实很想弄pdf,但是前段时间一直没时间去折腾,我把每个Java并发编程核心技术的都整理成了一个又一个的文档.昨天也是终于全部 ...

  4. Java 并发/多线程教程(四)-并发模型

    本系列译自jakob jenkov的Java并发多线程教程(本章节部分内容参考http://ifeve.com/并发编程模型),个人觉得很有收获.由于个人水平有限,不对之处还望矫正! 并发系统可以有多 ...

  5. Java 并发/多线程教程(五)-相同线程

    本系列译自jakob jenkov的Java并发多线程教程,个人觉得很有收获.由于个人水平有限,不对之处还望矫正! 相同线程是一并发框架模型,是一个单线程系统向外扩展成多个单线程的系统.这样的结果就是 ...

  6. Java并发/多线程教程——1

    本系列译自jakob jenkov的Java并发多线程教程,个人觉得很有收获.由于个人水平有限,不对之处还望矫正!在早期,计算机只有一个CPU,同一时刻只能执行一个程序,后来有了多任务的说法,多任务是 ...

  7. java并发多线程面试_Java多线程并发面试问答

    java并发多线程面试 Today we will go through Java Multithreading Interview Questions and Answers. We will al ...

  8. java 并发多线程显式锁概念简介 什么是显式锁 多线程下篇(一)

    java 并发多线程显式锁概念简介 什么是显式锁 多线程下篇(一) 目前对于同步,仅仅介绍了一个关键字synchronized,可以用于保证线程同步的原子性.可见性.有序性 对于synchronize ...

  9. Java 并发 多线程:创建线程的四种方式

    Java 并发 多线程: 创建线程的四种方式 继承 Thread 类并重写 run 方法 实现 Runnable 接口 实现 Callable 接口 使用线程池的方式创建 1. 通过继承 Thread ...

最新文章

  1. 有一个5 * 5的二维数组,保留主对角线上的元素,并使其他元素均为0,要求用函数和子函数完成
  2. 今天 CSDN 编辑器的一个惊人的变化
  3. golang 理解包导入
  4. LeetCode 61——旋转链表
  5. 微软发布Azure Cosmos DB产品以及新的物联网解决方案
  6. Android Treble 计划技术文档
  7. .net core 与ELK(2)安装Elasticsearch可视化工具
  8. 《剑指Offer》 数值的整数次方
  9. linux下java多线程_Linux系统下Java问题排查——cpu使用率过高或多线程锁问题
  10. 我是如何在自学编程9个月后找到工作的
  11. java svg to png_如何用Image Magick将SVG转换为PNG?
  12. mac safari无法打开网页_safari无法打开网页是什么原因?mac上的Safari浏览器打不开网页怎么办?...
  13. DRILLNET 2.0------第二十三章 井控压井单模型
  14. ubuntu安装vbox虚拟机
  15. 怎么免费注册微信小程序-微信小程序开发-视频教程1
  16. 追风筝的人 第九章
  17. java代码控制开关
  18. css如何去掉或修改浏览器默认滚动条
  19. 最优化方法与实践-抛物线法(matlab)
  20. 国内虚拟主机及国内空间商排名【转】

热门文章

  1. JZOJ 3789. 【NOI2015模拟8.20】编辑器
  2. JZOJ 5478. 【NOIP2017提高组正式赛】列队
  3. JZOJ 3804. 【NOIP2014模拟8.24】小X 的AK 计划
  4. php管理员登录文件,快速的事情,只有管理员,PHP才能访问文件
  5. php记录邮件发送,有关php邮件发送一点记录
  6. php中一定要写 吗,PHP编程一定要改掉的5个不良习惯
  7. BZOJ 3329 Xorequ (数位DP、矩阵乘法)
  8. php插件 pycharm_原来Pycharm中有这么多好用的插件|Pycharm精选插件
  9. NOI2013矩阵游戏
  10. BZOJ2194 快速傅立叶之二 【fft】