顺序:进程->线程->协程/纤程
进程:
进程是程序的一次静态执行过程,占用特定的地址资源,每个进程之间都是独立存在,由三部分形成:cpu/data/code
缺点:内存的浪费,cpu负担
线程概念
对于程序而言,线程就是一个指令的集合,一个进程里面的不同执行路径,称为线程,是进程中一个“单一连续的控制流程”/执行路径,线程又被称为轻量级进程,一个进程可以拥有多个并行的线程,一个进程中的线程共享相同的内存单元,内存地址空间->可以访问相同的变量和对象,而且它们从同一堆中分配对象->通信/数据交互/同步操作。由于线程之间的通信是在同一地址空间上进行的,所以不需要额外通信机制。这使得通信更简单而且信息传递也更快。
协程/纤程
线程的又一次细分,这里不多说
线程启动三种方式
-
继承Threadl类,子类重写父类run方法
package com.juc.c_01;
/**
* @Author zcm
* @Email 779986181@qq.com
* @date 2020/5/21 21:33
* @Description 继承Thread类,重写run方法
* 实现线程注意
* 1,继承Thread类
* 2,必须重写run方法,run中写核心执行逻辑代码
* 3,线程启动的时候,不要直接调用run方法,而是调用线程start(),
* 4.每次运行相同的代码,出来的结果可能都不一定,因为多线程谁先抢占资源谁先执行无法人为控制。
*/
public class T_001 extends Thread {
@Override
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName() + "---" + i);
}
}
public static void main(String[] args) {
//调用线程
// new T_001().run();
new T_001().start();
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName()+"-----"+i);
}
}
}
调用run方法和调用start()方法的区别
运行结果
1.start()调用结果
2.run()调用结果
-
实现Runnable接口,实现run方法
package com.juc.c_01;
/**
* @Author zcm
* @Email 779986181@qq.com
* @date 2020/5/21 22:15
* @Description 实现Runnable接口,实现run方法
* 1.实现runnable接口
* 2.重写run方法
* 3.创建Thread对象,将刚刚创建的Runnable的子类,作为构造参数,thread.start()调用线程,这个还使用了代理模式。
*/
public class T_002 implements Runnable {
@Override
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println("使用Thread方法实现线程一:" + Thread.currentThread().getName() + "---" + i);
}
}
public static void main(String[] args) {
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName() + "---" + i);
}
T_002 t_002 = new T_002();
//先创建runnable子类对象,然后创建Thread对象,将Runnable子类对象作为构造参数,最后调用start()
new Thread(t_002).start();
}
}
执行结果
线程的生命周期
1.新生状态
当线程创建好当前线程对象之后,没有启动之前,调用start()之前
用new关键字建立一个线程后,该线程对象就处于新生状态,
处于新生状态线程又自己的内存空间,通过调用start()方法进入就绪状态。
1.Thread T_001 t_001=new T_001();
2.Runnable T_002 t_002=new T_002();
2.就绪状态:
准备开始执行,并没有执行,表示调用start()方法之后
-当对应的线程创建完成,且调用start方法之后,所有的线程会添加到一个就绪的队列中,所有线程同时抢占cpu资源,
-处于就绪状态线程具备了运行条件,但分配到cpu,处于线程就绪队列,等待系统为其分配CPU
-当系统选定一个等待执行的线程后,它就会从就绪进入执行状态,该动作称为CPU调度
3.运行状态:
-当当前进程获取到cpu资源后,就绪队列中的所有线程同时去抢占cpu资源,谁先抢占到谁先执行。在执行过程中叫做运 行状态,抢占到cpu资源,执行代码逻辑开始。
-在运行状态的线程执行的run方法中代码,直到等待某资源二阻塞或完成任何而死亡。
-如果在给定的时间片内没有执行结束,就会被系统给换下来回到等待执行状态。
4.死亡状态:
-当运行中的线程正常执行完所有代码逻辑或者因为异常导致程序结束,叫做死亡状态。
-在运行状态的线程在某些情况下,如执行了sleep(睡眠)方法,或者等待I/O设备资源,将让出cpu并暂时停止自己运行,进入阻 塞状态。
在阻塞状态的线程不能进入就绪队列,只有当引起阻塞原因消除,如睡眠时间已到,或等待的I/O设备空闲下来,线程便转入就 绪状态,重新到就绪列中排队等待,被系统选中后从原来停止的位置开始继续执行。
进入方式:
1.程序正常结束
2.认为中断,调用stop方法
3.程序抛出未捕获的异常
5,阻塞状态:
-在程序运行过程中,发生某些异常情况,导致当前线程无法执行下去,这时会进入阻塞状态,进入阻塞状态的原因消除了,这时 候所有阻塞线程会进入就绪队列,随机抢占cpu资源,等待执行。
-死亡状态是线程生命周期的最后一个阶段,线程死亡原因有三个,一是线程正常的线程完成他的全部工作,二是线程被强制性地 终止,如stop方法来终止一个线程【不推荐这种方式,太粗暴了】,三是线程抛出未捕获的异常。
进入方式:
1.sleep方法
2.等待I/O资源
3.join 方法(代码中执行,强制阻塞其他线程,优先执行当前线程)
-
线程池Executors.newCachedThrad 【不多说】
线程API方法
1.public static Thread currentThread():放回目前执行的线程。
2.public final String getName():返回线程名称。
3.public final int getPriority():返回线程的优先级。
4.public final setPriority(String name):设定线程名称。
5.public final Boolean isAlive():判断线程是否在运动,如果是,返回true,否则返回false。
6.public final void join():调用该方法的线程强制执行,其他线程处于阻塞状态,该线程执行完毕后,期它线程再执行。
7.public static void sleep(long millis):使用当前正在执行的线程休眠millis秒,线程处于阻塞状态。
8.public static void yieId(): 当前线程正在执行的线程暂停一次,允许其他线程执行,不阻塞,线程进入就绪状态。
9.public final void stop():强迫线程停止执行,已经过时,不推荐使用,做法太粗暴。
API方法示例代码:
package com.juc.thread;
/**
* @Author zcm
* @Email 779986181@qq.com
* @date 2020/5/24 15:46
* @Description 线程的API
* 方法
*/
public class ThreadApi implements Runnable {
public static void main(String[] args) {
//获取当线程对象
Thread thread = Thread.currentThread();
//获取当前线程名称
System.out.println("当前线程的名称:" + thread.getName());
//获取当前线程Id
System.out.println("当前线程ID:" + thread.getId());
//获取当前线程优先级,一般系统中范围0-10的值,如果没有人为设置,默认值5,有些系统的范围0-100
System.out.println("获取当前线程优先级:" + thread.getPriority());
//设置当前线程的优先级
/**
* 线程优先级越高,一定有限执行吗?
* 答:不一定,只是优先执行的概率比较大而已。
*/
thread.setPriority(10);
System.out.println("获取设置当前线程优先级:" + thread.getPriority());
//判断当前线程是否存活
System.out.println("判断当前线程是否存活:"+thread.isAlive());
ThreadApi target = new ThreadApi();
Thread thread1 = new Thread(target);
System.out.println("判断当前线程是否存活1:"+thread1.isAlive());
thread1.start();
System.out.println("判断当前线程是否存活1:"+thread1.isAlive());
System.out.println("线程优先级:"+thread1.getPriority());
for (int i = 0; i <5 ; i++) {
System.out.println(Thread.currentThread().getName()+"--------"+i);
if (i==0){
try {
//强制阻塞其他线程,有限执行当前线程
thread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
@Override
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName()+"--------"+i);
if (i==2) {
try {
//i=2,当前线程休眠1秒,休眠的时候线程会进入阻塞转台
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
yieId()示例代码
package com.juc.thread;
/**
* @Author zcm
* @Email 779986181@qq.com
* @date 2020/5/24 17:57
* @Description YieId演示
*/
public class YieIdTest implements Runnable {
public static void main(String[] args) {
YieIdTest yieIdTest = new YieIdTest();
Thread thread = new Thread(yieIdTest);
thread.start();
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName() + "-" + i);
}
}
@Override
public void run() {
for (int i = 0; i < 5; i++) {
if (i == 2) {
Thread.yield();
System.out.println(Thread.currentThread().getName() + "-" + i + "礼让一次");
} else {
System.out.println(Thread.currentThread().getName() + "--" + i);
}
}
}
}
Object类中有两个方法用户等待/唤醒线程,在多线程的时候可以实现唤醒等待的操做,但是唤醒的对象不是Thread类,而是设置的共享变量和共享对象。
2.notify() 唤醒当前共享变量和共享对象
3.notifyAll() 唤醒所有共享变量和共享对象
4.wait() 等待
线程出现死锁怎么解决?
1.继承Thread方式
缺点:每次启动线程对象的时候都会创建自己对象的属性值,相当于每个线程操作自己,没有真正意义实现贡献,怎么解决?
解决方法:将共享变量设置成static
2.每次访问共享对象数据据不一致,怎么解决?
解决方法:使用线程同步,synchronized
2. 实现Runnable接口
优点:每次只创建了一个共享对象,所有的线程都可以实现资源共享
缺点:数据不一致的问题
解决方法:线程同步synchronized