|
好久没坚持学习了,所以,这次要好好下定决心学习。多线程与高并发不是一天两天就能弄懂的,需要不断的学习、实践,本次笔者将近学习的内容知识记录下来。多线程也是一项比较重要的内容,虽然CRUD不太会接触到,但是,在一些相关场景可能会有某些问题是由于线程导致的。
|
要了解线程的概念,就需要知道什么是进程。简单理解就是一个进程中包含了许多个线程。现在就简单介绍,后续若是有对操作系统进行研究的话会慢慢介绍,具体关于线程进程的内容可以去看看王道的操作系统,里面讲述得特别清楚。
是系统进行资源分配的基本单位,是操作系统结构的基础,进程是线程的容器。程序是指令、数据及其组织形式的描述,进程是程序的实体。
所谓线程就是操作系统(OS)能够进行运算调度的小单位,是一个基本的CPU执行单元,也是执行程序流的小单元。能够提高OS的并发性能,减小程序在并发执行时所付出的时空开销。线程是进程的一个实体,是被系统独立调度和分派的基本单位。线程本身是不拥有系统资源的,但是它能够使用同属进程的其他线程共享进程所拥有的全部资源。
在Java中,常见得就是继承Thread类或者实现Runnable接口,再通过run或者start方法去执行线程。
如以下代码,这是一段很简单得代码块,通过继承Thread类,重写run方法来创建线程,并且通过run和start来运行。
public class Thread_demo01 { private static class Thread1 extends Thread { @Override public void run() { for (int i = ; i < 5; i++) { try { Thread.sleep(1); } catch (InterruptedException e) { throw new RuntimeException(e); } System.out.println("Thread1-" + i); } } } public static void main(String[] args) { new Thread1().run(); // 顺序执行 // new Thread1().start(); // 线程同时执行 for (int i = ; i < 5; i++) { try { Thread.sleep(1); } catch (InterruptedException e) { throw new RuntimeException(e); } System.out.println("main"); } } }
这两种不同得启动方式,出现得现象也是不同的。
|
sleep()需要提供一个时间参数(毫秒),会使得线程在一定的时间内被暂停执行,在sleep的过程中,线程是不会释放锁的,只会进入阻塞状态,让出cpu给其他线程去执行。如下代码演示,此处不做锁的探究。
public class T3_Thread_Sleep { public static void main(String[] args) { Thread t1 = new Thread(new Thread1()); Thread t2 = new Thread(new Thread2()); t1.start(); t2.start(); } static class Thread1 implements Runnable { @Override public void run() { System.out.println("T1 is running"); try { Thread.sleep(1000); } catch (InterruptedException e) { throw new RuntimeException(e); } System.out.println("T1 is end"); } } static class Thread2 implements Runnable { @Override public void run() { System.out.println("T2 is running"); try { Thread.sleep(2000); } catch (InterruptedException e) { throw new RuntimeException(e); } System.out.println("T2 is end"); } } }
运行之后在T1线程休眠的时候会让出cpu资源给T2线程,T1线程会睡眠1秒,T2睡眠2秒,终结果如图
yield()与sleep()都是让线程暂停执行,也是不会释放锁资源。但是yield并不是让进程进入阻塞态,而是回到就绪态,等待重新获取CPU资源。此时,其他的线程有机会获得cpu资源,也有可能在yield方法进入就绪态后立马变成执行态。如以下代码,同样不考虑锁的问题。
public class T4_Thread_Yield { static class Thread1 implements Runnable { @Override public void run() { System.out.println("T1 is running"); Thread.yield(); System.out.println("T1 is end"); } } static class Thread2 implements Runnable { @Override public void run() { System.out.println("T2 is running"); System.out.println("T2 is end"); } } public static void main(String[] args) { Thread t1 = new Thread(new Thread1()); Thread t2 = new Thread(new Thread2()); t1.start(); t2.start(); } }
join()方法是暂停当前线程,调用执行另一个线程,等待join的线程执行完毕后才能够继续执行当前线程。如以下例子,T1,T2同时开始,在T2线程中join了T1,就会导致T1要先执行完毕之后,才会去执行T2。
public class T5_Thread_Join { public static void main(String[] args) { Thread T1 = new Thread(() -> { System.out.println("T1开始"); for (int i = ; i < 5; i++) { System.out.println("线程T1执行中: " + i); } System.out.println("T1结束"); }); Thread T2 = new Thread(() -> { System.out.println("T2开始"); for (int i = ; i < 5; i++) { System.out.println("线程T2执行中: " + i); if (i == 3) { try { T1.join(); } catch (InterruptedException e) { throw new RuntimeException(e); } } } System.out.println("T2结束"); }); T1.start(); T2.start(); } }
|
线程具有基本的三态(就绪、运行、阻塞)。线程与进程一样,各线程之间也存着共享资源和互相合作的制约关系,致使线程运行时具有间断性。接下来看一下如图,这是五种状态的转化。
在Java线程中有6中状态,从线程的创建到线程的终止。线程创建为NEW创建态,通过start启动线程,线程内部会从就绪态转成运行态,在Java线程中统称为“运行态”,线程由于被挂起、调用yeild等方法能够使线程从运行态转成就绪态,也能够通过线程的其他方法或者锁阻塞线程,直到时间结束或者是获得锁等,从而回到RUNABLE状态。
- 初始(NEW):新创建了一个线程对象,但还没有调用start()方法。
- 运行(RUNNABLE):Java线程中将就绪(Ready)和运行中(Running)两种状态笼统的称为“运行”。线程对象创建后,其他线程(比如main线程)调用了该对象的start()方法。该状态的线程位于可运行线程池中,等待被线程调度选中,获取CPU的使用权,此时处于就绪状态(Ready)。就绪状态的线程在获得CPU时间片后变为运行中状态(Running)。
- 阻塞(BLOCKED):表示线程阻塞于锁。
- 等待(WAITING):进入该状态的线程需要等待其他线程做出一些特定动作(通知或中断)。
- 超时等待(TIMED_WAITING):该状态不同于WAITING,它可以在指定的时间后自行返回。
- 终止(TERMINATED):表示该线程已经执行完毕。
如线程状态转换图,以下就是Java线程状态的转换流程。线程可以通过实现Runnable接口或者继承Threa类,然后去实例化Java的线程对象。在线程被执行之前都是属于创建态(NEW),在调用start方法后,线程就会转成RUNABLE状态,在RUNABLE中,当线程处于就绪态(Ready)的时候,经过调度分配了cpu资源,这时转成了运行态,当线程被挂起、线程执行了yield,线程将会退回就绪态。在运行态(RUNABLE),也会通过一些处理而被阻塞或者等待。终止状态(TERMINATED)是线程执行完毕退出,此时,终止态的线程不会直接转成创建态。
Java中线程的状态都是在java.lang.Thread.State的枚举类中。
可以看一下以下枚举代码,分别为(NEW、RUNNABLE、BLOCKED、WAITING、TIMED_WAITING、TERMINATED)六种。
public enum State { NEW, RUNNABLE, BLOCKED, WAITING, TIMED_WAITING, TERMINATED; }
接下来看一下演示代码
public class T6_Thread_State { static class Thread1 extends Thread { @Override public void run() { System.out.println("run - 当前线程的状态: " + this.getState()); for (int i = ; i < 5; i++) { try { System.out.println("sleep前 - 当前线程的状态: " + this.getState()); Thread.sleep(1000); System.out.println("sleep后 - 当前线程的状态: " + this.getState()); } catch (InterruptedException e) { throw new RuntimeException(e); } } } } public static void main(String[] args) throws InterruptedException { Thread1 t1 = new Thread1(); System.out.println("main - 当前线程的状态: " + t1.getState()); t1.start(); System.out.println("join前 - 当前线程的状态: " + t1.getState()); t1.join(); System.out.println("join后 - 当前线程的状态: " + t1.getState()); } }