Java并发(三)

终结任务

cancel()和isCancel()方法被放到一个所有任务都可以看到的类中,这些任务通过检查sCancel()来确定何时终止自己。

装饰性花园

花园管理希望统计多个大门进入公园的总人数,每个大门都长有计数器,且每个大门计数值递增时,公园 的总人数的共享计数值也会递增

代码1-1

<pre name="code" class="java">import java.util.concurrent.*;
import java.util.*;

class Count {
	private int count = 0;

	public synchronized int increment() {

		return ++count;
	}

	public synchronized int value() {
		return count;
	}
}

class Entrance implements Runnable {
	private static Count count = new Count();
	private static List<Entrance> entrances = new ArrayList<Entrance>();
	private int number = 0;
	private final int id;
	private static volatile boolean canceled = false;

	public static void cancel() {
		canceled = true;
	}

	public Entrance(int id) {
		this.id = id;
		entrances.add(this);
	}

	public void run() {
		while (!canceled) {
			++number;
			System.out.println(this + " Total: " + count.increment());
			try {
				TimeUnit.MILLISECONDS.sleep(100);
			} catch (InterruptedException e) {
				System.out.println("sleep interrupted");
			}
		}
		System.out.println("Stopping " + this);
	}

	public synchronized int getValue() {
		return number;
	}

	public String toString() {
		return "Entrance " + id + ": " + getValue();
	}

	public static int getTotalCount() {
		return count.value();
	}

	public static int sumEntrances() {
		int sum = 0;
		for (Entrance entrance : entrances) {
			sum += entrance.getValue();
		}
		return sum;
	}
}

public class OrnamentalGarden {
	public static void main(String[] args) throws Exception {
		ExecutorService exec = Executors.newCachedThreadPool();
		for (int i = 0; i < 5; i++) {
			exec.execute(new Entrance(i));
		}
		TimeUnit.SECONDS.sleep(3);
		Entrance.cancel();
		exec.shutdown();
		if (!exec.awaitTermination(250, TimeUnit.MILLISECONDS)) {
			System.out.println("Some tasks were not terminated!");
		}
		System.out.println("Total: " + Entrance.getTotalCount());
		System.out.println("Sum of Entrances: " + Entrance.sumEntrances());
	}
}

 代码1-1运行结果: 

Entrance 0: 1 Total: 1Entrance 4: 1 Total: 5Entrance 3: 1 Total: 4Entrance 2: 1 Total: 3Entrance 1: 1 Total: 2Entrance 2: 2 Total: 6Entrance 4: 2 Total: 9Entrance 3: 2 Total: 8Entrance 1: 2 Total: 7Entrance 0: 2 Total: 10…………Entrance 4: 29 Total: 141Entrance 2: 29 Total: 142Entrance 3: 29 Total: 143Entrance 1: 29 Total: 144Entrance 0: 29 Total: 145Entrance 2: 30 Total: 146Entrance 3: 30 Total: 149Entrance 1: 30 Total: 148Entrance 4: 30 Total: 147Entrance 0: 30 Total: 150Stopping Entrance 2: 30Stopping Entrance 1: 30Stopping Entrance 3: 30Stopping Entrance 4: 30Stopping Entrance 0: 30Total: 150Sum of Entrances: 150

这里使用单个Count对象来跟踪花园参观者的共享计数值,Count.increment()和Count.value()都是synchronized,用来控制对count域进行访问。每个Entrance任务都维护一个本地值number,用来统计进入大门的人数。Entrance.canceled是一个volatile的布尔标志,而它只会被读取和赋值,所以不需要同步访问。

最后,主线程在所有任务启动3秒后,调用Entrance的静态cancel()方法,之后再调用exec上的awaitTermination()方法。awaitTermination()等待每个任务结束,如果所有的任务在超时时间达到之前全部结束,返回true,否则返回false。

线程状态

  1. 新建:当线程被创建时,它只会短暂处于这种状态。此时它已被分配了必须的系统资源,并执行了初始化。此刻线程已有资格获得CPU时间了,之后调度器将整个线程转变为可运行状态或阻塞态。
  2. 就绪:在这种状态下,只要调度器把时间分片分配给线程,线程就可以运行。也就是说,任意时刻,线程可运行也可不运行。
  3. 阻塞:线程能够运行,但某个条件阻止它的运行。当线程处于阻塞状态,调度器将忽略线程,不会分配CPU时间给线程。直到线程重新进入就绪态,它才有可能被分配到CPU时间。
  4. 死亡:处于死亡或终止状态的线程将不再是可调度的,并且也不会得到CPU时间,它的任务已结束,或不再是可运行的。任务死亡的通常方式是从run()方法返回,但是任务的线程还可以被中断。
进入阻塞状态

  1. 通过调用sleep(milliseconds)使任务进入休眠状态,在这种情况下,任务在指定时间内是不会运行的。
  2. 通过wait()使线程挂起。直到线程得到notify()或notifyAll()的消息,线程才会进入就绪态
  3. 任务在等待某个输入/输入完成
  4. 任务视图在某个对象上调用其同步控制方法,但是对象锁不可用,因为另一哥任务已经获取了这个锁。
中断

Thread类包含interrupt()方法,可以终止任务,这个方法将设置线程的终端状态,线程将抛出InterruptedException异常。

代码1-2

import java.util.concurrent.*;
import java.io.*;

class SleepBlocked implements Runnable {
	public void run() {
		try {
			TimeUnit.SECONDS.sleep(100);
		} catch (InterruptedException e) {
			System.out.println("InterruptedException");
		}
		System.out.println("Exiting SleepBlocked.run()");
	}
}

class IOBlocked implements Runnable {
	private InputStream in;

	public IOBlocked(InputStream is) {
		in = is;
	}

	public void run() {
		try {
			System.out.println("Waiting for read():");
			in.read();
		} catch (IOException e) {
			if (Thread.currentThread().isInterrupted()) {
				System.out.println("Interrupted from blocked I/O");
			} else {
				throw new RuntimeException(e);
			}
		}
		System.out.println("Exiting IOBlocked.run()");
	}
}

class SynchronizedBlocked implements Runnable {
	public synchronized void f() {
		while (true)
			Thread.yield();
	}

	public SynchronizedBlocked() {
		new Thread() {
			public void run() {
				f();
			}
		}.start();
	}

	public void run() {
		System.out.println("Trying to call f()");
		f();
		System.out.println("Exiting SynchronizedBlocked.run()");
	}
}

public class Interrupting {
	private static ExecutorService exec = Executors.newCachedThreadPool();

	static void test(Runnable r) throws InterruptedException {
		Future<?> f = exec.submit(r);
		TimeUnit.MILLISECONDS.sleep(100);
		System.out.println("Interrupting " + r.getClass().getName());
		f.cancel(true);
		System.out.println("Interrupt sent to " + r.getClass().getName());
	}

	public static void main(String[] args) throws Exception {
		test(new SleepBlocked());
		test(new IOBlocked(System.in));
		test(new SynchronizedBlocked());
		TimeUnit.SECONDS.sleep(3);
		System.out.println("Aborting with System.exit(0)");
		System.exit(0);
	}
}

代码1-2运行结果:

Interrupting SleepBlocked
Interrupt sent to SleepBlocked
InterruptedException
Exiting SleepBlocked.run()
Waiting for read():
Interrupting IOBlocked
Interrupt sent to IOBlocked
Trying to call f()
Interrupting SynchronizedBlocked
Interrupt sent to SynchronizedBlocked
Aborting with System.exit(0)


上面每一个任务都表示一种不同类型的阻塞。SleepBlocked是可中断的阻塞示例,而IOBlocked和SynchronizedBlocked是不可中断的阻塞示例。这两个程序分别证明I/O和在synchronized块上的等待是不可中断的。从输出可以看到,可以中断对sleep()的调用,但是不能中断试图获取synchronized锁或试图执行I/O的线程。


对于I/O异常无法中断问题,有一个笨拙但有效的方案,即关闭任务在其发生阻塞的底层资源

代码1-3

import java.net.*;
import java.util.concurrent.*;
import java.io.*;

class IOBlocked implements Runnable {
	private InputStream in;

	public IOBlocked(InputStream is) {
		in = is;
	}

	public void run() {
		try {
			System.out.println("Waiting for read():");
			in.read();
		} catch (IOException e) {
			if (Thread.currentThread().isInterrupted()) {
				System.out.println("Interrupted from blocked I/O");
			} else {
				throw new RuntimeException(e);
			}
		}
		System.out.println("Exiting IOBlocked.run()");
	}
}

public class CloseResource {
	public static void main(String[] args) throws Exception {
		ExecutorService exec = Executors.newCachedThreadPool();
		ServerSocket server = new ServerSocket(8080);
		InputStream socketInput = new Socket("localhost", 8080).getInputStream();
		exec.execute(new IOBlocked(socketInput));
		exec.execute(new IOBlocked(System.in));
		TimeUnit.MILLISECONDS.sleep(100);
		System.out.println("Shutting down all threads");
		exec.shutdownNow();
		TimeUnit.SECONDS.sleep(1);
		System.out.println("Closing " + socketInput.getClass().getName());
		socketInput.close();
		TimeUnit.SECONDS.sleep(1);
		System.out.println("Closing " + System.in.getClass().getName());
		System.in.close(); 
	}
}

代码1-3运行结果(绿色为输入内容):

Waiting for read():
Waiting for read():
Shutting down all threads
Closing java.net.SocketInputStream
Interrupted from blocked I/O
Exiting IOBlocked.run()
Closing java.io.BufferedInputStream
Java Thread
Interrupted from blocked I/O
Exiting IOBlocked.run()


shutdownNow()被调用之后以及在两个输入流上调用close()之前的延迟强调的是一旦底层资源被关闭,任务将解除阻塞。


被阻塞的NIO类通道会自动地相应中断

代码1-4

import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousCloseException;
import java.nio.channels.ClosedByInterruptException;
import java.nio.channels.SocketChannel;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;

class NIOBlocked implements Runnable {

	private final SocketChannel sc;

	public NIOBlocked(SocketChannel sc) {
		super();
		this.sc = sc;
	}

	@Override
	public void run() {
		// TODO Auto-generated method stub
		try {
			System.out.println("Waiting for read() in " + this);
			sc.read(ByteBuffer.allocate(1));
		} catch (ClosedByInterruptException e) {
			// TODO Auto-generated catch block
			System.out.println("ClosedByInterruptException");
		} catch (AsynchronousCloseException e) {
			// TODO Auto-generated catch block
			System.out.println("AsynchronousCloseException");
		} catch (IOException e) {
			// TODO Auto-generated catch block
			throw new RuntimeException(e);
		}
		System.out.println("Exiting NIOBlocked.run() " + this);
	}
}

public class NIOInterruption {
	public static void main(String[] args) throws Exception {
		ExecutorService exec = Executors.newCachedThreadPool();
		ServerSocket server = new ServerSocket(8080);
		InetSocketAddress isa = new InetSocketAddress("localhost", 8080);
		SocketChannel sc1 = SocketChannel.open(isa);
		SocketChannel sc2 = SocketChannel.open(isa);
		Future<?> f = exec.submit(new NIOBlocked(sc1));
		exec.execute(new NIOBlocked(sc2));
		exec.shutdown();
		TimeUnit.SECONDS.sleep(1);
		f.cancel(true);
		TimeUnit.SECONDS.sleep(1);
		sc2.close();
	}
}

代码1-4运行结果:

Waiting for read() in com.jerry.demo2.NIOBlocked@7ad1e32d
Waiting for read() in com.jerry.demo2.NIOBlocked@674f1c67
ClosedByInterruptException
Exiting NIOBlocked.run() com.jerry.demo2.NIOBlocked@674f1c67
AsynchronousCloseException
Exiting NIOBlocked.run() com.jerry.demo2.NIOBlocked@7ad1e32d


被互斥锁阻塞

代码1-5

import java.util.concurrent.*;
import java.util.concurrent.locks.*;

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

	public BlockedMutex() {
		lock.lock();
	}

	public void f() {
		try {
			lock.lockInterruptibly();
			System.out.println("lock acquired in f()");
		} catch (InterruptedException e) {
			System.out.println("Interrupted from lock acquisition in f()");
		}
	}
}

class Blocked2 implements Runnable {
	BlockedMutex blocked = new BlockedMutex();

	public void run() {
		System.out.println("Waiting for f() in BlockedMutex");
		blocked.f();
		System.out.println("Broken out of blocked call");
	}
}

public class Interrupting2 {
	public static void main(String[] args) throws Exception {
		Thread t = new Thread(new Blocked2());
		t.start();
		TimeUnit.SECONDS.sleep(1);
		System.out.println("Issuing t.interrupt()");
		t.interrupt();
	}
}




代码1-5运行结果:

Waiting for f() in BlockedMutex
Issuing t.interrupt()
Interrupted from lock acquisition in f()
Broken out of blocked call


BlockedMutex类有一个构造器,它要获取所创建对象自身上的Lock,并且不释放这个锁。出于这个原因,如果试图从第二个任务调用f(),那么将会因为BlockedMutex对象持有的锁而被阻塞。在Blocked2中,run()方法在调用blocked.f()的地方停止。与I/O不同,interrupt()可以打算被互斥阻塞的调用。


检查中断

通过调用interrupted()来检查中断状态

代码1-6

import java.util.concurrent.*;

class NeedsCleanup {
	private final int id;

	public NeedsCleanup(int ident) {
		id = ident;
		System.out.println("NeedsCleanup " + id);
	}

	public void cleanup() {
		System.out.println("Cleaning up " + id);
	}
}

class Blocked3 implements Runnable {
	private volatile double d = 0.0;

	public void run() {
		try {
			while (!Thread.interrupted()) {
				NeedsCleanup n1 = new NeedsCleanup(1);
				try {
					System.out.println("Sleeping");
					TimeUnit.SECONDS.sleep(1);
					NeedsCleanup n2 = new NeedsCleanup(2);
					try {
						System.out.println("Calculating");
						for (int i = 1; i < 2500000; i++){
							d = d + (Math.PI + Math.E) / d;
						}
						System.out.println("Finished time-consuming operation");
					} finally {
						n2.cleanup();
					}
				} finally {
					n1.cleanup();
				}
			}
			System.out.println("Exiting via while() test");
		} catch (InterruptedException e) {
			System.out.println("Exiting via InterruptedException");
		}
	}
}

public class InterruptingIdiom {
	public static void main(String[] args) throws Exception {
		Thread t = new Thread(new Blocked3());
		t.start();
		TimeUnit.MILLISECONDS.sleep(3000);
		t.interrupt();
	}
}

代码1-6运行结果:

NeedsCleanup 1
Sleeping
NeedsCleanup 2
Calculating
Finished time-consuming operation
Cleaning up 2
Cleaning up 1
NeedsCleanup 1
Sleeping
NeedsCleanup 2
Calculating
Finished time-consuming operation
Cleaning up 2
Cleaning up 1
NeedsCleanup 1
Sleeping
Cleaning up 1
Exiting via InterruptedException



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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值