本文记录一些 Java 多线程相关的概念性的知识。
线程的状态
新建 (New) 线程已被创建,但是尚未启动
可运行 (Runnable) 此线程在 JVM 中正在运行
阻塞 (Blocked) 此线程正在等待获取一个监视锁 (monitor lock),需要其他线程显式唤醒
等待 (Waiting) 此线程正在无限期等待另一个线程完成某些工作
进入方法
退出方法
Object#wait()
Object#notify()
或 Object#notifyAll()
Thread#join()
被调用的线程执行完毕
限期等待 (Timed waiting) 此线程正在有限期等待另一个线程完成某些工作
进入方法
退出方法
Thread.sleep()
设定的休眠时间结束
Object#wait(long timeout)
时间结束 / Object#notify()
/ Object#notifyAll()
Thread#join(long millis)
时间结束 / 被调用的线程执行完毕
终止 (Terminated) 线程结束
使用线程 见 Java 如何创建和运行多线程
互斥同步 synchronized
同步一个代码块 只作用于同一个对象,如多个 Thread
使用同一个 Runnable
时。一个线程若要使用此方法,则必须获得 obj 对象的锁。
1 2 3 4 5 public void something () { synchronized (obj) { } }
同步一个方法 1 2 3 4 5 public void synchronized something () { }
1 2 3 4 5 public void static synchronized aStaticMethod () { }
同步一个类 作用于整个类,即使两个线程使用同一个类的不同对象,也会进行同步。一个线程若要使用此方法,则必须获得该类的锁。
1 2 3 4 5 public void something () { synchronized (SynchronizationExample.class) { } }
ReentrantLock
ReentrantLock
是 java.util.concurrent
包中的锁
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 public class LockDemo implements Runnable { private Lock lock = new ReentrantLock (); @Override public void run () { try { lock.lock(); for (int i = 0 ; i < 10 ; i++) { System.out.println(i + " " ); } } finally { lock.unlock(); } } }
1 2 3 4 5 6 7 8 9 10 11 12 public class Main { public static void main (String[] args) { LockDemo lockDemo = new LockDemo (); ExecutorService executorService = Executors.newCachedThreadPool(); executorService.execute(lockDemo); executorService.execute(lockDemo); } }
线程协作 Thread#join()
在 A 线程中调用 B 线程的 join()
方法,会将当前线程挂起,直到目标线程结束。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 public class MyRunnable implements Runnable { private AtomicInteger ticketCount = new AtomicInteger (5 ); @Override public void run () { System.out.println(Thread.currentThread().getName() + " started." ); while (true ) { try { Thread.sleep(500 ); } catch (InterruptedException e) { e.printStackTrace(); } if (ticketCount.get() > 0 ) { System.out.println(Thread.currentThread().getName() + " has " + ticketCount.getAndDecrement() + " tickets" ); } else { break ; } } System.out.println(Thread.currentThread().getName() + " stopped." ); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public class Main { public static void main (String[] args) throws InterruptedException { Thread t1 = new Thread (new MyRunnable ()); Thread t2 = new Thread (new MyRunnable ()); Thread t3 = new Thread (new MyRunnable ()); t1.start(); t1.join(); t2.start(); t3.start(); } }
执行结果如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 Thread-0 started. Thread-0 has 5 tickets Thread-0 has 4 tickets Thread-0 has 3 tickets Thread-0 has 2 tickets Thread-0 has 1 tickets Thread-0 stopped. Thread-1 started. Thread-2 started. Thread-1 has 5 tickets Thread-2 has 5 tickets Thread-2 has 4 tickets Thread-1 has 4 tickets Thread-2 has 3 tickets Thread-1 has 3 tickets Thread-2 has 2 tickets Thread-1 has 2 tickets Thread-1 has 1 tickets Thread-2 has 1 tickets Thread-1 stopped. Thread-2 stopped.
wait()
,notify()
,和 notifyAll()
wait()
将当前线程变为等待状态,notify()
和 notifyAll()
将等待状态的线程唤醒。wait()
方法必须在有锁 (即 synchronized
) 的代码块中执行。
当有多个线程处于等待状态时,notify()
会任意选择一个线程来唤醒,选择的方式由 JVM 的实现来决定;而 notifyAll()
则会唤醒所有等待中的线程。
因为线程唤醒后,程序将会从 wait()
的下一条语句中开始执行,所以 wait()
方法应当总在 while
循环中调用,通过循环条件控制线程是否继续等待。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 public class WaitNotifyDemo { public synchronized void before () { System.out.println("before" ); notifyAll(); } public synchronized void after () { try { wait(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("after" ); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 public class Main { public static void main (String[] args) throws InterruptedException { ExecutorService executorService = Executors.newCachedThreadPool(); WaitNotifyDemo waitNotifyDemo = new WaitNotifyDemo (); executorService.execute(waitNotifyDemo::after); executorService.execute(waitNotifyDemo::before); } }
执行结果: