线程管理5,6,7

线程管理(五)线程的睡眠和恢复

有时, 你会感兴趣在一段确定的时间内中断执行线程。例如, 程序的一个线程每分钟检查反应器状态。其余时间,线程什么也不做。在这段时间,线程不使用任何计算机资源。过了这段时间,当JVM选择它时,线程会准备好继续执行。为达此目的,你可以使用Thread类的 sleep() 方法 。此方法接收一个整数作为参数,表示线程暂停运行的毫秒数。 在调用sleep() 方法后,当时间结束时,当JVM安排他们CPU时间,线程会继续按指令执行,

另一种可能是使用一个有TimeUnit列举元素的sleep() 方法,使用线程类的 sleep() 方法让当前线程睡眠,但是它接收的参数单位是表示并转换成毫秒的。

在这个指南中, 我们将开发一个程序使用sleep()方法来每秒写入真实的日期。

1.   创建一个类名为 FileClock,并一定实现Runnable接口。

2.   实现  run() 方法

3.   写一个10次循环的环。在每次循环,创建一个Date对象,写入文本并调用TimeUnit 类有SECONDS属性的sleep()方法来暂停线程1秒的运行。根据这个值,线程会停止将近一秒。当然 sleep() 方法 可以抛出一个 InterruptedException 异常,我们要加入捕捉代码。被中断时,解放或关闭正在使用的线程资源是好习惯。

4.   我们已经实现了线程。 现在,让我们来实现例子的主类吧。创建一个类名为 FileMain ,包含 main() 方法。

5.   创建FileClock类的对象并让一个线程执行它。然后,开始执行线程。

6.   在主线程调用TimeUnit类有SECONDS属性的 sleep() 方法来等待5秒。

7.   中断 FileClock 线程。

8.   运行例子并查看结果。

package concurrency.study.chap01;

import java.util.Date;
import java.util.concurrent.TimeUnit;

//线程管理(五)线程的睡眠和恢复
public class FileClock implements Runnable {

	@Override
	public void run() {
		for (int i = 0; i < 10; i++) {
			System.out.printf("%s\n", new Date());

			try {
				TimeUnit.SECONDS.sleep(1);
			} catch (InterruptedException e) {
				e.printStackTrace();
				System.out.printf("The FileClock has been interrupted");
			}
		}
	}

}
package concurrency.study.chap01;

import java.util.concurrent.TimeUnit;

public class FileMain {
	public static void main(String[] args) {
		FileClock clock = new FileClock();
		Thread thread = new Thread(clock);
		thread.start();

		try {
			TimeUnit.SECONDS.sleep(5);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		
		thread.interrupt();
		
	}
}

当你运行这个例子,你可以发现程序是如何每秒写入一个Date对象的,然后,有信息显示FileClock线程被中断。

当你调用sleep()方法, Thread 离开CPU并在一段时间内停止运行。在这段时间内,它是不消耗CPU时间的,使用可以执行其他任务。

当 Thread 是睡眠和中断的时候,那方法会立刻抛出InterruptedException异常并不会一直等到睡眠时间过去。

Java 并发 API 有另一种方法能让线程对象离开 CPU。它是 yield() 方法, 它向JVM表示线程对象可以让CPU执行其他任务。JVM 不保证它会遵守请求。通常,它只是用来试调的。

线程管理(六)等待线程的终结

在某些情况下,我们需要等待线程的终结。例如,我们可能会遇到程序在执行前需要初始化资源。在执行剩下的代码之前,我们需要等待线程完成初始化任务。为达此目的, 我们使用Thread 类的join() 方法。当前线程调用某个线程的这个方法时,它会暂停当前线程,直到被调用线程执行完成。

在这个指南中, 我们将学习用初始化例子来使用这个方法。

1.   创建一个类名为 DataSourcesLoader 并一定实现Runnable接口。

2.   实现run()方法。 它写信息来表明它开始运行,然后睡眠4秒,最后再写信息表明它结束运行。

3.   创建一个类名为 NetworkConnectionsLoader 并一定要Runnable接口。实现run()方法。它将与DataSourcesLoader类的run()方法一样,但是它会睡眠6秒。

4.   现在, 创建一个类名为 Main,包含 main()方法。

5.   创建一个 DataSourcesLoader 类对象并让线程运行它。

6.    创建一个 NetworkConnectionsLoader 类的对象并让线程运行它。

7.    让2个线程对象都调用 start() 方法

8.   2个线程都使用 join() 方法等待终结。 此方法可以抛出InterruptedException 异常, 所以要包含捕捉代码。

9.   写一条信息表明程序结束。

package concurrency.study.chap01;

import java.util.Date;
import java.util.concurrent.TimeUnit;

public class DataSourcesLoader implements Runnable {

	@Override
	public void run() {
		System.out.printf("Beginning data sources loading: %s\n", new Date());
		try {
			TimeUnit.SECONDS.sleep(4);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		System.out.printf("Data sources loading has finished:%s\n", new Date());
	}
}
package concurrency.study.chap01;

import java.util.Date;

public class DataSourcesLoaderMain {
	public static void main(String[] args) {
		DataSourcesLoader dsLoader = new DataSourcesLoader();
		Thread thread1 = new Thread(dsLoader);

		NetworkConnectionsLoader ncLoader = new NetworkConnectionsLoader();
		Thread thread2 = new Thread(ncLoader);

		thread1.start();
		thread2.start();

		try {
			thread1.join();
			thread2.join();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}

		System.out.printf("Main: Configuration has been loaded: %s\n", new Date());
	}
}
package concurrency.study.chap01;

import java.util.Date;
import java.util.concurrent.TimeUnit;

public class NetworkConnectionsLoader implements Runnable {
	@Override
	public void run() {
		System.out.printf("Beginning data sources loading: %s\n", new Date());
		try {
			TimeUnit.SECONDS.sleep(6);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		System.out.printf("Data sources loading has finished:%s\n", new Date());
	}
}

当你运行这个程序时,你可以发现2个线程对象都开始他们的执行。首先, DataSourcesLoader 结束它的运行。然后, NetworkConnectionsLoader 类结束它的运行,同时,主线程对象继续运行并写下了最后的信息。

Java 提供2种形式的 join() 方法:

  • join (long milliseconds)
  • join (long milliseconds, long nanos)

第一种join() 方法, 这方法让调用线程等待特定的毫秒数。例如,如果thread1对象使用代码thread2.join(1000), 那么线程 thread1暂停运行,直到以下其中一个条件发生:

  • thread2 结束运行
  • 1000 毫秒过去了

当其中一个条件为真时,join() 方法返回。

第二个版本的 join() 方法和第一个很像,只不过它接收一个毫秒数和一个纳秒数作为参数。

线程管理(七)守护线程的创建和运行

Java有一种特别的线程叫做守护线程。这种线程的优先级非常低,通常在程序里没有其他线程运行时才会执行它。当守护线程是程序里唯一在运行的线程时,JVM会结束守护线程并终止程序。

根据这些特点,守护线程通常用于在同一程序里给普通线程(也叫使用者线程)提供服务。它们通常无限循环的等待服务请求或执行线程任务。它们不能做重要的任务,因为我们不知道什么时候会被分配到CPU时间片,并且只要没有其他线程在运行,它们可能随时被终止。JAVA中最典型的这种类型代表就是垃圾回收器。

在这个指南中, 我们将学习如何创建一个守护线程,开发一个用2个线程的例子;我们的使用线程会写事件到queue, 守护线程会清除queue里10秒前创建的事件。

1.   创建 Event 类. 这个类只是用来储存我们程序里的工作的事件信息。声明2个属性,一个是java. util.Date 类型的 date 和另一个是String 类型的event 。并生成它们的读值和写值方法。

2.   创建 WriterTask 类并实现Runnable接口。

3.   声明queue,储存事件并实现类的构造函数,初始化queue。

4.   实现这个任务的 run() 方法 。 此方法有100个循环。在每个循环中我们会创建 一个Event对象,并保存到 queue里, 然后休眠1秒。

5.   创建 CleanerTask 类并一定扩展Thread类。

6.   声明 queue,储存事件并实现类的构造函数,初始化queue,在这个构造函数,用setDaemon() 方法让此线程成为守护线程。

7.   实现run()方法。它是无限循环来获取当前日期并调用 clean() 方法.

8.   实现 clean() 方法. 它获取最后的事件,如果它在10秒前被创建,就删除它并查看下一个事件。如果一个事件被删除,它会写一个事件信息和queue的新的大小,为了让你看到变化过程。

9.   现在实现主类。 创建一个类名为 Main 和 main() 方法。

10. 创建使用 Deque 类的queue 来保存事件。

11. 创建 和开始3个 WriterTask 线程和一个 CleanerTask.

package concurrency.study.chap01;

import java.util.Date;
import java.util.Deque;

public class CleanerTask extends Thread {

	private Deque<Event> deque;

	public CleanerTask(Deque<Event> deque) {
		this.deque = deque;
		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());
		}
	}
}
package concurrency.study.chap01;

import java.util.Date;
import java.util.Deque;
import java.util.concurrent.TimeUnit;

public class WriterTask implements Runnable {
	private Deque<Event> deque;

	public WriterTask(Deque<Event> deque) {
		this.deque = deque;
	}

	@Override
	public void run() {
		for (int i = 1; i < 100; i++) {
			Event event = new Event();
			event.setDate(new Date());
			event.setEvent(String.format("The thread %s has generated an   event", Thread.currentThread().getId()));
			deque.addFirst(event);
			try {
				TimeUnit.SECONDS.sleep(1);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}

}
package concurrency.study.chap01;

import java.util.ArrayDeque;
import java.util.Deque;

public class MainTask {

	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();
	}
}
package concurrency.study.chap01;
import java.util.Date;
public class Event {
	Date date;
	String evnent;
	public Date getDate() {
		return date;
	}
	public void setDate(Date date) {
		this.date = date;
	}
	public String  getEvent() {
		// TODO Auto-generated method stub
		return evnent;
	}

	public void setEvent(String format) {
	
		this.evnent = format;
		
	}		
}

如果分析这个程序的输出,你可以发现queue可以一直增加直到它有30个事件,然后它的大小会在27-30之间直到运行结束。

程序开始时有3个 WriterTask 线程。每个线程写一个事件然后休眠1秒。10秒之后,我们有30个事件在queue里。在这10秒内,当3个 WriterTask 线程休眠时, CleanerTasks已经开始运行,但是它没有删除任何事件,因为所有事件都才生成不到10秒。在剩下的运行里,CleanerTask 每秒删除3个事件, 然而3个 WriterTask 线程会另写3个,所以queue的大小在27-30之间。

你可以修改 WriterTask 线程的休眠时间。如果你使用一个较小的值,你会发现CleanerTask 被分配到 CPU 时间片会更少,由于 CleanerTask 没有删除任何事件,所以queue大小会一直增加。

只能在start() 方法之前可以调用 setDaemon() 方法。一旦线程运行了,就不能修改守护状态。

可以使用 isDaemon() 方法来检查线程是否是守护线程(方法返回 true) 或者是使用者线程 (方法返回 false)。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值