(一)java多线程之Thread
本人邮箱: kco1989@qq.com
欢迎转载,转载请注明网址 http://blog.csdn.net/tianshi_kco
github: https://github.com/kco1989/kco
代码已经全部托管github有需要的同学自行下载
Thread类
学习java线程的开发者,首先遇到的第一个类就是Thread,通过使用Thread类,我们就可以启动,停止,中断一个线程. 在同一个时间片里, 可能会有多个线程在执行, 每个线程都拥有它自己的方法调用堆栈, 参数和变量.每个app至少会有一个线程--主线程(main thread).
创建一个线程
java创建线程有两种方式
- 创建一个继承
Thread
的子类,并实现run
方法 - 使用
Thread
的构造方法public Thread(Runnable target)
创建,这个需要传入一个实现Runnable接口的子类
实现
下面我们分别以这两种方式实现一下.
- 编写SubThread继承Thread,并覆盖run方法 SubThread.java
public class SubThread extends Thread{
@Override
public void run() {
for (int i = 0; i < 10; i ++){
System.out.println(Thread.currentThread().getName() + ":" + i);
}
}
public static void main(String[] args) {
System.out.println("begin main");
SubThread sub = new SubThread();
sub.start();
System.out.println("end main");
}
}
- 编写SubRunnable实现Runnable,然后使用构造器Thread(Runnable) 创建一个线程
public class SubRunnable implements Runnable {
@Override
public void run() {
for (int i = 0; i < 10; i ++){
System.out.println(Thread.currentThread().getName() + ":" + i);
}
}
public static void main(String[] args) {
System.out.println("begin main");
Thread thread = new Thread(new SubRunnable());
thread.start();
System.out.println("end main");
}
}
区别
- 使用第一种方法创建的话,你可以在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
,例如:
public class StopThread1 {
public static void main(String[] args) throws InterruptedException {
System.out.println("begin main");
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 10; i ++){
try {
Thread.sleep(100);
System.out.println(Thread.currentThread().getName() + ":" + i);
} catch (InterruptedException e) {
break;
}
}
}
});
thread.start();
System.out.println("main sleep 500ms");
Thread.sleep(500);
thread.interrupt();
System.out.println("end main");
}
}
在调用
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
则停止线程,否则继续
public class StopThread2 {
public static class StopRunnable implements Runnable{
private boolean isStop = false;
public void setStop(){
this.isStop = true;
}
@Override
public void run() {
int count = 0;
while (!isStop){
try {
Thread.sleep(100);
System.out.println(Thread.currentThread().getName() + ":" + count++);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public static void main(String[] args) throws InterruptedException {
System.out.println("begin main");
StopRunnable stop = new StopRunnable();
Thread thread = new Thread(stop);
thread.start();
Thread.sleep(200);
stop.setStop();
System.out.println("end main");
}
}
线程的属性
- 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的资源回收则是一个守护线程.
public class TestDaemonThread {
public static void main(String[] args) {
System.out.println("start main");
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 10; i ++){
try {
System.out.println(Thread.currentThread().getName() + ":" + i);
Thread.sleep(10);
} catch (Exception e) {
e.printStackTrace();
}
}
}
});
thread.start();
System.out.println("end main");
}
}
该例子中,程序必须等到主线程和子线程同时执行完成才会停止,因为默认创建的线程都是非守护线程,如果在
thread.start();
前加入thread.setDaemon(true);
, 那么程序不会等子线程执行完才结束程序的.
Thread.join()
等到某线程执行完毕才开始执行,如果调用Thread.join(long)
则表示等到某线程执行完毕或指定的超时时间结束后才开始执行
public class ThreadJoinTest {
public static void main(String[] args) throws InterruptedException {
Thread thread1 = new Thread(() -> {
for (int i = 0;i < 10; i ++){
try {
Thread.sleep(10);
System.out.println(Thread.currentThread().getName() + ":" + i);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "thread1");
Thread thread2 = new Thread(() -> {
try {
thread1.join();
for (int i = 0;i < 10; i ++){
Thread.sleep(10);
System.out.println(Thread.currentThread().getName() + ":" + i);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}, "thread2");
thread1.start();
thread2.start();
}
}
上面的例子,
thread2
线程会等thread1
执行完之后才开始执行
Thread.yield
这个方法标识当前线程会按时线程调度者让其他线程先执行.但CPU是否让其他线程优先执行,还是要看CPU的心情了.
线程的异常
如果线程发现一些运行时异常而没有在run
方法俘获,会怎么办?
程序会打印出一推错误堆栈,如果我们先把线程的错误按照某种可读的方式打印到问题,但又不想在每个
run
方法中增加try{...}catch(Exception e){...}
怎么办?
我们查看Thread
类的源码发现,在Thread
中有一个内部接口UncaughtExceptionHandler
,这个正是我们所需要的.实现这个接口,并调用Thread.setUncaughtExceptionHandler
,那么但线程出现时,则会回调uncaughtException
方法
public class ThreadExceptionTest {
public static void main(String[] args) throws InterruptedException {
System.out.println("begin main");
Thread thread = new Thread(() -> {
int i = 1 / 0;
},"myThread");
thread.setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
@Override
public void uncaughtException(Thread t, Throwable e) {
System.out.println(String.format("%s发生异常%s", t.getName(), e.getMessage()));
}
});
thread.start();
System.out.println("end main");
}
}
打赏
如果觉得我的文章写的还过得去的话,有钱就捧个钱场,没钱给我捧个人场(帮我点赞或推荐一下)
(一)java多线程之Thread的更多相关文章
- Java多线程之Thread、Runnable、Callable及线程池
一.多线程 线程是指进程中的一个执行流程,一个进程中可以有多个线程.如java.exe进程中可以运行很多线程.进程是运行中的程序,是内存等资源的集合,线程是属于某个进程的,进程中的多个线程共享进程中的 ...
- Java多线程之Runnable与Thread
Java多线程之Thread与Runnable 一.Thread VS Runnable 在java中可有两种方式实现多线程,一种是继承Thread类,一种是实现Runnable接口:Thread类和 ...
- Java基础-进程与线程之Thread类详解
Java基础-进程与线程之Thread类详解 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.进程与线程的区别 简而言之:一个程序运行后至少有一个进程,一个进程中可以包含多个线程 ...
- JAVA多线程之wait/notify
本文主要学习JAVA多线程中的 wait()方法 与 notify()/notifyAll()方法的用法. ①wait() 与 notify/notifyAll 方法必须在同步代码块中使用 ②wait ...
- JAVA多线程之volatile 与 synchronized 的比较
一,volatile关键字的可见性 要想理解volatile关键字,得先了解下JAVA的内存模型,Java内存模型的抽象示意图如下: 从图中可以看出: ①每个线程都有一个自己的本地内存空间--线程栈空 ...
- java多线程之yield,join,wait,sleep的区别
Java多线程之yield,join,wait,sleep的区别 Java多线程中,经常会遇到yield,join,wait和sleep方法.容易混淆他们的功能及作用.自己仔细研究了下,他们主要的区别 ...
- JAVA多线程之UncaughtExceptionHandler——处理非正常的线程中止
JAVA多线程之UncaughtExceptionHandler——处理非正常的线程中止 背景 当单线程的程序发生一个未捕获的异常时我们可以采用try....catch进行异常的捕获,但是在多线程环境 ...
- java多线程之wait和notify协作,生产者和消费者
这篇直接贴代码了 package cn.javaBase.study_thread1; class Source { public static int num = 0; //假设这是馒头的数量 } ...
- Java多线程之ConcurrentSkipListMap深入分析(转)
Java多线程之ConcurrentSkipListMap深入分析 一.前言 concurrentHashMap与ConcurrentSkipListMap性能测试 在4线程1.6万数据的条件下, ...
随机推荐
- 使用js实现ajax的get请求步骤
(以下内容非原创,视频整合得来的) 1.创建XMLHttpRequest对象 2.浏览器与服务器建立连接 3.浏览器向服务器发送请求 4.服务器向浏览器响应请求 下面给出一个实例 1.创建一个test ...
- JavaWeb从0开始学(二)-----JSP基本语法与编译指令
在上一节中我们学习了如何搭建一个简单的Web应用,并且已经知晓了一个JSP页面主要由静态的HTML内容和动态的Java脚本共同组成.JSP的基本语法共有JSP注释.JSP声明.输出JSP表达式与JSP ...
- apply()
apply() 1.apply和call的区别在哪里 2.什么情况下用apply,什么情况下用call 3.apply的其他巧妙用法(一般在什么情况下可以使用apply) apply:方法能劫持另外一 ...
- TCP的连接和建立 图解
前言 在没有理解TCP连接是如何建立和终止之前,我想你可能并不会使用connect,accept,close这三个函数并且使用netstat程序来调试应用.所以掌握TCP连接的建立和终止势在必行. 三 ...
- 标准之路网站上一篇文章《十天学会web标准(div+css)》的营养精华
以下精华出自如下链接,“http://www.aa25.cn/special/10day/index.shtml”,<十天学会web标准(DIV+CSS)>. 这个内容不要删掉:“< ...
- select count(*)优化 快速得到总记录数
1.select count(*) from table_name 比select count(主键列) from table_name和select count(1) from table_name ...
- nvcc编译器选项及配置
nvcc命令选项: 选项命令有长名和短名,通常我们使用是用短名,长名主要用于描述. 1.指定编译阶段 主要指定编译的阶段以及要编译的输入文件. -cuda -cubin -fatbin -ptx ...
- Android 5.0及以上版本使用webview不能存储第三方Cookies解决方案
Android 5.0以上的手机使用原生WebView浏览网页,在进行登录的时候会提示验证码错误,通过查找5.0以上系统的api文档,发现5.0以上版本的webview做了较大的改动,如:同步cook ...
- MIT 计算机科学及编程导论 Python 笔记 1
计算机科学及编程导论在 MIT 的课程编号是 6.00.1,是计算机科学及工程学院的经典课程.之前,课程一直使用 Scheme 作为教学语言,不过由于 Python 简单.易学等原因,近年来已经改用 ...
- Android 中基于 Binder的进程间通信
摘要:对 Binder 工作机制进行了分析. 首先简述 Android 中 Binder 机制与传统的 Linux 进程间的通信比较,接着对基于 Binder 进程间通信的过程分析 最后结合开发实例 ...