多线程学习(十二)

线程状态:

线程可以处于下面的四种状态之一:

新建(NEW)

线程被创建成功之后会短暂处于这个状态。线程已分配了必需的系统资源。并执行了初始化。此时线程已经有资格获得cpu时间片了。此后调度器将把这个线程转变为可运行或阻塞状态。
就绪(Rannable)

在这种情况下,线程只要分配到时间片,他就运行。
阻塞(Blocked)

线程能够运行,但是某种条件阻止了他的运行。当线程阻塞时候。调度器会忽略阻塞线程。不会分配时间片给它,直到线程重新进入可运行状态,才能继续执行操作。
死亡(Dead)

处于死亡或者终止状态的线程,将不会被调度。他的任务已结束。或者不在是可运行的。任务线程死亡通常是从run 方法中返回。但是线程还是可以被中断的。

进入阻塞状态:

  1. sleep()
  2. await()
  3. 等待输出/输入完成
  4. 同步 等待锁

在较早的代码中可能使用 suspend() 和 resume() 来唤醒线程 (留坑、这个两个方法如何唤醒。为什么会导致死锁) ,但是现在已经被废止了(会导致死锁),stop() 也被废止了。因为它不释放线程获得的锁。

中断


下面将演示使用Executor展示interrupt()的用法:

public class SleepBlock implements Runnable {

	@Override
	public void run() {
		// TODO Auto-generated method stub
		try {
			TimeUnit.MILLISECONDS.sleep(600);
		} catch (InterruptedException e) {
			// e.printStackTrace();
			System.out.println("SleepBlock Interrupted");
		}
		System.out.println("Exit SleepBlock run");
	}

}
public class SynchronizedBlock implements Runnable {

	public SynchronizedBlock() {
		new Thread(this) {

			@Override
			public void run() {
				// TODO Auto-generated method stub
				f();
			}

		}.start();
	}

	public synchronized void f() {
		while (true) {
			Thread.yield();
		}
	}

	@Override
	public void run() {
		// TODO Auto-generated method stub
		System.out.println("try invoke fn");
		f();
		System.out.println("Exit synchronized run ");
	}

}
public class IOBlock implements Runnable {
	private InputStream in;

	public IOBlock(InputStream in) {
		super();
		this.in = in;
	}

	@Override
	public void run() {
		// TODO Auto-generated method stub
		// 暂无演示
	}
	
}
public class InterruptTest {
	private static ExecutorService exec = Executors.newCachedThreadPool();

	static void test(Runnable target) {
		Future<?> f = exec.submit(target);
		try {
			TimeUnit.MILLISECONDS.sleep(100);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		System.out.println("interrupting " + target.getClass().getName());
		f.cancel(true);
		System.out.println("interrupt sent to " + target.getClass().getName());
	}

	public static void main(String[] args) throws Exception {
		test(new SleepBlock());
		test(new SynchronizedBlock());
		TimeUnit.SECONDS.sleep(3);
		System.out.println("system exit(0)");
		System.exit(0);
	}
}

运行结果:

上面总共表示了3种阻塞SleepBlock表示可中断阻塞,而IOBlock和SynchronizedBlaock表示不可中断阻塞。他们都是不可以被中断的。通过观察也可以知道 这两个不需要 InterruptException处理。
** 从输出可以看出,能够中断对sleep()的调用。(或者任何抛出InterruptException的操作) 但是你不能中断 IOBlock 和SynchronizedBlock 的操作。 这点有些令人烦恼特别是 执行IO程序的时候,这以为着他将锁住你的多线程程序的性能。特别是web程序。

public class CloseResourceTest {
	public static void main(String[] args) throws Exception {
		ExecutorService exec = Executors.newCachedThreadPool();
		ServerSocket server = new ServerSocket(9292, 1, InetAddress.getByName("127.0.0.1"));
		Socket socket = new Socket("127.0.0.1", 9292);
		InputStream in = socket.getInputStream();
		exec.execute(new IOBlock(in));
		exec.execute(new IOBlock(System.in));
		TimeUnit.MILLISECONDS.sleep(100);
		System.out.println("showdown all Thread");
		exec.shutdownNow();
		TimeUnit.SECONDS.sleep(1);
		System.out.println("closing " + in.getClass().getName());
		in.close();
		TimeUnit.SECONDS.sleep(1);
		System.out.println("closing:"+System.in.getClass().getName());
		System.in.close();
	}
}
public class IOBlock implements Runnable {
	private InputStream in;

	public IOBlock(InputStream in) {
		super();
		this.in = in;
	}

	@Override
	public void run() {
		// 暂无演示
		try {
			System.out.println("try to read");
			in.read();
		} catch (IOException e) {
			if (Thread.currentThread().interrupted()) {
				System.out.println("interupt IOBlock");
			} else {
				throw new RuntimeException();
			}
		}
		System.out.println("Exit IOBlock run");
	}

}

输出:

这个程序证明了 关闭底层资源之后 任务将解除阻塞。
nio 提供了更人性化的操作。被阻塞的nio通道会自动的响应中断。
(演示暂无 留坑)
一个锁多次由同一个任务获得:

public class MutiLock {
	public synchronized void f1(int count) {
		if (count-- > 0) {
			System.out.println("f1:" + count);
			f2(count);
		}
	}

	public synchronized void f2(int count) {
		if (count-- > 0) {
			System.out.println("f2:" + count);
			f1(count);
		}
	}

	public static void main(String[] args) {
		new Thread() {

			@Override
			public void run() {
				new MutiLock().f1(10);
			}

		}.run();
	}
}

JAVA SE5 并发库中新添加一个特性,即在ReentrantLock上阻塞的任务具有中断的能力:
这与synchronized的阻塞完全不同。

public class BlockedMutex {
	private Lock lock = new ReentrantLock();

	public BlockedMutex() {
		lock.lock();
	}
	
	public void f(){
		try {
			lock.lockInterruptibly();
		} catch (InterruptedException e) {
			System.out.println("lock intrupt");
		}
	}

}
public class Blocked2 implements Runnable {
	BlockedMutex block = new BlockedMutex();

	@Override
	public void run() {
		System.out.println("try lockinterruptly");
		block.f();
		System.out.println("exiting Blocked2 run");
	}

	public static void main(String[] args) throws Exception {
		Thread t = new Thread(new Blocked2());
		t.start();

		TimeUnit.SECONDS.sleep(1);

		System.out.println("to interrupt");
		t.interrupt();
	}
}

从运行结果可以看出这个被打断了。

中断检查

public class NeedClean {
	private final int id;

	public NeedClean(int id) {
		this.id = id;
		System.out.println(this);
	}

	@Override
	public String toString() {
		return "NeedClean [id=" + id + "]";
	}

	public void clean() {
		System.out.println("clean :" + this);
	}
}
public class Blocked3 implements Runnable {
	public static void main(String[] args) throws Exception {
		Thread t = new Thread(new Blocked3());
		t.start();
		TimeUnit.MILLISECONDS.sleep(110);
		System.out.println("send interrupt sign");
		t.interrupt();
	}

	@Override
	public void run() {
		try {
			while (!Thread.interrupted()) {
				// point1
				NeedClean need1 = new NeedClean(1);
				try {
					System.out.println("sleep");
					TimeUnit.MILLISECONDS.sleep(100);
					// blocking operation
					// point 2
					NeedClean need2 = new NeedClean(2);
					try {
						System.out.println("counting");
						for (long i = 0; i < 2500000000l; i++) {
							@SuppressWarnings("unused")
							double d = (Math.E + Math.PI) / i + Math.E + Math.PI % 0.3 + 16591;
						}
						System.out.println("finish counting");
					} finally {
						need2.clean();
					}
				} finally {
					need1.clean();
				}
			}
		} catch (InterruptedException e) {
			System.out.println("exit .....");
		}
		// TODO Auto-generated method stub

	}
}

中断发生在 Sleep:

中断发生在 counting:

解释一下输入

  1. 如果中断发生在 point 2 之后就是阻塞操作sleep 之后 非阻塞的运算之中时候 会执行完 for循环 然后执行finally 从 顶部的额while跳出循环
  2. 如果中断发生在 point1 和point2 之间(在sleep之前或者sleep之中)的话,那么任务在执行第一次阻塞操作之前 经由InterruptedException退出。
    介绍此类的 只要目的是为了说明 在涉及相应 interrupt 的类和程序时候 一定要做好 清理策略

关于形成 interrupt interrupted isinterrupted 的区别:
1、interrupt
interrupt方法用于中断线程。调用该方法的线程的状态为将被置为"中断"状态。
注意:线程中断仅仅是置线程的中断状态位,不会停止线程。需要用户自己去监视线程的状态为并做处理。支持线程中断的方法(也就是线程中断后会抛出interruptedException的方法)就是在监视线程的中断状态,一旦线程的中断状态被置为“中断状态”,就会抛出中断异常。

2、interrupted 和 isInterrupted

首先看一下该方法的实现:
public static boolean interrupted () {
return currentThread().isInterrupted(true);
}
该方法就是直接调用当前线程的isInterrupted(true)方法。
然后再来看一下 isInterrupted的实现:
public boolean isInterrupted () {
return isInterrupted( false);
}
这两个方法有两个主要区别:
interrupted 是作用于当前线程,isInterrupted 是作用于调用该方法的线程对象所对应的线程。(线程对象对应的线程不一定是当前运行的线程。例如我们可以在A线程中去调用B线程对象的isInterrupted方法。)
这两个方法最终都会调用同一个方法,只不过参数一个是true,一个是false;

第二个区别主要体现在调用的方法的参数上,让我们来看一看这个参数是什么含义

先来看一看被调用的方法 isInterrupted(boolean arg)的定义:

private native boolean isInterrupted( boolean ClearInterrupted);

原来这是一个本地方法,看不到源码。不过没关系,通过参数名我们就能知道,这个参数代表是否要清除状态位。
如果这个参数为true,说明返回线程的状态位后,要清掉原来的状态位(恢复成原来情况)。这个参数为false,就是直接返回线程的状态位。

这两个方法很好区分,只有当前线程才能清除自己的中断位(对应interrupted()方法)

总结的来说 就是 isinterrupted 每次只会查询标志位 不会改变标志位的状态
而 interrupted 测试线程是否中断 线程的中断状态 由该方法清除。换句话说,如果连续两次调用该方法,则第二次调用将返回 false(在第一次调用已清除了其中断状态之后,且第二次调用检验完中断状态前,当前线程再次中断的情况除外)。

public class Interrupt {
	public static void main(String[] args) throws Exception {
		Thread t = new Thread(new Worker());
		t.start();

		Thread.sleep(20);
		t.interrupt();
		System.out.println("Main thread stopped.");
	}

	public static class Worker implements Runnable {
		public void run() {
			System.out.println("Worker started.");

			try {
				for (long i = 0; i < 2500000000l; i++) {
					@SuppressWarnings("unused")
					double d = (Math.E + Math.PI) / i + Math.E + Math.PI % 0.3 + 16591;
				}
			} finally {
				System.out.println(Thread.interrupted()); // 如果为true的话 会重置标志位 为false 如果为false的话那就不会重置
				System.out.println(Thread.interrupted());
				System.out.println(Thread.interrupted());
			}

			System.out.println("Worker stopped.");
		}
	}
}

**强调一点 抛出InterruptedException 时候jvm 会丢失当前线程的的中断标志,所以在catch 块中的 interrupted或者isInterrupted 都返回false **

posted @ 2017-05-09 22:52  风中小蘑菇  阅读(190)  评论(0)    收藏  举报