文章目录
- 一、概念
- 1、进程
- 2、线程
- 3、CPU与线程的关系
- 4、并行、并发
- 5、线程的生命周期
- 二、创建
- 1、继承Thread
- 2、实现Runnable接口
- 3、实现Callable接口
- 三、API
- 1、获取运行使用的线程
- 2、唯一标识
- 3、线程名
- 4、优先级
- 5、是否处于活动状态
- 6、守护线程
- 7、join
-
- 8、yield
- 9、sleep
- 10、线程中断
一、概念
1、进程
2、线程
- 是1个进程(程序内部)的1条执行路径
- 单线程:1个进程中只有1个线程(1条执行路径)
- 多线程:1个进程中包含多个线程(多条执行路径)
- 线程之间堆内存、方法区内存共享;但是栈内存独立,1个线程一个栈
3、CPU与线程的关系
- 单核CPU:不能够做到真正的多线程并发,因为在一个时间单元内,只能执行一个线程的任务,多个线程谁获取时间片运行谁,每个线程获取时间片的概率相等,可能:t1、t2、t2、t2;给人一种多线程并发的感觉:其实是由于CPU的处理速度极快,多个线程之间频繁切换执行,跟人来的感觉是多个事情同时在做
4、并行、并发
- 并行:多核CPU同时执行多个任务。比如:多个人同时做不同的事
- 并发:单核CPU同时执行多个任务。比如:多个人做同一件事
5、线程的生命周期
- 新建状态
- 就绪状态
- 线程调用了start方法等待获取CPU时间片
- 表示当前线程具有抢夺CPU时间片的权力
- 运行状态
- 线程对象开始执行run方法
- run方法的开始执行标志着这个线程进入运行状态,当之前占有的CPU时间片用完之后,会重新回到就绪状态继续抢夺CPU时间片,当再次抢到CPU时间之后,会重新进入run方法接着上一次的代码继续往下执行
- 阻塞状态
- 当一个线程遇到阻塞事件,例如:sleep方法、获取synchronized排他锁失败(因为锁被其它线程所占用)等,此时线程会进入阻塞状态,阻塞状态的线程会放弃之前占有的CPU时间片,之前的时间片没了需要再次回到就绪状态抢夺CPU时间片
- 死亡状态
- run方法执行完毕或者因异常退出了run方法,该线程生命周期结束
二、创建
1、继承Thread
class Thread implements Runnable
- 缺点:
- 由于java是单继承的,这导致继承了Thread后就不能在继承其它类了;在实际开发中会经常继承某个超类来复用其中的方法,这导致两者不能同时继承
- 继承线程后重写run方法来定义任务,这又导致我们将任务直接定义在线程上使得线程只能做该任务,无法并发执行其他任务,重用性变差
public class HandleMsg extends Thread{private String threadKey;public HandleMsg(String threadKey){this.threadKey=threadKey;}@Overridepublic void run(){for(int i=0;i<10;i++){System.err.println(threadKey+":run");}}}
Thread t1=new Thread(()->{for(int i=0;i<10;i++) {System.err.println("t1:run");}
};
HandleMsg h1=new HandleMsg("h1");
HandleMsg h2=new HandleMsg("h2");
h1.start();
h2.start();
2、实现Runnable接口
public class HandleMsg implements Runnable{private String threadKey;public HandleMsg(String threadKey){this.threadKey=threadKey;}@Overridepublic void run(){for(int i=0;i<10;i++){System.err.println(threadKey+":run");}}}
HandleMsg h1=new HandleMsg("h1");
HandleMsg h2=new HandleMsg("h2");
Thread t1=new Thread(h1);
Thread t2=new Thread(h2);
t1.start();
t2.start();
3、实现Callable接口
FutureTask implements RunnableFuture
,RunnableFuture<V> extends Runnable
- 优点
- 线程和线程执行的任务分离
- 有返回值
- 可以声明抛出的异常
public class HandleMsg implements Callable<String>{private String threadKey;public HandleMsg(String threadKey){this.threadKey=threadKey;}@Overridepublic String call() throws Exception{for(int i=0;i<10;i++){System.err.println(threadKey+":run");}return threadKey;}}
HandleMsg h1=new HandleMsg("h1");
HandleMsg h2=new HandleMsg("h2");
FutureTask<String> f1=new FutureTask<>(h1);
FutureTask<String> f2=new FutureTask<>(h2);
Thread t1=new Thread(f1);
Thread t2=new Thread(f2);
t1.start();
t2.start();
String result1=f1.get();
String result2=f2.get();
三、API
1、获取运行使用的线程
Thread thread=Thread.currentThread();
2、唯一标识
long getId();
3、线程名
String getName();
void setName(String name);
4、优先级
int getPriority();
void setPriority(int newPriority);
5、是否处于活动状态
boolean isAlive();
6、守护线程
- 守护线程又称为后台线程,默认创建出来的线程都是普通线程或称为前台线程
- 当进程结束时,所有正在运行的守护线程都会被强制中断
- 进程的结束:当一个进程中没有任何前台线程时即结束
- main主线程就是前台线程,不受其他线程影响,分配其他线程后接着干自己的事,其他线程执行的时候,main线程可能已经结束了
boolean isDaemon();
void setDaemon(boolean on);
7、join
- 作用是:让当前执行的线程陷入等待(内部调用了wait方法)。
- 永久等待:其实现原理是不停的检查当前线程是否存活,该线程的任务执行完毕后就会处于死亡状态,如果存活则说明任务还未执行完毕-继续等待
- 线程启动之后调用
1、API
void join();
void join(long millis);
2、有无join对比
Thread t1=new Thread(()->{for(int i=0;i<5;i++){System.err.println(i);}
});
t1.start();System.err.println("next task");
Thread t1=new Thread(()->{for(int i=0;i<5;i++){System.err.println(i);}
});
t1.start();t1.join();System.err.println("next task");
8、yield
- 线程让步
- 暂停(不是终止)当前正在执行的线程任务,让其它具有相同优先级或更高优先级的等待的线程执行任务(其它也会包含暂停的线程,所以有可能刚暂停就执行)
- 暂停的线程状态变化:运行状态 -> 就绪状态
- 暂停期间不会释放锁,所以其他线程获取不到锁
9、sleep
- 使线程睡眠
- sleep的睡眠期间不会释放锁,所以其它线程获取不到锁
- 睡眠线程的状态变化:运行状态 -> 阻塞状态 -> 就绪状态
static void sleep(long millis) throws InterruptedException;
sleep | wait |
---|
属于Thread类 | 属于Object类 |
可以在任何地方使用 | wait、notify、notifyAll 只能在同步方法、同步控制块里面使用 |
睡眠期间不会释放锁 | 会释放锁,而且会将当前线程加入到等待队列中 |
不需要唤醒 | 可以被notify、notifyAll唤醒 |
| |
10、线程中断
boolean isInterrupted();
Thread t1=new Thread(()->{System.err.println("run......");try{Thread.sleep(10000L);}catch(InterruptedException e){e.printStackTrace();}System.err.println("end......");
});
t1.start();
t1.interrupt();System.err.println("next task");