java多线程编程(1)—— 基础知识总结
创始人
2025-05-31 13:02:27

目录

并发、并行的区别

进程与线程

进程

线程

联系

区别

如何实现线程

继承Thread类,重写run方法

实现Runable接口,实现run方法

callable接口+FutureTask

setPriority线程的优先级 

join插入线程

守护线程

线程生命周期

并发、并行的区别


并发:同一时刻,多个指令在单个CPU上交替执行
并行:同一时刻,多个指令在多个CPU上同时执行

假如在多个CPU的电脑上,同一时刻,如果执行指令超过CPU的线程数,那么CPU会对执行指令做交替执行,做并行操作

进程与线程

进程

具有一定独立功能的程序关于某个数据集合上的一次运行活动,进程是系统进行资源分配和调度的一个独立单位。

线程

是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位。线程自己基本上不拥有系统资源,只拥有一些在运行中必不可少的资源(如程序计数器,一组寄存器和栈),但是它可与同属一个进程的其他的线程共享进程所拥有的全部资源。

联系

线程是进程的一部分,一个线程只能属于一个进程,而一个进程可以有多个线程,但至少有一个线程

区别

  • 一个程序至少有一个进程,一个进程至少有一个线程;
     
  • 线程的划分尺度小于进程,使得多线程程序的并发性高;
     
  • 进程在执行过程中拥有独立的内存单元,而多个线程共享内存,但线程之间没有单独的地址空间,一个线程死掉就等于整个进程死掉。

如何实现线程

继承Thread类,重写run方法

public class ThreadDemo1 extends Thread{@Overridepublic void run() {super.run();for(int i=0;i<10;i++){System.out.println(getName()+i);}}
}public static void main(String[] args){ThreadDemo1 thread1=new ThreadDemo1();thread1.setName("女神");ThreadDemo2 thread2=new ThreadDemo2();thread2.setName("备胎");thread2.setDaemon(true);thread1.start();thread2.start();}

其中setName为给线程取名字,通过getName获取

输出:

可以看到线程都是交替、并行的方式去执行


实现Runable接口,实现run方法

public class MyRunable implements Runnable{@Overridepublic void run() {for(int i=0;i<100;i++){System.out.println(Thread.currentThread().getName()+"hello5555");}}
}public static void main(String[] args){MyRunable runable1=new MyRunable();MyRunable runable2=new MyRunable();MyRunable runable3=new MyRunable();Thread thread1=new Thread(runable1);Thread thread2=new Thread(runable2);Thread thread3=new Thread(runable3);thread1.setName("线程1");thread2.setName("线程2");thread3.setName("线程3");thread1.start();thread2.start();thread3.start();}

Runable中无法通过getName去获取到线程的名称,但是可以通过Thread.currentThread().getName去获取的


callable接口+FutureTask

这种可以获取多线程执行的结果

public class MyCallBale implements Callable {//Callable中的泛型为要返回的值的类型@Overridepublic Integer call() throws Exception {int sum=0;for (int i = 0; i < 10; i++) {sum+=i;}return sum;}
}public static void main(String[] args){MyCallBale callBale=new MyCallBale();FutureTask futureTask=new FutureTask<>(callBale);Thread thread=new Thread(futureTask);thread.start();try {System.out.println(futureTask.get());} catch (ExecutionException e) {e.printStackTrace();} catch (InterruptedException e) {e.printStackTrace();}}

输出:

 由此,我们也发现继承thread类跟实现runable有本质上的优缺点了

  1. 继承的话,就不方便扩展了,实现Runable就挺好的
  2. Runable接口,同时也支持多个线程执行同一个Runable任务

setPriority线程的优先级 

1-10档位,默认是5,数字越大,线程并发的时候有很大的概率会优先执行,来个例子试试

public class ThreadDemo extends Thread{@Overridepublic void run() {super.run();for(int i=0;i<100;i++){System.out.println(getName()+"hello5555");}}
}public static void main(String[] args){ThreadDemo thread1=new ThreadDemo();thread1.setName("线程1");ThreadDemo thread2=new ThreadDemo();thread2.setName("线程2");thread1.setPriority(1);thread2.setPriority(10);thread1.start();thread2.start();}

输出:

可以看到线程2先执行了,但是这种也算是概率性的,比如我在执行了一次,输出:

 额,又变成线程1了额,感觉没啥用的这个,这个方法平时不建议使用

join插入线程

在start后执行,会强制插入到其他线程之前执行插入的线程的代码逻辑 Thread.join,实现:

 public static void main(String[] args) {ThreadDemo1 thread1=new ThreadDemo1();thread1.setName("女神");ThreadDemo2 thread2=new ThreadDemo2();thread2.setName("备胎");//   thread2.setDaemon(true);thread1.start();thread2.start();thread2.join();System.out.println("主线程666");}

这样的话,原本会先执行main线程的    System.out.println("主线程666")的,但是因为thread2线程插入到了主线程之前了,这样的话,就会先执行完线程的逻辑,然后再执行main线程的逻辑

守护线程

在主线程关闭后无需手动关闭守护线程,因为会自动关闭,避免了麻烦,Java垃圾回收线程就是一个典型的守护线程,简单粗暴的可以理解为所有为线程服务而不涉及资源的线程都能设置为守护线程。举个例子

public static void main(String[] args) {Thread thread = new Thread(() -> {while (true) {try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("----睡眠一秒-----");}});//默认为false,设置为false代表非守护线程,true为守护线程,守护线程在主方法结束时候结束thread.setDaemon(true);thread.start();try {TimeUnit.SECONDS.sleep(3);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("主线程over");}

输出:

如果设置为false的话,Thread线程就还是照着正常的执行,不会因为设置了守护线程导致中断的问题

 输出:

应用场景:

多线程的情况下,主线程在执行结束后,如果有涉及到子线程的话,就可以给它设置为守护线程,其次在守护线程中创建的所有子线程都是守护线程,但是如果是调用join的话,守护线程还是会执行完,然后再执行主线程的内容

线程生命周期

新建(start)->就绪(有执行的资格,但是没有抢到CPU的执行权,所以没有执行权利)->运行(抢到CPU的执行权了,去执行代码,但是可能会被其他线程又抢走,所以可能会->就绪)->结束运行状态的可能出现的异常情况:

  • 运行状态时,通过阻塞方法,生命周期会由运行->阻塞(没有执行资格,没有执行权),当sleep时间到了后,又会重新->就绪状态,重新抢夺CPU的执行权
  • 运行状态通过wait(),生命周期会由运行-》等待(没有执行资格,没有执行权),等待被唤醒后,再次变为就绪状态
  • 运行状态通过sleep(10),生命周期会由运行-》计时等待,等时间到了后,又会重新-》就绪状态

流程图说明:

相关内容

热门资讯

玩家分享攻略“金满地开挂器”(... 您好:金满地这款游戏可以开挂,确实是有挂的,需要了解加客服微信【4282891】很多玩家在这款游戏中...
我来分享“欢喜麻将怎么装挂”(... 有 亲,根据资深记者爆料欢喜麻将是可以开挂的,确实有挂(咨询软件无需打开...
重大消息“福建麻将开挂使用方法... 有 亲,根据资深记者爆料福建麻将是可以开挂的,确实有挂(咨询软件无需打开...
实测推荐“衡阳跑胡子开挂神器”... 实测推荐“衡阳跑胡子开挂神器”(详细开挂教程)您好:衡阳跑胡子这款游戏可以开挂,确实是有挂的,需要了...
今日重大消息“开心休闲辅助开挂... 您好:开心休闲这款游戏可以开挂,确实是有挂的,需要了解加客服微信【5848499】很多玩家在这款游戏...