Java线程
Java线程的实现方式
方式1:使用 Thread类或继承Thread类 // 创建线程对象 Thread t = new Thread() { public void run() { // 要执行的任务 } }; // 启动线程
方式2:实现 Runnable 接口配合Thread 把【线程】和【任务】(要执行的代码)分开 Thread 代表线程 Runnable 可运行的任务(线程要执行的代码) Runnable runnable = new Runnable() { public void run(){ // 要执行的任务 } }; // 创建线程对象 Thread t = new Thread( runnable ); // 启动线程
方式3:使用有返回值的 Callable class CallableTask implements Callable<Integer> { @Override public Integer call() throws Exception { return new Random().nextInt(); } } //创建线程池 ExecutorService service = Executors.newFixedThreadPool(10); //提交任务,并用 Future提交返回结果
方式4:使用 lambda new Thread(() -> System.out.println(Thread.currentThread().getName())).start();
Java线程的生命周期
Java 语言中线程共有六种状态,分别是: NEW(初始化状态) RUNNABLE(可运行状态+运行状态) BLOCKED(阻塞状态) WAITING(无时限等待) TIMED_WAITING(有时限等待) TERMINATED(终止状态)
在操作系统层面,Java 线程中的 BLOCKED、WAITING、TIMED_WAITING 是一种状态,即前面我们提到的休眠状态。也就是说只要 Java 线程处于这三种状态之一,那么这个线程就永远没有 CPU 的使用权。

Thread常用方法
sleep方法
调用 sleep 会让当前线程从 Running 进入TIMED_WAITING状态,不会释放对象锁
其它线程可以使用 interrupt 方法打断正在睡眠的线程,这时 sleep 方法会抛出 InterruptedException,并且会清除中断标志
睡眠结束后的线程未必会立刻得到执行
sleep当传入参数为0时,和yield相同
yeld方法
yield会释放CPU资源,让当前线程从 Running 进入 Runnable状态,让优先级更高(至少是相同)的线程获得执行机会,不会释放对象锁;
假设当前进程只有main线程,当调用yield之后,main线程会继续运行,因为没有比它优先级更高的线程;
具体的实现依赖于操作系统的任务调度器
join方法
等待调用join方法的线程结束之后,程序再继续执行,一般用于等待异步线程执行完结果之后才能继续运行的场景
public class ThreadJoinDemo { public static void main(String[] sure) throws InterruptedException { Thread t = new Thread(new Runnable() { @Override public void run() { System.out.println("t begin"); try { Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("t finished"); } }); long start = System.currentTimeMillis(); t.start(); //主线程等待线程t执行完成 t.join(); System.out.println("执行时间:" + (System.currentTimeMillis() - start)); System.out.println("Main finished"); }
Java线程的中断机制
Java没有提供一种安全、直接的方法来停止某个线程,而是提供了中断机制。中断机制是一种协作机制,也就是说通过中断并不能直接终止另一个线程,而需要被中断的线程自己处理。被中断的线程拥有完全的自主权,它
既可以选择立即停止,也可以选择一段时间后停止,也可以选择压根不停止。
API的使用
interrupt(): 将线程的中断标志位设置为true,不会停止线程
isInterrupted(): 判断当前线程的中断标志位是否为true,不会清除中断标志位
Thread.interrupted():判断当前线程的中断标志位是否为true,并清除中断标志位,重置为fasle
public class ThreadInterruptTest { static int i = 0; public static void main(String[] args) { System.out.println("begin"); Thread t1 = new Thread(new Runnable() { @Override public void run() { while (true) { i++; System.out.println(i); //Thread.interrupted() 清除中断标志位 //Thread.currentThread().isInterrupted() 不会清除中断标志位 if (Thread.currentThread().isInterrupted() ) { System.out.println("========="); } if(i==10){ break; } } } }); t1.start(); //不会停止线程t1,只会设置一个中断标志位 flag=true t1.interrupt(); }
利用中断机制优雅的停止线程
while (!Thread.currentThread().isInterrupted() && more work to do) { do more work public class StopThread implements Runnable { @Override public void run() { int count = 0; while (!Thread.currentThread().isInterrupted() && count < 1000) { System.out.println("count = " + count++); } System.out.println("线程停止: stop thread"); } public static void main(String[] args) throws InterruptedException { Thread thread = new Thread(new StopThread()); thread.start(); Thread.sleep(5); thread.interrupt(); }
注意:使用中断机制时一定要注意是否存在中断标志位被清除的情况 sleep 期间能否感受到中断 修改上面的代码,线程执行任务期间有休眠需求 @Override public void run() { int count = 0; while (!Thread.currentThread().isInterrupted() && count < 1000) { System.out.println("count = " + count++); try { Thread.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println("线程停止: stop thread");

处于休眠中的线程被中断,线程是可以感受到中断信号的,并且会抛出一个 InterruptedException 异常,同时清除中断信号,将中断标记位设置成 false。这样就会导致while条件Thread.currentThread().
isInterrupted()为false,程序会在不满足count < 1000这个条件时退出。如果不在catch中重新手动添加中断信号,不做任何处理,就会屏蔽中断请求,有可能导致线程无法正确停止。
try { Thread.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); //重新设置线程中断状态为true Thread.currentThread().interrupt(); sleep可以被中断 抛出中断异常:sleep interrupted, 清除中断标志位 wait可以被中断 抛出中断异常:InterruptedException, 清除中断标志位
Java线程间通信
volatile volatile有两大特性,一是可见性,二是有序性,禁止指令重排序,其中可见性就是可以让线程之间进行通信。
public class VolatileDemo { private static volatile boolean flag = true; public static void main(String[] args) { new Thread(new Runnable() { @Override public void run() { while (true){ if (flag){ System.out.println("trun on"); flag = false; } } } }).start(); new Thread(new Runnable() { @Override public void run() { while (true){ if (!flag){ System.out.println("trun off"); flag = true; } } } }).start(); }
等待唤醒(等待通知)机制
等待唤醒机制可以基于wait和notify方法来实现,在一个线程内调用该线程锁对象的wait方法,线程将进入等待队列进行等待直到被唤醒。
public class WaitDemo { private static Object lock = new Object(); private static boolean flag = true; public static void main(String[] args) { new Thread(new Runnable() { @Override public void run() { synchronized (lock){ while (flag){ try { System.out.println("wait start ......."); lock.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println("wait end ....... "); } } }).start(); new Thread(new Runnable() { @Override public void run() { if (flag){ synchronized (lock){ if (flag){ lock.notify(); System.out.println("notify ......."); flag = false; } } } } }).start(); }
LockSupport是JDK中用来实现线程阻塞和唤醒的工具,线程调用park则等待“许可”,调用unpark则为指定线程提供“许可”。使用它可以在任何场合使线程阻塞,可以指定任何线程进行唤醒,并且不用担心阻塞和唤醒
操作的顺序,但要注意连续多次唤醒的效果和一次唤醒是一样的。
public class LockSupportTest { public static void main(String[] args) { Thread parkThread = new Thread(new ParkThread()); parkThread.start(); System.out.println("唤醒parkThread"); LockSupport.unpark(parkThread); } static class ParkThread implements Runnable{ @Override public void run() { System.out.println("ParkThread开始执行"); LockSupport.park(); System.out.println("ParkThread执行完成"); } }
管道输入输出流 管道输入/输出流和普通的文件输入/输出流或者网络输入/输出流不同之处在于,它主要用于线程之间的数据传输,而传输的媒介为内存。管道输入/输出流主要包括了如下4种具体实现:PipedOutputStream、
PipedInputStream、PipedReader和PipedWriter,前两种面向字节,而后两种面向字符。
public class Piped { public static void main(String[] args) throws Exception { PipedWriter out = new PipedWriter(); PipedReader in = new PipedReader(); // 将输出流和输入流进行连接,否则在使用时会抛出IOException out.connect(in); Thread printThread = new Thread(new Print(in), "PrintThread"); printThread.start(); int receive = 0; try { while ((receive = System.in.read()) != -1) { out.write(receive); } } finally { out.close(); } } static class Print implements Runnable { private PipedReader in; public Print(PipedReader in) { this.in = in; } @Override public void run() { int receive = 0; try { while ((receive = in.read()) != -1) { System.out.print((char) receive); } } catch (IOException ex) { } } }
Thread.join
join可以理解成是线程合并,当在一个线程调用另一个线程的join方法时,当前线程阻塞等待被调用join方法的线程执行完毕才能继续执行,所以join的好处能够保证线程的执行顺序,但是如果调用线程的join方法其
实已经失去了并行的意义,虽然存在多个线程,但是本质上还是串行的,最后join的实现其实是基于等待通知机制的
使用wait/notify阻塞唤醒线程 public class WaitTest { public void testWaitMethod(Object lock) { try { synchronized (lock) { System.out.println(Thread.currentThread().getName()+" begin wait()"); // wait() 释放锁资源 Thread.sleep(5000); lock.wait(); System.out.println(Thread.currentThread().getName()+" end wait()"); } } catch (Exception e) { e.printStackTrace(); } } public void testNotifyMethod(Object lock) { synchronized (lock) { System.out.println(Thread.currentThread().getName()+" begin notify()"); lock.notify(); System.out.println(Thread.currentThread().getName()+" end notify()"); } } public static void main(String[] args) { Object lock = new Object(); WaitTest test = new WaitTest(); Thread t1 = new Thread(new Runnable() { @Override public void run() { test.testWaitMethod(lock); } },"threadA"); t1.start(); Thread t2 = new Thread(new Runnable() { @Override public void run() { test.testNotifyMethod(lock); } },"threadB"); t2.start(); try { Thread.sleep(500000); } catch (InterruptedException e) { e.printStackTrace(); } } } 使用wait,notify来实现等待唤醒功能至少有两个缺点: wait和notify都是Object中的方法,在调用这两个方法前必须先获 得锁对象,这限制了其使用场合:只能在同步代码块中。 当对象的等待队列中有多个线程时,notify只能随机选择一个线 程唤醒,无法唤醒指定的线程 使用LockSupport的话,我们可以在任何场合使线程阻塞,同时也可以指 定要唤醒的线程,相当的方便。
使用park/unpark阻塞唤醒线程 LockSupport是JDK中用来实现线程阻塞和唤醒的工具。使用它可以在任何场合使线程阻 塞,可以指定任何线程进行唤醒,并且不用担心阻塞和唤醒操作的顺序,但要注意连续多次 唤醒的效果和一次唤醒是一样的。JDK并发包下的锁和其他同步工具的底层实现中大量使用 了LockSupport进行线程的阻塞和唤醒,掌握它的用法和原理可以让我们更好的理解锁和其 它同步工具的底层实现
public class LockSupportTest { public static void main(String[] args) { Thread parkThread = new Thread(new ParkThread()); parkThread.start(); System.out.println("唤醒parkThread"); LockSupport.unpark(parkThread); } static class ParkThread implements Runnable{ @Override public void run() { System.out.println("ParkThread开始执行"); LockSupport.park(); System.out.println("ParkThread执行完成"); } } }

浙公网安备 33010602011771号