本人邮箱: kco1989@qq.com

欢迎转载,转载请注明网址 http://blog.csdn.net/tianshi_kco

github: https://github.com/kco1989/kco

代码已经全部托管github有需要的同学自行下载

Thread类

学习java线程的开发者,首先遇到的第一个类就是Thread,通过使用Thread类,我们就可以启动,停止,中断一个线程. 在同一个时间片里, 可能会有多个线程在执行, 每个线程都拥有它自己的方法调用堆栈, 参数和变量.每个app至少会有一个线程--主线程(main thread).

创建一个线程

java创建线程有两种方式

  1. 创建一个继承Thread的子类,并实现run方法
  2. 使用Thread的构造方法public Thread(Runnable target)创建,这个需要传入一个实现Runnable接口的子类

实现

下面我们分别以这两种方式实现一下.

  • 编写SubThread继承Thread,并覆盖run方法 SubThread.java
  1. public class SubThread extends Thread{
  2. @Override
  3. public void run() {
  4. for (int i = 0; i < 10; i ++){
  5. System.out.println(Thread.currentThread().getName() + ":" + i);
  6. }
  7. }
  8. public static void main(String[] args) {
  9. System.out.println("begin main");
  10. SubThread sub = new SubThread();
  11. sub.start();
  12. System.out.println("end main");
  13. }
  14. }
  • 编写SubRunnable实现Runnable,然后使用构造器Thread(Runnable) 创建一个线程
  1. public class SubRunnable implements Runnable {
  2. @Override
  3. public void run() {
  4. for (int i = 0; i < 10; i ++){
  5. System.out.println(Thread.currentThread().getName() + ":" + i);
  6. }
  7. }
  8. public static void main(String[] args) {
  9. System.out.println("begin main");
  10. Thread thread = new Thread(new SubRunnable());
  11. thread.start();
  12. System.out.println("end main");
  13. }
  14. }

区别

  • 使用第一种方法创建的话,你可以在run方法中,可以用this直接调用线程的方法,比如获取线程的id-->this.getId()
  • 而使用第二方法创建线程,在run中,this对象压根就没有getId()这个方法,这个时候你只能用Thread.cuurentThread()这个静态方法获取该线程.
  • 在这里一般推荐使用第二种方法创建,因为这样比较符合面对象的思路,Thread就只负责线程的启动,停止,中断等操作,而Runnable就只负责线程要运行某一个具体任务.

不管使用那种方式创建线程,都可以调用Thread.cuurentThread()获取当前的线程

还有,Thread其实也是Runnable的一个子类

除了上面两种创建方法,其中还有另外一种方法创建线程,那就是实现ThreadFactory接口,这种比较适合批量生成某一种规格的线程

让线程"睡"一会

调用线程的Thread.sleep()方法会让线程睡眠一段时间,这个时候线程会挂起,然后将CPU的时间片转移给其他线程,让其他线程获得执行的机会.

Thread.sleep()接收一个毫秒值做完参数,并抛出一个InterruptedException异常.

停止线程

不管是使用哪一种方法创建线程,run方法的任务执行完了,线程就自动停止.

如果想在中途就停止线程,有下面几种方式

  • 调用线程的interrupt()方法,这时线程的中断位会被标识,并抛出InterruptedException,例如:
  1. public class StopThread1 {
  2. public static void main(String[] args) throws InterruptedException {
  3. System.out.println("begin main");
  4. Thread thread = new Thread(new Runnable() {
  5. @Override
  6. public void run() {
  7. for (int i = 0; i < 10; i ++){
  8. try {
  9. Thread.sleep(100);
  10. System.out.println(Thread.currentThread().getName() + ":" + i);
  11. } catch (InterruptedException e) {
  12. break;
  13. }
  14. }
  15. }
  16. });
  17. thread.start();
  18. System.out.println("main sleep 500ms");
  19. Thread.sleep(500);
  20. thread.interrupt();
  21. System.out.println("end main");
  22. }
  23. }

在调用thread.interrupt();这个语句时,会对该线程的中断状态标识为true,然后在抛出InterruptedException异常时,会清空该中断位.

修改程序,在抛出InterruptedException中添加System.out.println("InterruptedException:" + Thread.currentThread().isInterrupted());,然后再thread.interrupt();后面添加System.out.println("thread.isInterrupted:" + thread.isInterrupted());.然后运行程序.

这时候运行结果有可能打印出thread.isInterrupted:true;InterruptedException:false或者打印出thread.isInterrupted:false;InterruptedException:false,运行多次结果都有可能不一致,这个是因为主线程和子线程都通知在执行,还没有来的及执行主线程的打印语句,子线程异常中的打印语句就已经执行了.

  • 可以在线程中加一个boolean成员变量,提供setter方法,然后在run方法中判断该变量是否为true,若为true则停止线程,否则继续
  1. public class StopThread2 {
  2. public static class StopRunnable implements Runnable{
  3. private boolean isStop = false;
  4. public void setStop(){
  5. this.isStop = true;
  6. }
  7. @Override
  8. public void run() {
  9. int count = 0;
  10. while (!isStop){
  11. try {
  12. Thread.sleep(100);
  13. System.out.println(Thread.currentThread().getName() + ":" + count++);
  14. } catch (InterruptedException e) {
  15. e.printStackTrace();
  16. }
  17. }
  18. }
  19. }
  20. public static void main(String[] args) throws InterruptedException {
  21. System.out.println("begin main");
  22. StopRunnable stop = new StopRunnable();
  23. Thread thread = new Thread(stop);
  24. thread.start();
  25. Thread.sleep(200);
  26. stop.setStop();
  27. System.out.println("end main");
  28. }
  29. }

线程的属性

  • id: 通过Thread.getId()可以获取线程的id,线程的id是一个自增长的long, 不能修改
  • name: 通过Thread.getName(), 用一个字符串来标识线程的名字,可以通过Thread.setName()或部分构造器修改线程的名字
  • priority: 线程的优先级,线程创建默认优先级为5, 最小为优先级为1, 最大为10.优先级大的线程有机会先执行.但具体那个线程先执行还是要看CPU的心情了.
  • state: 线程的状态, 线程的状态有以下几种
    • Thread.State.NEW: 新建状态:这个是线程已经被创建但还没有调用'start()'方法时的状态
    • Thread.State.RUNNABLE: 运行状态 当前线程已经在JVM中执行
    • Thread.State.BLOCKED: 阻塞状态 表示当前线程在等待进入一个同步块或同步方法,也可以等到一个同步快被提交. 常见的有IO阻塞等.
    • Thread.State.WAITING: 等待状态 但线程调用Object.wait(),Thread.join(),LockSupport.park()就会进入等待状态.当前线程在等待其他线程执行某一个特定操作.比如:当前线程执行Object.wait(),那么就需要其他线程执行Object.notify()Object.notifyAll(),如果线程执行了Thread.join(),则需要等到指定的线程执行结束.
    • Thread.State.TIMED_WAITING: 有时间的等待 线程在等待某一个等待的时间.比如,线程执行了Thread.sleep,Object.wait(long),Thread.join(long)
    • Thread.State.TERMINATED: 终结 线程已经执行完毕.
  • daemon: 这个用来标识线程为守护线程或非守护线程的,默认创建的线程都是非守护线程.应用程序所有的非守护线程执行完毕之后,则程序就停止运行.比如主线程都是非守护线程,所以主线程会等到主线程的所有语句执行完成,程序才会停止运行.JVM的资源回收则是一个守护线程.
  1. public class TestDaemonThread {
  2. public static void main(String[] args) {
  3. System.out.println("start main");
  4. Thread thread = new Thread(new Runnable() {
  5. @Override
  6. public void run() {
  7. for (int i = 0; i < 10; i ++){
  8. try {
  9. System.out.println(Thread.currentThread().getName() + ":" + i);
  10. Thread.sleep(10);
  11. } catch (Exception e) {
  12. e.printStackTrace();
  13. }
  14. }
  15. }
  16. });
  17. thread.start();
  18. System.out.println("end main");
  19. }
  20. }

该例子中,程序必须等到主线程和子线程同时执行完成才会停止,因为默认创建的线程都是非守护线程,如果在thread.start();前加入thread.setDaemon(true);, 那么程序不会等子线程执行完才结束程序的.

Thread.join()

等到某线程执行完毕才开始执行,如果调用Thread.join(long)则表示等到某线程执行完毕或指定的超时时间结束后才开始执行

  1. public class ThreadJoinTest {
  2. public static void main(String[] args) throws InterruptedException {
  3. Thread thread1 = new Thread(() -> {
  4. for (int i = 0;i < 10; i ++){
  5. try {
  6. Thread.sleep(10);
  7. System.out.println(Thread.currentThread().getName() + ":" + i);
  8. } catch (InterruptedException e) {
  9. e.printStackTrace();
  10. }
  11. }
  12. }, "thread1");
  13. Thread thread2 = new Thread(() -> {
  14. try {
  15. thread1.join();
  16. for (int i = 0;i < 10; i ++){
  17. Thread.sleep(10);
  18. System.out.println(Thread.currentThread().getName() + ":" + i);
  19. }
  20. } catch (InterruptedException e) {
  21. e.printStackTrace();
  22. }
  23. }, "thread2");
  24. thread1.start();
  25. thread2.start();
  26. }
  27. }

上面的例子,thread2线程会等thread1执行完之后才开始执行

Thread.yield

这个方法标识当前线程会按时线程调度者让其他线程先执行.但CPU是否让其他线程优先执行,还是要看CPU的心情了.

线程的异常

如果线程发现一些运行时异常而没有在run方法俘获,会怎么办?

程序会打印出一推错误堆栈,如果我们先把线程的错误按照某种可读的方式打印到问题,但又不想在每个run方法中增加try{...}catch(Exception e){...}怎么办?

我们查看Thread类的源码发现,在Thread中有一个内部接口UncaughtExceptionHandler,这个正是我们所需要的.实现这个接口,并调用Thread.setUncaughtExceptionHandler,那么但线程出现时,则会回调uncaughtException方法

  1. public class ThreadExceptionTest {
  2. public static void main(String[] args) throws InterruptedException {
  3. System.out.println("begin main");
  4. Thread thread = new Thread(() -> {
  5. int i = 1 / 0;
  6. },"myThread");
  7. thread.setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
  8. @Override
  9. public void uncaughtException(Thread t, Throwable e) {
  10. System.out.println(String.format("%s发生异常%s", t.getName(), e.getMessage()));
  11. }
  12. });
  13. thread.start();
  14. System.out.println("end main");
  15. }
  16. }

打赏

如果觉得我的文章写的还过得去的话,有钱就捧个钱场,没钱给我捧个人场(帮我点赞或推荐一下)



(一)java多线程之Thread的更多相关文章

  1. Java多线程之Thread、Runnable、Callable及线程池

    一.多线程 线程是指进程中的一个执行流程,一个进程中可以有多个线程.如java.exe进程中可以运行很多线程.进程是运行中的程序,是内存等资源的集合,线程是属于某个进程的,进程中的多个线程共享进程中的 ...

  2. Java多线程之Runnable与Thread

    Java多线程之Thread与Runnable 一.Thread VS Runnable 在java中可有两种方式实现多线程,一种是继承Thread类,一种是实现Runnable接口:Thread类和 ...

  3. Java基础-进程与线程之Thread类详解

    Java基础-进程与线程之Thread类详解 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.进程与线程的区别 简而言之:一个程序运行后至少有一个进程,一个进程中可以包含多个线程 ...

  4. JAVA多线程之wait/notify

    本文主要学习JAVA多线程中的 wait()方法 与 notify()/notifyAll()方法的用法. ①wait() 与 notify/notifyAll 方法必须在同步代码块中使用 ②wait ...

  5. JAVA多线程之volatile 与 synchronized 的比较

    一,volatile关键字的可见性 要想理解volatile关键字,得先了解下JAVA的内存模型,Java内存模型的抽象示意图如下: 从图中可以看出: ①每个线程都有一个自己的本地内存空间--线程栈空 ...

  6. java多线程之yield,join,wait,sleep的区别

    Java多线程之yield,join,wait,sleep的区别 Java多线程中,经常会遇到yield,join,wait和sleep方法.容易混淆他们的功能及作用.自己仔细研究了下,他们主要的区别 ...

  7. JAVA多线程之UncaughtExceptionHandler——处理非正常的线程中止

    JAVA多线程之UncaughtExceptionHandler——处理非正常的线程中止 背景 当单线程的程序发生一个未捕获的异常时我们可以采用try....catch进行异常的捕获,但是在多线程环境 ...

  8. java多线程之wait和notify协作,生产者和消费者

    这篇直接贴代码了 package cn.javaBase.study_thread1; class Source { public static int num = 0; //假设这是馒头的数量 } ...

  9. Java多线程之ConcurrentSkipListMap深入分析(转)

    Java多线程之ConcurrentSkipListMap深入分析   一.前言 concurrentHashMap与ConcurrentSkipListMap性能测试 在4线程1.6万数据的条件下, ...

随机推荐

  1. PHP命名空间的概念与使用

    命名空间在其它编程语言中其名称不尽相同,但其核心慨念都是自定义一个存储空间.避免类名重复系统无法判断该执行哪一个类或是哪一个函数. 举例说明下.我先创建test这个文件夹在其当前目录下再创建一个ind ...

  2. wemall app商城源码Android短信监听接收器

    wemall doraemon是Android客户端程序,服务端采用wemall微信商城,不对原商城做任何修改,只需要在原商城目录下上传接口文件即可完成服务端的配置,客户端可随意定制修改.本文分享其中 ...

  3. 重磅消息:微信小程序支持长按二维码进入

    之前微信小程序一般通过以下入口进入: 而用户经常使用“长按二维码”识别应用的功能一直未开放,据酷客多了解,微信安卓6.5.6内测版已经支持长按二维码识别和进入小程序,意味着把小程序二维码分享给朋友,或 ...

  4. otool是mac自带的工具

    找了一晚上otool的安装包,到最后才发现mac自带otool

  5. 菜鸟Scrum敏捷实践系列(三)用户故事的组织---功能架构的规划

    菜鸟Scrum敏捷实践系列索引 菜鸟Scrum敏捷实践系列(一)用户故事概念 菜鸟Scrum敏捷实践系列(二)用户故事验收 菜鸟Scrum敏捷实践系列(三)用户故事的组织---功能架构的规划 采用Sc ...

  6. HTML 表单常用的代码元素

    表单: 将数据通过浏览器提交到服务器的媒介.<form action="" method="get/post" ></form> get ...

  7. Webstorm less watcher 配置

    file > sttings > File watchers > 添加LESS watcher 配置如图:

  8. Linux之uniq命令

    uniq - report or omit repeated lines  省去重复的行 参数: -i  忽略大小写字符的不同 -c  对重复的行进行记数 注意:uniq命令只会对相邻的重复的行进行去 ...

  9. 自动部署Nginx和nfs并架设Nginx集群脚本

    本人经过多次尝试,简单完成了自动部署Nginx和nfs脚本,并且能够自动部署web反向代理集群,下面详细的阐述一下本人的思路.(以下脚本本人处于初学阶段,写的并不是很完善,所以需要后期进行整理和修正, ...

  10. Node.js编程之异步

    异步操作 Node采用V8引擎处理JavaScript脚本,最大特点就是单线程运行,一次只能运行一个任务.这导致Node大量采用异步操作(asynchronous opertion),即任务不是马上执 ...