Java中多线程的状态及转移
Thread.start()和Thread.run()的区别
- Thread.start()只能被调用一次,而Thread.run()可以被调用多次
- Thread.start()是真正的多线程,在得到cpu的time slice时,开始执行run()方法;而Thread.run()只是一个函数,当Thread.run()被调用时,程序顺序执行Thread.run()的内容,执行结束后才会继续执行后面的内容
主动改变线程状态的方法
- Thread.sleep()
注意,Thread.sleep()需要处理InterruptedException异常 - Thread.interrupt()
注意,Thread.interrupted()只会在Thread.sleep()时或者使用Thread.interrupted()/Thread.currentThread().isInterrupted()时检测,否则即使外界调用Thread.interrupt(),线程仍然继续执行
举例来说,下面的代码不会停止,会一直输出”Running”Thread a=new Thread(new Runnable() { @Override public void run() { while(true){ System.out.println("Running"); } } }); a.start(); a.interrupt();
想要接收到interrupt信号需要下面这种写法
Thread a=new Thread(new Runnable() { @Override public void run() { while(!Thread.interrupted()){ System.out.println("Running"); } } }); a.start(); a.interrupt();
或者
Thread a = new Thread(new Runnable() { @Override public void run() { try { while (true) { System.out.println("Running"); Thread.sleep(100); } } catch (InterruptedException ie){ } } }); a.start(); a.interrupt();
Thread Safe
Confinement
线程之间不共享mutable数据(避免使用mutable的全局变量)
Immutability
- No mutator methods
- All fields are private and final
- No representation exposure
- No mutation whatsoever of mutable objects in the rep not even beneficent mutation
Threadsafe Data Types
private static Map<Integer,Boolean> cache = Collections.synchronizedMap(new HashMap<>());
即使在线程安全的ADT上,使用 iterator也是不安全的
Lock
使用synchronized 使用方法:
public void foo(){ synchronized(this){ ... } }
等价于
public synchronized void foo(){ ... }
Threadsafe 和 race condition
注意:这是两个没有关系的词
Threadsafe只保证在多线程的情况下,ADT依然满足ADT的spec
Threadsafe的ADT依然会发生race condition
比如HashSet的Iterator,在多线程下不是ThreadSafe的,因为Iterator的spec要求在使用Iterator时,不能修改HashSet
但即便使用了Collections.synchronizedMap
,虽然get(),put()等操作是Threadsafe的,但Iterator仍然不是Threadsafefor (String s: lst) { ... } // not threadsafe, even if lst is a synchronized list wrapper
即使使用了
Collections.synchronizedMap
,当两个Thread同时调用put()操作,是race condition锁
synchronized (list) { ... }
只代表别人不能用list的锁死锁(Deadlock)
如下图所示,当T1进入锁定a,T2进入锁定b后,T1和T2进入死锁
如何判断死锁?
两个进程有T1和T2,锁的序列为S1和S2,取S1和S2的任意两个前缀P1和P2(P1∩P2为空),以及相应的下一位N1和N2。
若N1$\in$P2且N2$\in$P1则N1和N2死锁。