Java并发之线程管理(线程基础知识)
线程的创建和运行
1、继承Thread类,并且覆盖run()方法。
2、实现Runnable接口类,使用带参数的Thread构造器来创建Thread对象,参数就是Runnable接口的一个对象。
那么创建完了怎么运行呢?调用run()方法?调用run()方法只是简单的 类对象调用自己的成员方法,那么怎么会开启线程呢?而且每一个线程还有自己的信息(线程名字,线程ID,优先级等)。那么应该是怎么运行呢?答案是调用start()方法。
- 继承Thread类
//创建
public class Thread2 extends Thread{
@Override
public void run() {
System.out.println("继承Thread类创建线程");
}
}
//运行
private static void createThread2() {
Thread2 t2 = new Thread2();
Thread thread = new Thread(t2);
thread.start();
}
- 实现Runnable接口
//创建
public class Thread1 implements Runnable {
@Override
public void run() {
System.out.println("实现Runnable接口创建线程");
}
}
//运行
private static void createThread1() {
Runnable t1 = new Thread1();
Thread thread = new Thread(t1);
thread.start();
}
线程信息的获取和设置
- ID:保存线程的唯一标识符
- Name:线程名称
- Priority:线程对象的优先级,线程优先级从1-10,1是最低优先级,10是最高优先级。
- Status:线程状态。在Java中,线程的状态有6种:new(创建)、runnbale(运行)、blocked(阻塞)、waiting(等待)、time waiting和terminated(终止)。状态转换图:
public enum State {
NEW,
RUNNABLE,
BLOCKED,
WAITING,
TIMED_WAITING,
TERMINATED;
}
线程的中断
- 调用interrupt()方法,当前执行的线程就会被中断。
task.interrupt();
判断一个线程是否被中断了,有两种方式:
- interrupted(),检查当前执行的线程是否被中断
- isInterrupted():当interrupt()方法被调用时,Thread类中表名线程是否被中断的属性会被设置为true。isInterrupted()方法返回这个属性的值。
推荐使用isInterrupted(),因为interrupted()方法是一个静态方法。
public static boolean interrupted() {
return currentThread().isInterrupted(true);
}
线程中断的控制
@Override
public void run() {
File file = new File(initPath);
if (file.isDirectory()) {
try {
dirctoryProcess(file);
} catch (InterruptedException e) {
System.out.printf("%s:the search has been interrupted", Thread.currentThread().getName());
}
}
}
private void dirctoryProcess(File file) throws InterruptedException {
File[] list = file.listFiles();
if (list != null) {
for (int i = 0; i < list.length; i++) {
if (list[i].isDirectory()) {
dirctoryProcess(list[i]);
} else {
fileProcess(list[i]);
}
}
}
if (Thread.interrupted()) {
throw new InterruptedException();
}
}
线程的休眠和恢复
- 休眠指定时间后线程自动恢复
- 线程休眠的方式有两种:
- Thread.sleep(1000);//休眠一秒,其中这里的单位为ms
- TimeUnit.SECONDS.sleep(1);//休眠一秒,这里的单位为s
等待线程的终止(join())
public class DataSourceLoader implements Runnable {
@Override
public void run() {
System.out.printf("Begining data sources loading: %s\n",new Date());
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.printf("Data Sources loading has finised:%s\n",new Date());
}
}
public class NetWorkConnectionLoader implements Runnable {
@Override
public void run() {
System.out.printf("Begining data sources loading: %s\n",new Date());
try {
TimeUnit.SECONDS.sleep(6);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.printf("Data Sources loading has finised:%s\n",new Date());
}
}
public class Main {
public static void main(String[] args) {
DataSourceLoader dsLoader = new DataSourceLoader();
Thread t1 = new Thread(dsLoader,"DataSourceThread");
NetWorkConnectionLoader ncLoader = new NetWorkConnectionLoader();
Thread t2 = new Thread(ncLoader,"NetWorkConnectionThread");
t1.start();
t2.start();
try {
t1.join();
t2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.printf("Main: Configuration has been loaded:%s\n",new Date());
}
}
Begining data sources loading: Tue Apr 25 14:46:39 CST 2017
Begining data sources loading: Tue Apr 25 14:46:39 CST 2017
Data Sources loading has finised:Tue Apr 25 14:46:42 CST 2017
NetWorkConnectionLoader loading has finised:Tue Apr 25 14:46:45 CST 2017
Main: Configuration has been loaded:Tue Apr 25 14:46:45 CST 2017
Main: Configuration has been loaded:Tue Apr 25 14:46:45 CST 2017
- join(long milliseconds)
- join(long milliseconds,long nanos)
当一个线程调用其他线程的join()方法时,如果使用的是第一种join()方式,那么它不必等到被调用线程运行终止,如果参数指定的毫秒时钟已经到达,它将继续运行。例如:thread1中有这样的代码thread2.join(1000),thread1将挂起,知道满足下面两个条件之一:
- thread2运行已经完成
- 时钟已经过去了1000毫秒
当两个条件中的任何一个成立是,join()方法将返回。
public final void join() throws InterruptedException {
join(0);
}
public final synchronized void join(long millis)
throws InterruptedException {
long base = System.currentTimeMillis();
long now = 0;
if (millis < 0) {
throw new IllegalArgumentException("timeout value is negative");
}
if (millis == 0) {
while (isAlive()) {
wait(0);
}
} else {
while (isAlive()) {
long delay = millis - now;
if (delay <= 0) {
break;
}
wait(delay);
now = System.currentTimeMillis() - base;
}
}
}
守护线程的创建和运行
public class WriterTask implements Runnable {
Deque<Event> deque; public WriterTask (Deque<Event> deque){
this.deque=deque;
} @Override
public void run() { // Writes 100 events
for (int i=1; i<100; i++) {
// Creates and initializes the Event objects
Event event=new Event();
event.setDate(new Date());
event.setEvent(String.format("The thread %s has generated an event",Thread.currentThread().getId())); // Add to the data structure
deque.addFirst(event);
try {
// Sleeps during one second
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public class Event { private Date date;
private String event; public Date getDate() {
return date;
} public void setDate(Date date) {
this.date = date;
} public String getEvent() {
return event;
} public void setEvent(String event) {
this.event = event;
}
}
public class CleanerTask extends Thread {
private Deque<Event> deque; public CleanerTask(Deque<Event> deque) {
this.deque = deque;
// Establish that this is a Daemon Thread
setDaemon(true);
} @Override
public void run() {
while (true) {
Date date = new Date();
clean(date);
}
} private void clean(Date date) {
long difference;
boolean delete; if (deque.size()==0) {
return;
} delete=false;
do {
Event e = deque.getLast();
difference = date.getTime() - e.getDate().getTime();
if (difference > 10000) {
System.out.printf("Cleaner: %s\n",e.getEvent());
deque.removeLast();
delete=true;
}
} while (difference > 10000);
if (delete){
System.out.printf("Cleaner: Size of the queue: %d\n",deque.size());
}
}
}
public static void main(String[] args) { Deque<Event> deque=new ArrayDeque<Event>(); WriterTask writer=new WriterTask(deque);
for (int i=0; i<3; i++){
Thread thread=new Thread(writer);
thread.start();
} CleanerTask cleaner=new CleanerTask(deque);
cleaner.start();
}
线程中不可控异常的处理
- 受检异常(非运行时异常)(Checked Exception)
- 非受检异常(运行时异常)(UnChecked Exception)
由于线程的run()方法不能接受抛出异常,对于受检异常来说,可以在编写程序时捕获,对于非受检异常来说,因为不知道会不会抛出异常,这样就比较麻烦。好在Java提供了一种在线程对象里捕获和处理运行时异常的一种机制。具体如下:
public class ExceptionHandler implements 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: %s: %s\n",e.getClass().getName(),e.getMessage());
System.out.printf("Stack Trace: \n");
e.printStackTrace(System.out);
System.out.printf("Thread status: %s\n",t.getState());
}
}
@Override
public void run() {
// The next instruction always throws and exception
int numero=Integer.parseInt("TTT");
}
public static void main(String[] args) {
Task task=new Task();
Thread thread=new Thread(task);
thread.setUncaughtExceptionHandler(new ExceptionHandler());
thread.start();
try {
thread.join();
} catch (InterruptedException e) {
e.printStackTrace();
} System.out.printf("Thread has finished\n");
}
An exception has been captured
Thread: 10
Exception: java.lang.NumberFormatException: For input string: "TTT"
Stack Trace:
java.lang.NumberFormatException: For input string: "TTT"
at java.lang.NumberFormatException.forInputString(NumberFormatException.java:65)
at java.lang.Integer.parseInt(Integer.java:580)
at java.lang.Integer.parseInt(Integer.java:615)
at com.packtpub.java7.concurrency.chapter1.recipe8.task.Task.run(Task.java:16)
at java.lang.Thread.run(Thread.java:745)
Thread status: RUNNABLE
Thread has finished
Exception in thread "Thread-0" java.lang.NumberFormatException: For input string: "TTT"
at java.lang.NumberFormatException.forInputString(NumberFormatException.java:65)
at java.lang.Integer.parseInt(Integer.java:580)
at java.lang.Integer.parseInt(Integer.java:615)
at com.packtpub.java7.concurrency.chapter1.recipe8.task.Task.run(Task.java:16)
at java.lang.Thread.run(Thread.java:745)
Thread has finished
线程局部变量的使用(ThreadLocal)
- 设置临界区,保证临界区里的数据一次只能有一个线程访问
- 为每个线程维护一个该共享数据的局部变量,这样,每个线程各自使用自己的局部变量。ThreadLocal就是这种思路的实现。
- get():返回此线程局部变量的当前线程副本中的值。
- set():将次线程局部变量的当前线程副本中的值设置为指定值。
- remove():移除此线程局部变量当前线程的值。
- initialValue():返回次线程局部变量的当前线程的“初始值”。线程第一次使用get()方法访问变量时将调用此方法,但如果线程之前调用了set(T)方法,则不会对该线程再调用initialValue()方法。通常,此方法对每个线程最多调用一次,但如果在调用get()后又调用了remove(),则可能再次调用此方法。
使用方式如下:
public class SafeTask implements Runnable { private static ThreadLocal<Date> startDate= new ThreadLocal<Date>() {
protected Date initialValue(){
return new Date();
}
}; @Override
public void run() {
// Writes the start date
System.out.printf("Starting Thread: %s : %s\n",Thread.currentThread().getId(),startDate.get());
try {
TimeUnit.SECONDS.sleep((int)Math.rint(Math.random()*10));
} catch (InterruptedException e) {
e.printStackTrace();
}
// Writes the start date
System.out.printf("Thread Finished: %s : %s\n",Thread.currentThread().getId(),startDate.get());
}
}
线程的分组
ThreadGroup threadGroup = new ThreadGroup("Searcher");
SearchTask searchTask=new SearchTask(result);
for (int i=0; i<5; i++) {
Thread thread=new Thread(threadGroup, searchTask);
thread.start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
private static void waitFinish(ThreadGroup threadGroup) {
while (threadGroup.activeCount()>9) {
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
threadGroup.interrupt();
线程组中不可控异常的处理
public void setUncaughtExceptionHandler(UncaughtExceptionHandler eh) {
checkAccess();
uncaughtExceptionHandler = eh;
}
class ThreadGroup implements Thread.UncaughtExceptionHandler {
public class MyThreadGroup extends ThreadGroup {
public MyThreadGroup(String name) {
super(name);
}
@Override
public void uncaughtException(Thread t, Throwable e) {
}
}
使用工厂类创建线程
public interface ThreadFactory {
Thread newThread(Runnable r);
}
public class Thread1 implements Runnable{
@override
public void run(){
}
}
Runnable t1 = new Thread1();
Thread t = new Thread(t1);
t.start();
public class MyThreadFactory implements ThreadFactory {
@Override
public Thread newThread(Runnable r) {
Thread t=new Thread(r,"thread_name");
return t;
}
}
Task task = new Task();
Thread thread=factory.newThread(task);
- 更容易修改类,或者改变创建对象的方式
- 更容易为有限资源创建对象的数目,可以限制一个类型的对象的个数
- 更容易为创建的对象生成统计数据
Java并发之线程管理(线程基础知识)的更多相关文章
- Java做acm所需要的基础知识之排序问题
Java做acm所需要的基础知识. 以前做acm的题都是用C/C++来写代码的,在学习完Java之后突然感觉Java中的方法比C/C++丰富很多,所以就整理一下平时做题需要用到的Java基础知识. 1 ...
- Java基础之线程——管理线程同步方法(BankOperation2)
控制台程序. 当两个或多个线程共享同一资源时,例如文件或内存块,就需要采取措施,确保其中的一个线程不会修改另一个线程正在使用的资源.当其中的一个线程更新文件中的某个记录,同时另一个线程正在检索这个记录 ...
- 别指望一文读懂Java并发之从一个线程开始
Understanding concurrent programming is on the same order of difficulty as understanding object-orie ...
- java笔记--用ThreadLocal管理线程,Callable<V>接口实现有返回值的线程
用ThreadLocal管理线程,Callable<V>接口实现有返回值的线程 ThreadLocal在我的笔记"关于线程同步"的第5种方式里面有介绍,这里就不多说了. ...
- java并发之如何解决线程安全问题
并发(concurrency)一个并不陌生的词,简单来说,就是cpu在同一时刻执行多个任务. 而Java并发则由多线程实现的. 在jvm的世界里,线程就像不相干的平行空间,串行在虚拟机中.(当然这是比 ...
- java第九节 网络编程的基础知识
/** * * 网络编程的基础知识 * 网络协议与TCP/IP * IP地址和Port(端口号) * 本地回路的IP地址:127.0.0.1 * 端口号的范围为0-65535之间,0-1023之间的端 ...
- 学 Java 网络爬虫,需要哪些基础知识?
说起网络爬虫,大家想起的估计都是 Python ,诚然爬虫已经是 Python 的代名词之一,相比 Java 来说就要逊色不少.有不少人都不知道 Java 可以做网络爬虫,其实 Java 也能做网络爬 ...
- (转)JAVA AJAX教程第二章-JAVASCRIPT基础知识
开篇:JAVASCRIPT是AJAX技术中不可或缺的一部分,所以想学好AJAX以及现在流行的AJAX框架,学好JAVASCRIPT是最重要的.这章我给大家整理了一些JAVASCRIPT的基础知识.常用 ...
- Java基础之线程——管理线程同步代码块(BankOperation4)
控制台程序. 除了同步类对象的方法之外,还可以把程序中的语句或代码块制定为synchronized,这种方式更强大,因为可以指定哪个对象从语句或代码块的同步中获益,而不像同步方法那样仅仅是包含代码的对 ...
- iOS/OS X线程安全的基础知识
处理多并发和可重入性问题,是每个库发展过程中面临的比较困难的挑战之一.在Parse平台上,我们尽最大的努力保证你在使用我的SDKs时所做的操作都是线程安全的,保证不会出现性能问题. 在这篇文章中我们将 ...
随机推荐
- 吾八哥学Python(一):搭建Python开发环境(Windows)
学习Python的第一步当然是要配置一下开发环境了,这里记录一下本人在windows 10(64位)下配置Python开发环境的过程,供跟我一样的新手参考一下. 一.下载Python安装包 目前最新的 ...
- sqlite3基本相关使用
闲来无事,复习和总结了一下之前学习到的关于sqlite3数据库的相关知识: [1] sqlite3的安装:1.离线安装:sudo dpkg -i *.deb2.在线安装:sudo apt-get in ...
- 张高兴的 Windows 10 IoT 开发笔记:ADXL345 加速度传感器
GitHub : https://github.com/ZhangGaoxing/windows-iot-demo/tree/master/ADXL345Demo
- 填个小坑,Vue不支持IE8及以下,跨域ajax不支持IE9
这特么就尴尬了,说好的Vue支持IE8及以下的呢,引入jquery,测试IE个浏览器,IE9仍然显示不正常, 然而命令行测试Vue仍然存在, 数据回不来!数据回不来!数据回不来! 好吧 肉包子打狗$ ...
- JS中最经典的全局变量和局部变量问题
话不多说,直接上例子: 1.程序的运行结果为:100 10 100 var a = 10; function test(){ a = 100; console.log(a); console.lo ...
- WordPress 4.8 安装配置教程 (基于 centos 7.3, php 7.0, mysql 5.7.19, nginx 1.12.1)
最近想要整个 blog,记录自己工作.学习中的点滴.Wordpress 自然是首选,因为内容才是关键,所以也就不怕别人说太 low.网上大部份都是讲 wordpress 配合 apache 的安装教程 ...
- Oracle学习笔记之游标详解
游标 游标存在意义:解决"select *"返回空.多行记录问题,但凡select,就可能多行结果集,也就需要用游标. 游标分4步走:cursor.open.fetch.close ...
- Tomcat 笔记-设置虚拟主机
通过作用虚拟主机,可以使多个不同域名的网站共存于一个Tomcat中 在tomcat的server.xml文件中添加主机名: <Host name="hostname" app ...
- 【ASP.NET MVC 学习笔记】- 05 依赖注入工具Ninject
本文参考:http://www.cnblogs.com/willick/p/3223042.html 1.Ninject是一款轻量级的DI工具,可通过VS的插件NuGet将其引用到项目中. 2.使用N ...
- 容器与Docker简介(一)——微软微服务电子书翻译系列
前不久参加了深圳的Azure开源者峰会,会上张善友张老师推荐了微软的一个架构网站:.NET Application Architecture 这几天正好工作比较闲,看了下里面关于微服务架构的介绍,非常 ...