文档章节

Java多线程(一)-- 线程基础

兴趣使然的程序员
 兴趣使然的程序员
发布于 2017/05/03 16:11
字数 1486
阅读 20
收藏 0

1、线程的创建和运行

Java中有两种方式可以创建线程:

  1. 继承Thread类,重写run()方法
  2. 创建一个实现Runnable接口的类,重写run()方法,然后使用带参数的Thread构造器来创建Thread对象。

需要注意的是,创建Thread类的实例并不会创建一个新的线程,调用run()方法也不会,只有调用start()方法才会创建一个新的线程。

一般可以使用匿名内部类创建线程:

Thread newThread = new Thread(new Runnable() {
    @Override
    public void run() {
        System.out.println("this is a thread");
    }
});
newThread.start();

同时由于Runnable接口是一个函数式接口,可以使用Lambda表达式替代:

Thread newThread = new Thread(() -> System.out.println("This is a Thread"));
newThread.start();

2、线程信息的获取和设置

Thread类有一些保存信息的属性,这些属性可以用来标识线程,显示线程的状态,控制线程的优先级:

  1. Id:保存了线程的唯一标识符
  2. Name:线程的名称
  3. Priority:线程的优先级(从1到10,1最低10最高)。需要注意的是优先级高的线程并不是一定会先执行!只是先执行的概率比较高
  4. Status:线程的状态(有6种:new 、runnable、blocked、waiting、time waiting或者terminated,可以用State.TERMINATED等枚举类型获取常量。

以上方法都提供了get方法用于查看,Name和Priority还提供了set方法用于设置,Id和Status不允许修改。

3、线程的中断

当所有的“非守护线程”运行结束时,一个java程序才结束。

  1. 我们可以通过interrupt()方法来中断一个运行中的线程
  2. 同时可以用isInterrupted()方法查看一个线程是否被中断
  3. 当一个线程被中断时,会抛出InterruptedException
  4. 与并发线程相关的Java方法都会抛出InterrupedException,如sleep()方法

4、线程的休眠和恢复

可以使用sleep()方法使线程进入休眠,接收参数为毫秒数。调用sleep()方法后,线程会被block指定长度的时间。

yield()方法和sleep()方法类似,使线程休眠,但是不能够指定时间,而且只是让同级的线程有优先执行的机会,但是不保证一定让其他同级的线程先执行。

5、等待线程的终止

当一个线程对象的join()方法被调用时,调用它的线程将被挂起,直到这个线程对象完成它的任务。join()方法还有一个重载join(milliseconds)方法,设定外层的线程阻塞得到的最大秒数。

6、守护线程

在Java中有两类线程:User Thread(用户线程)和Deamon Thread(守护线程)。

Daemon线程的作用是为其他线程的运行提供服务,同时也是优先级最低的线程。比如GC线程。除了虚拟机内部提供的守护线程以外,用户也可以自行定义守护线程:

  1. 定义一个线程(和一般的用户线程没有差别)
  2. 调用Thread类的setDaemon(true)方法,将线程转为守护线程
  3. 调用Thread类的start()方法启动线程

注意线程只能在启动之前变为守护线程,无法在启动之后修改。

7、线程中不可控异常的处理

Java中主要有两种异常:非运行时异常(Checked Exception,必须显示捕获,比如IOException)、运行时异常(Unchecked Exception,不必显示捕获,比如NumberFormatException)。

由于线程的run()方法不支持throws语句,所以当线程对象抛出Checked Exception时,必须捕获并处理。而运行时异常则会从run()方法中抛出,默认行为是在控制台打印出堆栈记录,并结束抛出异常的线程(注意不是调用该线程的线程)。

如果希望执行处理运行时异常,可以实现UncaughtExceptionHandler接口,然后用thread的setUncaughtExceptionHandler()方法注册。

public class ThreadTest {
    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(()->{
           int n = Integer.parseInt("AAA");
        });
        //注册异常处理器
        thread.setUncaughtExceptionHandler(new ExceptionHandler());
        thread.start();
    }
}
//自定义的异常处理器
class ExceptionHandler implements Thread.UncaughtExceptionHandler{
    @Override
    public void uncaughtException(Thread t, Throwable e) {
        System.out.printf("An Exception has been captured\n");
        System.out.printf("Thread: %s\n",t.getId());
        System.out.printf("Exception is: %s: %s\n",e.getClass().getName(),e.getMessage());
        System.out.println("Stack Trace: ");
        e.printStackTrace();
        System.out.printf("Thread status: %s\n",t.getState());
    }
}

8、线程局部变量(ThreadLocal)的使用

共享数据时并发程序最核心的问题之一。

如果使用一个实现了Runnable接口的类的实例,创建了多个线程对象。则这些“源自同一个Runnable”的线程对象将共享这个Runnable实例的属性。也就是说如果在一个其中一个线程中改变了一个属性,所有的线程都会被这个改变影响。

如果不希望某些属性被共享,可以考虑使用线程局部变量(Thread-Local Variable):

public class ThreadTest {
    public static void main(String[] args) throws InterruptedException {
        UnsafeTask unsafeTask = new UnsafeTask();
        for (int i = 0; i < 10; i++) {
            Thread thread = new Thread(unsafeTask);
            thread.start();
            TimeUnit.SECONDS.sleep(2);
        }
    }
}

class UnsafeTask implements Runnable{
    //使用ThreadLocal维护的变量
    private static ThreadLocal<Date> threadLocalStartDate = new ThreadLocal<Date>(){
        @Override
        protected Date initialValue() {
            return new Date();
        }
    };
    //一般的变量
    private Date startDate;
    @Override
    public void run() {
        startDate = new Date();
        System.out.println("start-"+Thread.currentThread().getName()+"-"+startDate.toString()+"-"+
        threadLocalStartDate.get());
        try {
            TimeUnit.SECONDS.sleep((int)Math.rint(Math.random()*10));
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("end-"+Thread.currentThread().getName()+"-"+startDate.toString()+"-"+threadLocalStartDate.get());
    }
}

ThreadLocal关键字的具体使用在后面的博客中介绍。

9、线程的分组

Java中可以对多个线程进行分组,以可以进行统一的调度。

public class ThreadTest {
    public static void main(String[] args) throws InterruptedException {
        Runnable runnable = ()->{
            while (true){
                System.out.println("thread active");
                try {
                    Thread.sleep(10000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        };
        //创建线程组
        ThreadGroup threadGroup = new ThreadGroup("group1");
        //创建线程的同时加入线程组
        Thread thread1 = new Thread(threadGroup,runnable);
        Thread thread2 = new Thread(threadGroup,runnable);
        //打印线程组信息
        threadGroup.list();
        //线程组中的已开启的线程数目(即使interrupt,也仍然会算进去)
        thread1.start();
        thread2.start();
        int threadNum = threadGroup.activeCount();
        System.out.println(threadNum);
        //获取线程组中的线程信息
        Thread[] threads = new Thread[threadNum];
        threadGroup.enumerate(threads);
        threads[1].interrupt();
        //关闭所有线程
        threadGroup.interrupt();
    }
}

 

© 著作权归作者所有

兴趣使然的程序员
粉丝 23
博文 112
码字总数 87412
作品 0
深圳
程序员
私信 提问
JNI 的多线程

之前的博文中讲述了JNI的基础知识: Java 类型和C/C++类型的转换 cygwin + gcc+makeFile入门(三): JNI的编译 这两篇文章讲述了JNI最普遍的两个问题, 环境的建立以及参数的传递. JNI作为连接J...

晨曦之光
2012/03/09
1K
0
ThreadLocal 基础知识

ThreadLocal是什么 · 早在JDK 1.2的版本中就提供java.lang.ThreadLocal,ThreadLocal为解决多线程程序的并发问题提供了一种新的思路。使用这个工具类可以很简洁地编写出优美的多线程程序。 ...

向阳而生
2015/11/21
0
0
一份关于 Java、Kotlin 与 Android 的学习笔记

JavaKotlinAndroidLearn 这是一份关于 Java 、Kotlin 、Android 的学习笔记,既包含对基础知识点的介绍,也包含对一些重要知识点的源码解析,笔记的大纲如下所示: Java 重拾Java(0)-基础知...

叶应是叶
2018/08/08
0
0
读书笔记之《Java并发编程的艺术》-并发编程基础

读书笔记部分内容来源书出版书,版权归本书作者,如有错误,请指正。 欢迎star、fork,读书笔记系列会同步更新 git https://github.com/xuminwlt/j360-jdk module j360-jdk-thread/me.j360....

Hi徐敏
2015/11/11
0
8
Java面试:投行的15个多线程和并发面试题

本文由ImportNew -一杯哈希不加盐 翻译自dzone。欢迎加入翻译小组。转载请见文末要求。 多线程和并发问题已成为各种 Java 面试中必不可少的一部分。如果你准备参加投行的 Java 开发岗位面试,...

ImportNew
2018/08/23
0
0

没有更多内容

加载失败,请刷新页面

加载更多

texlive安装

Installing to: D:/bin/texlive/texlive/2019Installing [001/307, time/total: ??:??/??:??]: adobemapping [2130k]Installing [002/307, time/total: 00:03/08:57]: ae [84k]Installing......

MtrS
今天
2
0
运维规范

命名规范 发布流程 监控告警 故障定位 状态 日志 监控

以谁为师
今天
2
0
约瑟夫环(报数游戏)java实现

开端 公司组织考试,一拿到考题,就是算法里说的约瑟夫环,仔细想想 以前老师将的都忘了,还是自己琢磨把~ package basic.gzy;import java.util.Iterator;import java.util.LinkedList;...

无极之岚
今天
3
0
Kernel字符设备驱动框架

Linux设备分为三大类:字符设备,块设备和网络设备,这三种设备基于不同的设备框架。相较于块设备和网络设备,字符设备在kernel中是最简单的,也是唯一没有基于设备基础框架(device结构)的...

yepanl
今天
3
0
Jenkins 中文本地化的重大进展

本文首发于:Jenkins 中文社区 我从2017年开始,参与 Jenkins 社区贡献。作为一名新成员,翻译可能是帮助社区项目最简单的方法。 本地化的优化通常是较小的改动,你无需了解项目完整的上下文...

Jenkins中文社区
昨天
4
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部