更多内容请关注微信公众号【Java技术江湖】java
这是一位阿里 Java 工程师的技术小站,做者黄小斜,专一 Java 相关技术:SSM、SpringBoot、MySQL、分布式、中间件、集群、Linux、网络、多线程,偶尔讲点Docker、ELK,同时也分享技术干货和学习经验,致力于Java全栈开发!(关注公众号后回复”资料“便可领取 3T 免费技术学习资源以及我我原创的程序员校招指南、Java学习指南等资源)git
本文介绍了Java多线程的基本概念,使用方法,以及底层实现原理。帮助你更好地使用Java的多线程。程序员
具体代码在个人GitHub中能够找到github
https://github.com/h2pl/MyTech
喜欢的话麻烦点一下星哈谢谢。编程
文章首发于个人我的博客:后端
https://h2pl.github.io/2018/0...
更多关于Java后端学习的内容请到个人CSDN博客上查看:安全
https://blog.csdn.net/a724888
Java之父对线程的定义是:微信
线程是一个独立执行的调用序列,同一个进程的线程在同一时刻共享一些系统资源(好比文件句柄等)也能访问同一个进程所建立的对象资源(内存资源)。java.lang.Thread对象负责统计和控制这种行为。每一个程序都至少拥有一个线程-即做为Java虚拟机(JVM)启动参数运行在主类main方法的线程。在Java虚拟机初始化过程当中也可能启动其余的后台线程。这种线程的数目和种类因JVM的实现而异。然而全部用户级线程都是显式被构造并在主线程或者是其余用户线程中被启动。网络
本文主要讲了java中多线程的使用方法、线程同步、线程数据传递、线程状态及相应的一些线程函数用法、概述等。在这以前,首先让咱们来了解下在操做系统中进程和线程的区别: 进程:每一个进程都有独立的代码和数据空间(进程上下文),进程间的切换会有较大的开销,一个进程包含1--n个线程。(进程是资源分配的最小单位) 线程:同一类线程共享代码和数据空间,每一个线程有独立的运行栈和程序计数器(PC),线程切换开销小。(线程是cpu调度的最小单位) 线程和进程同样分为五个阶段:建立、就绪、运行、阻塞、终止。 多进程是指操做系统能同时运行多个任务(程序)。 多线程是指在同一程序中有多个顺序流在执行。 在java中要想实现多线程,有两种手段,一种是继续Thread类,另一种是实现Runable接口.(其实准确来说,应该有三种,还有一种是实现Callable接口,并与Future、线程池结合使用
下面的图大体介绍了Java线程的调用过程,每一个线程使用一个独立的调用栈进行线程执行,栈中的数据不共享,堆区和方法区的数据是共享的。多线程
构造方法 Thread类中不一样的构造方法接受以下参数的不一样组合: 一个Runnable对象,这种状况下,Thread.start方法将会调用对应Runnable对象的run方法。若是没有提供Runnable对象,那么就会当即获得一个Thread.run的默认实现。 一个做为线程标识名的String字符串,该标识在跟踪和调试过程当中会很是有用,除此别无它用。 线程组(ThreadGroup),用来放置新建立的线程,若是提供的ThreadGroup不容许被访问,那么就会抛出一个SecurityException 。 Thread对象拥有一个守护(daemon)标识属性,这个属性没法在构造方法中被赋值,可是能够在线程启动以前设置该属性(经过setDaemon方法)。 当程序中全部的非守护线程都已经终止,调用setDaemon方法可能会致使虚拟机粗暴的终止线程并退出。 isDaemon方法可以返回该属性的值。守护状态的做用很是有限,即便是后台线程在程序退出的时候也常常须要作一些清理工做。 (daemon的发音为”day-mon”,这是系统编程传统的遗留,系统守护进程是一个持续运行的进程,好比打印机队列管理,它老是在系统中运行。)
启动线程
调用start方法会触发Thread实例以一个新的线程启动其run方法。新线程不会持有调用线程的任何同步锁。
当一个线程正常地运行结束或者抛出某种未检测的异常(好比,运行时异常(RuntimeException),错误(ERROR) 或者其子类)线程就会终止。
当线程终止以后,是不能被从新启动的。在同一个Thread上调用屡次start方法会抛出InvalidThreadStateException异常。
若是线程已经启动可是尚未终止,那么调用isAlive方法就会返回true.即便线程因为某些缘由处于阻塞(Blocked)状态该方法依然返回true。
若是线程已经被取消(cancelled),那么调用其isAlive在何时返回false就因各Java虚拟机的实现而异了。没有方法能够得知一个处于非活动状态的线程是否已经被启动过了。
Java的线程实现基本上都是内核级线程的实现,因此Java线程的具体执行还取决于操做系统的特性。
Java虚拟机为了实现跨平台(不一样的硬件平台和各类操做系统)的特性,Java语言在线程调度与调度公平性上未做出任何的承诺,甚至都不会严格保证线程会被执行。可是Java线程却支持优先级的方法,这些方法会影响线程的调度:
每一个线程都有一个优先级,分布在Thread.MIN_PRIORITY和Thread.MAX_PRIORITY之间(分别为1和10)
默认状况下,新建立的线程都拥有和建立它的线程相同的优先级。main方法所关联的初始化线程拥有一个默认的优先级,这个优先级是Thread.NORM_PRIORITY (5).
线程的当前优先级能够经过getPriority方法得到。
线程的优先级能够经过setPriority方法来动态的修改,一个线程的最高优先级由其所在的线程组限定。
只有不多几个方法能够用于跨线程交流:
每一个线程都有一个相关的Boolean类型的中断标识。在线程t上调用t.interrupt会将该线程的中断标识设为true,除非线程t正处于Object.wait,Thread.sleep,或者Thread.join,这些状况下interrupt调用会致使t上的这些操做抛出InterruptedException异常,可是t的中断标识会被设为false。 任何一个线程的中断状态均可以经过调用isInterrupted方法来获得。若是线程已经经过interrupt方法被中断,这个方法将会返回true。 可是若是调用了Thread.interrupted方法且中断标识尚未被重置,或者是线程处于wait,sleep,join过程当中,调用isInterrupted方法将会抛出InterruptedException异常。 调用t.join()方法将会暂停执行调用线程,直到线程t执行完毕:当t.isAlive()方法返回false的时候调用t.join()将会直接返回(return)。 另外一个带参数毫秒(millisecond)的join方法在被调用时,若是线程没可以在指定的时间内完成,调用线程将从新获得控制权。 由于isAlive方法的实现原理,因此在一个尚未启动的线程上调用join方法是没有任何意义的。一样的,试图在一个尚未建立的线程上调用join方法也是不明智的。 起初,Thread类还支持一些另一些控制方法:suspend,resume,stop以及destroy。这几个方法已经被声明过时。其中destroy方法历来没有被实现,估计之后也不会。而经过使用等待/唤醒机制增长suspend和resume方法在安全性和可靠性的效果有所欠缺
静态方法 Thread类中的部分方法被设计为只适用于当前正在运行的线程(即调用Thread方法的线程)。为强调这点,这些方法都被声明为静态的。 Thread.currentThread方法会返回当前线程的引用,获得这个引用能够用来调用其余的非静态方法,好比Thread.currentThread().getPriority()会返回调用线程的优先级。 Thread.interrupted方法会清除当前线程的中断状态并返回前一个状态。(一个线程的中断状态是不容许被其余线程清除的) Thread.sleep(long msecs)方法会使得当前线程暂停执行至少msecs毫秒。 Thread.yield方法纯粹只是建议Java虚拟机对其余已经处于就绪状态的线程(若是有的话)调度执行,而不是当前线程。最终Java虚拟机如何去实现这种行为就彻底看其喜爱了。
每个线程都是一个线程组中的成员。默认状况下,新建线程和建立它的线程属于同一个线程组。线程组是以树状分布的。 当建立一个新的线程组,这个线程组成为当前线程组的子组。getThreadGroup方法会返回当前线程所属的线程组,对应地,ThreadGroup类也有方法能够获得哪些线程目前属于这个线程组,好比enumerate方法。 ThreadGroup类存在的一个目的是支持安全策略来动态的限制对该组的线程操做。好比对不属于同一组的线程调用interrupt是不合法的。 这是为避免某些问题(好比,一个applet线程尝试杀掉主屏幕的刷新线程)所采起的措施。ThreadGroup也能够为该组全部线程设置一个最大的线程优先级。 线程组每每不会直接在程序中被使用。在大多数的应用中,若是仅仅是为在程序中跟踪线程对象的分组,那么普通的集合类(好比java.util.Vector)应是更好的选择。
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 //不断循环 }