概念
进程与线程
- 进程:一个应用就是一个进程,如360安全卫士本身是一个进程
- 线程:一个进程的执行单元,如360的电脑优化、木马查杀等功能
作用:提高效率
注意:两个
进程
的内存独立不共享,在Java语言中两个线程
的堆内存
和方法区内存
共享,但是栈
内存独立
思考:使用了多线程之后main方法结束后,可能程序也不会结束。
因为main方法结束只是代表主线程结束了,主栈空了,其他栈(线程)可能还在。
并发与并行
- 并发:再同一时刻,有多个指令在单个CPU上
交替
执行 - 并行:在同一时刻,在多个指令在多个CPU上
同时
进行
实现方式
- 继承Thread类,重写run方法
- 实现Runnable接口的方式
- 利用Callable接口和Future接口方式实现
方法三能够获取多线程执行结果
方法一:
//新建一个类,继承Thread类
public class OneThread extends Thread{
//重写Run方法
public void run(){
//要多线程执行的代码
}
}
class Test{
public static void main(String[] args){
//创建新建类的实例
OneThread t = new OneThread();
//开启线程
t.start();
//如果要利用多个线程,只需要建多个实例对象
OneThread t1 = new OneThread();
OneThread t2 = new OneThread();
t1.start();
t2.start();
}
}
注意:start()方法的作用是:启动一个分支线程,在JVM中开辟一个新的栈空间,这段代码任务完成之后,瞬间结束。启动成功的线程会自动调用run()方法,并且run方法在分支栈的栈底部(压栈)
main方法在主栈的栈底部,run方法在分支栈的栈底部。run和main是平级的
如果直接在main方法中调用run方法,相当于普通的调用方法,并不会开辟新的线程。
方式二:
//新建一个类,实现 Runnable接口
public class MyThread implements Runnable{
//实现run方法
public void run(){
//想要利用多线程执行的代码
}
}
class Test{
public static void main(String[] args){
MyThread mt = new MyThread();
Thread t = new Thread(mr);
//开启线程
t.start();
}
}
也可以采用匿名内部类的方式
class Test{
public static void main(String[] args){
Thread t = new Thread(new Runable(){
@Override
public void run(){
//想要开启另一个线程执行的代码
}
});
//开启线程
t.start();
}
}
方式三:
//新建一个类实现Callable接口
//注意此处的泛型为结果的泛型,此处以返回整数型为例
public class MyCallable implements Callable<Integer>{
//重写Callable中的call方法
public Integer call() throws Exception {
//此处为要利用多线程执行的代码
int number = 1;
return number;
}
}
class{
public static void main(String[] args) throw Exception{
//创建MyCallable的实例
MyCallable mc = new MyCallable();
//创建FutureTask的实例,用来管理多线程运行的结果
FutureTask<Integer> ft = new FutureTask<>(mc);
//创建线程对象
Thread t1 = new Thread(ft);
//启动线程
t1.start();
//获取多线程运行的结果
int result= ft.get();
}
}
Thread类中成员方法
基本方法
基本方法 | 说明 |
---|---|
String getName() | 获取线程的名字 |
void setName() | 设置线程的名称(构造方法也可以设置名称) |
static Thread currentThread() | 获取当前线程的对象 |
static void sleep(Long time) | 让线程休眠指定的时间ms |
线程默认名称:Thread-0、 Thread-1 …
优先级方法
Java中采用抢占式调度,所有的线程都是随机执行的
优先级方法 | 说明 |
---|---|
setPriority(int newPriority) | 设置线程的优先级 |
final int getProiority() | 获取线程的优先级 |
Java中线程的优先级最低是
1
,最高是10
,如果没有赋值,则默认
是5
守护线程
final void setDaemon(boolean on)
ture:设为守护线程
当非守护线程执行结束之后,守护线程会陆续结束(即使代码块没有执行完毕)
插入线程
public final void join()
将使用该方法的线程插入到当前方法之前。
线程的生命周期
- 刚创建线程对象的
新建状态
- 一直在抢夺执行权的
就绪状态
- 抢夺到执行权的
运行状态
- 遇到sleep或其他阻塞式方法的
阻塞状态
- 线程中代码执行完成后的
死亡状态
注意:
- 线程在
运行期间
有可能会被其他线程抢走执行权
,从而回到就绪状态
- 阻塞状态结束后,线程会回到就绪状态重新抢夺执行权
同步代码块
由于Java中线程在执行的任何时间都会被抢夺走执行权,如果我们需要让这个线程执行完某个步骤才让其能被夺走,我们可以使用锁
来“锁住”这个线程。
格式:
synchronized (锁对象){
操作共享数据的代码
}
注意:锁对象一定是唯一的,一般写为当前类的字节码文件对象如:MyThread.class
特点:
- 锁默认打开,有一个线程进去锁会自动关闭
- 里面的代码全部执行完毕,线程出来锁才会自动打开
面试题
public class ThreadTest {
public static void main(String[] args) {
MyThread t = new MyThread();
t.start();
try {
//此处会让t线程睡眠5s吗
t.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Hello World!");
}
}
class MyThread extends Thread {
@Override
public void run() {
for (int i = 0; i < 1000; i++) {
System.out.println("T ---> " + i);
}
}
}
答案是主线程睡眠5s,因为sleep方法是静态方法,不能通过对象来调用
代码中存在一个常见的误解关于Thread类的sleep方法的使用。在main方法中尝试调用t.sleep(5000);,这里的t是MyThread类的一个实例,而MyThread是Thread类的子类。然而,sleep方法是Thread类的静态方法,这意味着它应该直接通过类名调用,而不是通过Thread类的实例调用。
正确的调用方式应该是Thread.sleep(5000);,这样会让当前执行的线程(在这个上下文中是主线程)睡眠5秒。