• 多线程学习(十二)


    线程状态:

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

    新建(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 **

  • 相关阅读:
    minecraft我的世界汇总网站
    扫雷网页版
    扫雷模型(非完全一样)
    设计模式-策略模式
    hadoop(2)hadoop配置
    hadoop(1)入门
    Openssl
    加密解密
    信息安全通信
    Web
  • 原文地址:https://www.cnblogs.com/joeCqupt/p/6833176.html
Copyright © 2020-2023  润新知