为什么要使用多线程编程
- 更多的处理器核心
- 更快的相应时间
- 更好的编程模型
优先级
java线程的优先级为1-10,默认为5,可以通过setPriority()来设置。Io型的线程应该设置更高的优先级,计算型的线程应该设置较低的优先级,需要注意的是程序的正确性和线程的优先级的高低无关
java线程的状态
NEW RUNNABLE BLOCKED WAITING TIME_WAITING TERMINATED
Daemon线程
Daemon线程是一种支持性的线程,主要用于后台的调度,如果虚拟机中不存在非daemon的线程,虚拟机将会退出。所以不能依靠finally块中的内容来确定执行关闭或者清理资源的逻辑
package chapter04;
/**
* 6-5
*/
public class Daemon {
public static void main(String[] args) {
Thread thread = new Thread(new DaemonRunner());
thread.setDaemon(true);
thread.start();
}
static class DaemonRunner implements Runnable {
@Override
public void run() {
try {
SleepUtils.second(100);
} finally {
System.out.println("DaemonThread finally run.");
}
}
}
}
程序执行后并没有任何输出
对线程进行中断,抛出异常的是休眠的线程
suspend(),resume(),stop()等方法已经过时,常常使用等待通知机制来替代
如何安全的终止线程
通过中断或者标志位的方式能够使得线程终止时有机会去清理资源,而不是武断的将线程停止
package chapter04;
import java.util.concurrent.TimeUnit;
/**
* 6-9
*/
public class Shutdown {
public static void main(String[] args) throws Exception {
Runner one = new Runner();
Thread countThread = new Thread(one, "CountThread");
countThread.start();
// 睡眠1秒,main线程对CountThread进行中断,使CountThread能够感知中断而结束
TimeUnit.SECONDS.sleep(1);
countThread.interrupt();
Runner two = new Runner();
countThread = new Thread(two, "CountThread");
countThread.start();
// 睡眠1秒,main线程对Runner two进行取消,使CountThread能够感知on为false而结束
TimeUnit.SECONDS.sleep(1);
two.cancel();
}
private static class Runner implements Runnable {
private long i;
private volatile boolean on = true;
@Override
public void run() {
while (on && !Thread.currentThread().isInterrupted()) {
i++;
}
System.out.println("Count i = " + i);
}
public void cancel() {
on = false;
}
}
}
线程间通信:等到通知机制
package lintcode;
/**
*
*/
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.TimeUnit;
/**
* 6-11
*/
public class WaitNotify {
static boolean flag = true;
static Object lock = new Object();
public static void main(String[] args) throws Exception {
Thread waitThread = new Thread(new Wait(), "WaitThread");
waitThread.start();
TimeUnit.SECONDS.sleep(1);
Thread notifyThread = new Thread(new Notify(), "NotifyThread");
notifyThread.start();
}
static class Wait implements Runnable {
public void run() {
// 加锁,拥有lock的Monitor
synchronized (lock) {
// 当条件不满足时,继续wait,同时释放了lock的锁
while (flag) {
try {
System.out.println(Thread.currentThread() + " flag is true. wait @ "
+ new SimpleDateFormat("HH:mm:ss").format(new Date()));
lock.wait();
} catch (InterruptedException e) {
}
}
// 条件满足时,完成工作
System.out.println(Thread.currentThread() + " flag is false. running @ "
+ new SimpleDateFormat("HH:mm:ss").format(new Date()));
}
}
}
static class Notify implements Runnable {
public void run() {
// 加锁,拥有lock的Monitor
synchronized (lock) {
// 获取lock的锁,然后进行通知,通知时不会释放lock的锁,
// 直到当前线程释放了lock后,WaitThread才能从wait方法中返回
System.out.println(Thread.currentThread() + " hold lock. notify @ " + new SimpleDateFormat("HH:mm:ss").format(new Date()));
lock.notifyAll();
flag = false;
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
// 再次加锁
synchronized (lock) {
System.out.println(Thread.currentThread() + " hold lock again. sleep @ "
+ new SimpleDateFormat("HH:mm:ss").format(new Date()));
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}
结果为:
等待通知的相关方法:
notify():通知一个在对象上等待的线程,使其从wait()方法返回,返回的前提是改线程获取了对象的锁
notifyAll():通知所有等待的线程
wait():进入waiting状态,只有等待另外线程的通知或者是被中断才能够被返回,在调用了wait()后,即可释放锁
等待通知的经典范式:
等待方:
synchronized(对象){
while(条件不满足){
wait()
}
处理相应的事务
}
通知方:
synchronized(对象){
改变条件
对象.notify();
}
管道输入输出流
和一般的文件或者是网络的输入输出流不同的是主要用于线程之间的数据传输,媒介为内存
package chapter04;
import java.io.IOException;
import java.io.PipedReader;
import java.io.PipedWriter;
/**
* 6-12
*/
public class Piped {
public static void main(String[] args) throws Exception {
PipedWriter out = new PipedWriter();
PipedReader in = new PipedReader();
// 将输出流和输入流进行连接,否则在使用时会抛出IOException
out.connect(in);
Thread printThread = new Thread(new Print(in), "PrintThread");
printThread.start();
int receive = 0;
try {
while ((receive = System.in.read()) != -1) {
out.write(receive);
}
} finally {
out.close();
}
}
static class Print implements Runnable {
private PipedReader in;
public Print(PipedReader in) {
this.in = in;
}
public void run() {
int receive = 0;
try {
while ((receive = in.read()) != -1) {
System.out.print((char) receive);
}
} catch (IOException ex) {
}
}
}
}
Thread.join()的使用
A执行了thread.join()后,会等待thread执行完后才从thread.join()返回
使用该方法我们可以设计多线程正序打印数字:
package lintcode;
public class WaitNotify {
public static void main(String[] args) {
Thread previous=Thread.currentThread();
for(int i = 0;i<10;i++){
Thread t=new Thread(new job(previous,i));
previous=t;
t.start();
}
System.out.println("开始计数");
}
}
class job implements Runnable{
private Thread previous;
private int count;
job(Thread t,int i){
this.previous=t;
this.count=i;
}
@Override
public void run() {
// TODO Auto-generated method stub
try {
previous.join();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(count);
}
}
结果为
实际上join()的原理是等待通知机制的一个应用,线程结束了会通知调用线程的线程继续执行
线程应用的示例
等待超时模式
调用一个方法是等待一段时间,如果该方法在给定的时间内得到了结果,那么将结果立刻返回,超时就返回一个默认的结果
等待超时模式的伪代码如下:
public synchronized Object get(long miills){
future=当前时间+mills
剩余时间=mills
while(result==null&&剩余时间》0){
wait(生物时间)
剩余时间=future-当前时间
}
return result
}