Java中的线程
Java之父对线程的定义是:
线程是一个独立执行的调用序列,同一个进程的线程在同一时刻共享一些系统资源(比如文件句柄等)也能访问同一个进程所创建的对象资源(内存资源)。java.lang.Thread对象负责统计和控制这种行为。
每个程序都至少拥有一个线程-即作为Java虚拟机(JVM)启动参数运行在主类main方法的线程。在Java虚拟机初始化过程中也可能启动其他的后台线程。这种线程的数目和种类因JVM的实现而异。然而所有用户级线程都是显式被构造并在主线程或者是其他用户线程中被启动。
本文主要讲了java中多线程的使用方法、线程同步、线程数据传递、线程状态及相应的一些线程函数用法、概述等。在这之前,首先让我们来了解下在操作系统中进程和线程的区别:复制代码
进程:每个进程都有独立的代码和数据空间(进程上下文),进程间的切换会有较大的开销,一个进程包含1--n个线程。(进程是资源分配的最小单位)复制代码
线程:同一类线程共享代码和数据空间,每个线程有独立的运行栈和程序计数器(PC),线程切换开销小。(线程是cpu调度的最小单位)复制代码
线程和进程一样分为五个阶段:创建、就绪、运行、阻塞、终止。复制代码
多进程是指操作系统能同时运行多个任务(程序)。复制代码
多线程是指在同一程序中有多个顺序流在执行。复制代码
在java中要想实现多线程,有两种手段,一种是继续Thread类,另外一种是实现Runable接口.(其实准确来讲,应该有三种,还有一种是实现Callable接口,并与Future、线程池结合使用复制代码
Java线程状态机
Java 给多线程编程提供了内置的支持。 一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。
多线程是多任务的一种特别的形式,但多线程使用了更小的资源开销。
这里定义和线程相关的另一个术语 - 进程:一个进程包括由操作系统分配的内存空间,包含一个或多个线程。一个线程不能独立的存在,它必须是进程的一部分。一个进程一直运行,直到所有的非守护线程都结束运行后才能结束。
多线程能满足程序员编写高效率的程序来达到充分利用 CPU 的目的。
一个线程的生命周期
线程是一个动态执行的过程,它也有一个从产生到死亡的过程。
下图显示了一个线程完整的生命周期。
- 新建状态:
使用 **new** 关键字和 **Thread** 类或其子类建立一个线程对象后,该线程对象就处于新建状态。它保持这个状态直到程序 **start()** 这个线程。复制代码
- 就绪状态:
当线程对象调用了start()方法之后,该线程就进入就绪状态。就绪状态的线程处于就绪队列中,要等待JVM里线程调度器的调度。复制代码
- 运行状态:
如果就绪状态的线程获取 CPU 资源,就可以执行 **run()**,此时线程便处于运行状态。处于运行状态的线程最为复杂,它可以变为阻塞状态、就绪状态和死亡状态。复制代码
- 阻塞状态:
如果一个线程执行了sleep(睡眠)、suspend(挂起)等方法,失去所占用资源之后,该线程就从运行状态进入阻塞状态。在睡眠时间已到或获得设备资源后可以重新进入就绪状态。可以分为三种:复制代码
* 等待阻塞:运行状态中的线程执行 wait() 方法,使线程进入到等待阻塞状态。复制代码
* 同步阻塞:线程在获取 synchronized 同步锁失败(因为同步锁被其他线程占用)。复制代码
* 其他阻塞:通过调用线程的 sleep() 或 join() 发出了 I/O 请求时,线程就会进入到阻塞状态。当sleep() 状态超时,join() 等待线程终止或超时,或者 I/O 处理完毕,线程重新转入就绪状态。复制代码
- 死亡状态:
一个运行状态的线程完成任务或者其他终止条件发生时,该线程就切换到终止状态。 ## Java多线程实战 ## 多线程的实现复制代码
public class 多线程实例 {复制代码
//继承thread @Test public void test1() { class A extends Thread { @Override public void run() { System.out.println("A run"); } } A a = new A(); a.start(); }复制代码
//实现Runnable @Test public void test2() { class B implements Runnable {复制代码
@Override public void run() { System.out.println("B run"); } } B b = new B(); //Runable实现类需要由Thread类包装后才能执行 new Thread(b).start(); }复制代码
//有返回值的线程 @Test public void test3() { Callable callable = new Callable() { int sum = 0; @Override public Object call() throws Exception { for (int i = 0;i < 5;i ++) { sum += i; } return sum; } }; //这里要用FutureTask,否则不能加入Thread构造方法 FutureTask futureTask = new FutureTask(callable); new Thread(futureTask).start(); try { System.out.println(futureTask.get()); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } }复制代码
//线程池实现 @Test public void test4() { ExecutorService executorService = Executors.newFixedThreadPool(5); //execute直接执行线程 executorService.execute(new Thread()); executorService.execute(new Runnable() { @Override public void run() { System.out.println("runnable"); } }); //submit提交有返回结果的任务,运行完后返回结果。 Future future = executorService.submit(new Callable<String>() { @Override public String call() throws Exception { return "a"; } }); try { System.out.println(future.get()); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); }复制代码
ArrayList<String> list = new ArrayList<>(); //有返回值的线程组将返回值存进集合 for (int i = 0;i < 5;i ++ ) { int finalI = i; Future future1 = executorService.submit(new Callable<String>() { @Override public String call() throws Exception { return "res" + finalI; } }); try { list.add((String) future1.get()); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } } for (String s : list) { System.out.println(s); } } }复制代码
线程状态转换
public class 线程的状态转换 { //一开始线程是init状态,结束时是terminated状态 class t implements Runnable { private String name; public t(String name) { this.name = name; } @Override public void run() { System.out.println(name + "run"); } }复制代码
//测试join,父线程在子线程运行时进入waiting状态 @Test public void test1() throws InterruptedException { Thread dad = new Thread(new Runnable() { Thread son = new Thread(new t("son")); @Override public void run() { System.out.println("dad init"); son.start(); try { //保证子线程运行完再运行父线程 son.join(); System.out.println("dad run"); } catch (InterruptedException e) { e.printStackTrace(); } } }); //调用start,线程进入runnable状态,等待系统调度 dad.start(); //在父线程中对子线程实例使用join,保证子线程在父线程之前执行完复制代码
}复制代码
//测试sleep @Test public void test2(){ Thread t1 = new Thread(new Runnable() { @Override public void run() { System.out.println("t1 run"); try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } } });复制代码
//主线程休眠。进入time waiting状态 try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } t1.start();复制代码
}复制代码
//线程2进入blocked状态。 public static void main(String[] args) { test4(); Thread.yield();//进入runnable状态 }复制代码
//测试blocked状态 public static void test4() { class A { //线程1获得实例锁以后线程2无法获得实例锁,所以进入blocked状态 synchronized void run() { while (true) { System.out.println("run"); } } } A a = new A(); new Thread(new Runnable() { @Override public void run() { System.out.println("t1 get lock"); a.run(); } }).start(); new Thread(new Runnable() { @Override public void run() { System.out.println("t2 get lock"); a.run(); } }).start();复制代码
}复制代码
//volatile保证线程可见性 volatile static int flag = 1; //object作为锁对象,用于线程使用wait和notify方法 volatile static Object o = new Object(); //测试wait和notify //wait后进入waiting状态,被notify进入blocked(阻塞等待锁释放)或者runnable状态(获取到锁) public void test5() { new Thread(new Runnable() { @Override public void run() { //wait和notify只能在同步代码块内使用 synchronized (o) { while (true) { if (flag == 0) { try { Thread.sleep(2000); System.out.println("thread1 wait"); //释放锁,线程挂起进入object的等待队列,后续代码运行 o.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println("thread1 run"); System.out.println("notify t2"); flag = 0; //通知等待队列的一个线程获取锁 o.notify(); } } } }).start(); //解释同上 new Thread(new Runnable() { @Override public void run() { while (true) { synchronized (o) { if (flag == 1) { try { Thread.sleep(2000); System.out.println("thread2 wait"); o.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println("thread2 run"); System.out.println("notify t1"); flag = 1; o.notify(); } } } }).start(); }复制代码
//输出结果是 // thread1 run // notify t2 // thread1 wait // thread2 run // notify t1 // thread2 wait // thread1 run // notify t2 //不断循环复制代码
} ## Java Thread常用方法复制代码
yield():
执行此方法会向系统线程调度器(Schelduler)发出一个暗示,告诉其当前JAVA线程打算放弃对CPU的使用,但该暗示,有可能被调度器忽略。使用该方法,可以防止线程对CPU的过度使用,提高系统性能。
Thread#sleep(time)或Thread.sleep(time, nanos):
使当前线程进入休眠阶段,状态变为:TIME_WAITING
Thread.interrupt():
中断当前线程的执行,允许当前线程对自身进行中断,否则将会校验调用方线程是否有对该线程的权限。
如果当前线程因被调用Object#wait(),Object#wait(long, int), 或者线程本身的join(), join(long),sleep()处于阻塞状态中,此时调用interrupt方法会使抛出InterruptedException,而且线程的阻塞状态将会被清除。
interrupted(),返回true或者false:
查看当前线程是否处于中断状态,这个方法比较特殊之处在于,如果调用成功,会将当前线程的interrupt status清除。所以如果连续2次调用该方法,第二次将返回false。
Thread.isInterrupted(),返回true或者false:
与上面方法相同的地方在于,该方法返回当前线程的中断状态。不同的地方在于,它不会清除当前线程的interrupt status状态。
join(time):
A线程调用B线程的join()方法,将会使A等待B执行,直到B线程终止。如果传入time参数,将会使A等待B执行time的时间,如果time时间到达,将会切换进A线程,继续执行A线程。
Java基础18-一文搞懂Java多线程使用方式、实现原理以及常见面试题(二):https://developer.aliyun.com/article/1535712