线程的基本操作
目录
- 线程状态切换
- 终止线程(stop)
- 中断线程(interrupt)
- 挂起(suspend) 和 继续执行(resume)
- 等待线程结束(join) 和 谦让(yield)
- sleep
- 线程优先级
- 守护线程
- 线程的同步操作(synchronized, wait, notify)
线程状态切换
终止线程(stop)
不推荐使用Thread.stop(), 他会释放所有的monitor, 导致数据不一致.
假设有一条数据库记录, 有两个字段ID, Name.
为了保持同一条记录ID, Name一致, 会在读写该对象的时候加锁.
线程A获取到锁, 开始写操作, 写完ID = 1, 还没写Name, 被强制stop了, 释放掉了锁.
线程B拿到锁, 读取对象, 以为是线程安全的, 实际上数据是错的, 只有ID有值, Name为null.
中断线程(interrupt)
可以通过调用thread.interrupt()对线程进行中断操作.
当线程收到interrupt信号后, 可能会有两种场景:
- 线程处于运行状态 : 将isinterrupt置为true
- 线程处于阻塞状态 : 抛出InterruptedException, isinterrupt为false.
线程有两个可以判断是否中断的方法, 如下所示.
使用静态Thread.interrupted, 会清除当前中断状态, 而thread.isInterrupted则不会清除状态.
1 // 中断线程 2 public void Thread.interrupt() 3 // 判断是否被中断 4 public boolean Thread.isInterrupted() 5 // 判断是否被中断,并清除当前中断状态isinterrupt 6 public static boolean Thread.interrupted()
1 public void run() { 2 while (true) { 3 if (Thread.currentThread().isInterrupted()) { 4 System.out.println("Interruted!"); 5 break; 6 } 7 Thread.yield(); 8 } 9 } 10 11 t1.interrupt();
挂起(suspend) 和 继续执行(resume)
@Deprecated不推荐使用, 原因是如果在多线程环境中, 假设多个线程调用线程A的suspend和resume.
如果线程B先调用了threadA.suspend(), 线程C再调用threadA.resume(), 则没问题.
如果线程B先调用了threadA.resume(), 线程C再调用threadA.suspend(), 这个时候将导致线程A处于冻结状态, 其持有的锁无法释放.
等待线程结束(join) 和 谦让(yield)
yield
可以让当前线程暂停一下, 类似于sleep, 但是他不会阻塞该线程, 只是从运行状态切换为就绪状态.
当yeild执行后, 优先级大于等于当前线程优先级的所有线程都会有竞争CPU执行的机会, 他自身也会参与竞争.
join
该操作会使得线程执行存在等待, 如果A线程调用B线程的join操作, 则A会等待B执行完成后, 才会继续往下执行.
线程B执行完毕后, 系统会调用notifyAll()来通知所有等待线程.
join也可以指定时间参数, 等待N秒后, 无论目标线程是否完成, 当前线程都会继续往下执行.
1 //join的本质, 处于等待状态 2 while(isAlive()){ 3 wait(0); 4 }
sleep
sleep相对容易理解, 他会使得当前Thread进入阻塞状态, 同时不释放该线程占用的锁.
守护线程
- 在后台默默地完成一些系统性的服务,比如垃圾回收线程、JIT线程就可以理解为守护线程
- 当一个Java应用内,只有守护线程时,Java虚拟机就会自然退出
在一个大型系统中, 应当在new Thread的时候给他加上是否为守护线程, 以及线程名称.
防止系统退出的时候, 出现一堆Thread未关闭阻塞退出.
1 Thread t= new DaemonT(); 2 t.setName("DaemonT"); 3 t.setDaemon(true); 4 t.start();
线程优先级
高优先级的线程更容易在竞争中获胜, 但并不是绝对的.
1 Thread high=new HightPriority(); 2 LowPriority low=new LowPriority(); 3 high.setPriority(Thread.MAX_PRIORITY); 4 low.setPriority(Thread.MIN_PRIORITY); 5 low.start(); 6 high.start();
线程的同步操作
synchronized
- 指定加锁对象:对给定对象加锁,进入同步代码前要获得给定对象的锁。
- 直接作用于实例方法:相当于对当前实例加锁,进入同步代码前要获得当前实例的锁。
- 直接作用于静态方法:相当于对当前类加锁,进入同步代码前要获得当前类的锁。
Object.wait 和 Object.notify notifyAll
三个方法都是Object对象的方法, 需要在synchronized语句块内使用.
wait: 使当前线程暂停执行并释放对象锁标示,让其他线程可以进入synchronized数据块,当前线程被放入对象等待池中。
notify: 从对象的等待池中移走一个任意的线程并放到锁标志等待池中,只有锁标志等待池中线程能够获取锁标志;如果锁标志等待池中没有线程,则notify()不起作用。
notifyAll: 从对象等待池中移走所有等待那个对象的线程并放到锁标志等待池中。
1 public class SimpleWait { 2 3 private static final Object lock = new Object(); 4 5 private static class T1 extends Thread { 6 7 @Override 8 public void run() { 9 synchronized (lock) { 10 System.out.println("T1 start!"); 11 12 try { 13 System.out.println("T1 wait for lock!"); 14 lock.wait(); 15 } catch (InterruptedException e) { 16 e.printStackTrace(); 17 } 18 // 2s后, T2释放锁, T1拿到锁才开始执行wait后的操作 19 System.out.println("T1 end!"); 20 } 21 } 22 } 23 private static class T2 extends Thread { 24 25 @Override 26 public void run() { 27 synchronized (lock) { 28 System.out.println("T2 start, notify others!"); 29 lock.notify(); 30 System.out.println("T2 end!"); 31 try { 32 Thread.sleep(2000); 33 } catch (InterruptedException e) { 34 e.printStackTrace(); 35 } 36 } 37 } 38 } 39 40 public static void main(String[] args){ 41 T1 t1 = new T1(); 42 T2 t2 = new T2(); 43 t1.start(); 44 t2.start(); 45 } 46 }
下一篇博文我们将讲述Java内存模型