Java 并发:第二部分 - 多线程

在了解了 如何建立线程以后,咱们将会在本篇文章中了解能够对多线程作什么。
当咱们有了线程,咱们就能够对线程作以下几個操做:
一、让当前线程在x毫秒的时间内睡眠;
二、等待壹個其它的线程结束;
三、管理线程的优先级,暂停壹個线程以给予壹個其它线程运行的机会;

四、中断线程; java

咱们看看该如何去作全部这些事情。 算法

首先简单点,咱们可让壹個线程在指定数量的毫秒内睡眠。为了作到这壹点,Thread类有壹個方法sleep(long millis)。可是这個方法是静态的,因此你只能让当前线程进入睡眠状态。你不能选择那個你但愿它睡眠的线程,你惟壹的选择是当前线程: shell

Thread.sleep(1000);
让当前线程睡眠1000毫秒(也就是1秒)。可是,你必须捕获异常,InterruptedException。若是睡眠的线程被中断,这個异常就会发生,因此你能够这样作:
try {
	Thread.sleep(1000);
} catch (InterruptedException e){
	e.printStackTrace();
}
可是这不是壹個好的管理异常的方法,过壹会儿咱们将会看到如何处理这個异常。
若是你但愿更加精确,你可使用sleep()方法的重载版本,带有两個参数毫秒和纳秒的sleep()方法。这個睡眠时间的精确度依赖于系统时钟和计时器。
举個例子,若是你但愿睡眠1000毫秒1000纳秒,你能够这样作:
try {
	Thread.sleep(1000, 1000);
} catch (InterruptedException e){
	e.printStackTrace();
}
这里有個小例子来测试上述代码:
public class SleepThread {
    public static void main(String[] args) {
        System.out.println("Current time millis : " + System.currentTimeMillis());

        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("Current time millis : " + System.currentTimeMillis());

        System.out.println("Nano time : " + System.nanoTime());

        try {
            Thread.sleep(2, 5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("Nano time : " + System.nanoTime());
    }
}
在个人电脑上,上述代码的运行结果为:
Current time millis : 1273959308480
Current time millis : 1273959309480
Nano time : 5878165216075
Nano time : 5878166730976

你能够看到基于毫秒的睡眠时间很是精确,可是基于纳秒的睡眠时间粗糙的多。固然,运行结果依赖于你的电脑、你的操做系统和你的配置。
另外一方面,你能够在线程中等待着其余线程死亡。例如,您能够建立五个线程来计算各部分的结果,等这五個线程完成后,基于五個线程的结果计算最终的结果。这样作,你可使用线程类的join()方法。这种方法不是静态的,因此你能够用在任何线程中等待它死亡。当线程在等待另外一个线程中断时,在sleep()这种方法中会抛出InterruptedException异常。因此为了等待线程2,你必须这样作: 安全

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


这将迫使当前线程等待 thread2 死亡。你也能够增长超时时间,使用join(),join(long millis) 和join(long millis, int nanos)方法的重载版本,以毫秒、或者毫秒+纳秒为单位,这里有個小例子演示了全部的用法。 多线程

public class JoinThread {
	public static void main(String[] args) {
		Thread thread2 = new Thread(new WaitRunnable());
		Thread thread3 = new Thread(new WaitRunnable());

		System.out.println("Current time millis : " + System.currentTimeMillis());

		thread2.start();

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

		System.out.println("Current time millis : " + System.currentTimeMillis());

		thread3.start();

		try {
			thread3.join(1000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}

		System.out.println("Current time millis : " + System.currentTimeMillis());
	}

	private static class WaitRunnable implements Runnable {
		@Override
		public void run() {
			try {
				Thread.sleep(5000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
        }
}
以上代码在个人电脑上运行的结果以下:
Current time millis : 1274015478535
Current time millis : 1274015483538
Current time millis : 1274015484538

你能够看到第壹個 join() 等待了另壹個线程 5 秒钟,当咱们设置了壹個超时时,咱们只等待了壹秒钟就从 join 方法中返回了。
当咱们使用线程时,一样有可能更改线程的优先级。在Java虚拟机中,Thread类使用基于优先级的调度算法。因此若是壹個线程以更高的优先级进入运行态时,新的线程将会运行,而当前正在运行的线程会返回可运行态,并等待下次运行。可是这种行为是没法保证的,它彻底依赖于你使用的虚拟机。因此,不要依赖线程的优先级,只使用它来提升你的程序的性能。
一般来说,线程类的优先级是壹個从0到10的整数,可是有些虚拟机拥有更低或者更高的优先级。为了了解优先级的范围,你可使用线程类的常量: 并发

public class ThreadPriorityRange {
	public static void main(String[] args) {
		System.out.println("Minimal priority : " + Thread.MIN_PRIORITY);
		System.out.println("Maximal priority : " + Thread.MAX_PRIORITY);
		System.out.println("Norm priority : " + Thread.NORM_PRIORITY);
         }
}
在个人机器上,我老是获得以下结果:
Minimal priority : 1
Maximal priority : 10
Norm priority
若是要设置线程的优先级,你可使用 Thread 类的 setPriority(int priority) 方法。若是你传入壹個比最大优先级更大的值,这個方法就会使用最大优先级对应的值。若是你没有指定优先级,就会默认使用当前线程的优先级。
另外壹种会用到的与优先级相关的是 yield() 方法,这個方法是静态的,因此它做用于当前线程。这個方法的做用就是让线程再次进入运行状态,其它线程只能再次等待他们运行的机会。可是在实践中,这個方法的行为是没法保证的。在特定系统中,它能够实现为壹個空操做。在实践中想测试这個并不容易,由于它的运行结果然的是依赖于你的电脑,虚拟机和操做系统。实践中最好不使用线程的优先级。
最后壹件你可使用线程类作的事情是中断它。在Java中,若是壹個线程没有结束,你没有办法强制它中止,它将继续无限的执行下去。可是你可使用 Thread 类的 interrupt() 方法中断它。这個方法中断壹個线程,若是线程在睡觉或者正在加入另壹個线程,方法就会抛出壹個InterruptedException。你必须知道若是线程在睡觉或者加入另壹個线程,线程的中断状态会被清除。顾名思义,isInterrupted()方法会返回false。如下是壹個演示的小例子:
public class InterruptThread {
	public static void main(String[] args) {
		Thread thread1 = new Thread(new WaitRunnable());

		thread1.start();

		try {
			Thread.sleep(1000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}

		thread1.interrupt();
	}

	private static class WaitRunnable implements Runnable {
		@Override
		public void run() {
			System.out.println("Current time millis : " + System.currentTimeMillis());

			try {
				Thread.sleep(5000);
			} catch (InterruptedException e) {
				System.out.println("The thread has been interrupted");
				System.out.println("The thread is interrupted : " + Thread.currentThread().isInterrupted());
			}

			System.out.println("Current time millis : " + System.currentTimeMillis());
		}
        }
}
运行后产生了这样的结果:
Current time millis : 1274017633151
The thread has been interrupted
The thread is interrupted : false
Current time millis : 1274017634151
你能够看到壹秒钟以后,第二個线程被中断,它的中断状态被设置为false。若是你没有睡眠,可是作了不少繁重的工做,你能够像下面这样去测试中断,以促使你的线程正确的中断。
public class InterruptableRunnable implements Runnable {
	@Override
	public void run() {
		while(!Thread.currentThread().isInterrupted()){
			//Heavy operation
		}
	}
}
如今你知道如何中断壹個线程了,你能够想象,这种简单的捕获InterruptedException不足以让你的线程“安全中断”。想象你的线程像下面这样:
public class UglyRunnable implements Runnable {
	@Override
	public void run() {
		while(!Thread.currentThread().isInterrupted()){
			//Heavy operation
			try {
				Thread.sleep(5000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			//Other operation
		}
	}
}
如今,当你的线程在睡眠时,另外壹個线程想中断你的线程。睡眠会被中断,可是中断状态会被清除以便循环能够继续。建立壹個更好的线程的方法是,在捕获InterruptedException异常以后,再次中断线程:
public class BetterRunnable implements Runnable {
	@Override
	public void run() {
		while(!Thread.currentThread().isInterrupted()){
			//Heavy operation
			try {
				Thread.sleep(5000);
			} catch (InterruptedException e) {
				Thread.currentThread().interrupt();
			}
			//Other operation
		}
	}
}

使用这段代码,在中断以后,中断状态会被恢复,循环会中止。基于你的代码,你也能够在中断以后,在interrupt()方法后增长壹個 continue 语句来确保再也不操做。在某些状况下,你还须要使用几個if语句来检测中断状态,以控制其作或者不作某些事情。
因此,咱们如今已经知道了全部用线程能够作的事情了。我但愿大家以为这篇文章有趣。你能够在这里下载本文的源代码。
下壹篇文章是关于Java并发的,咱们将会看到如何使用同步代码来保证线程安全。 ide

本文英文原文:http://www.baptiste-wicht.com/2010/05/java-concurrency-part-2-manipulate-threads/ 性能

相关文章
相关标签/搜索