前言:在当今的互联网行业中,多线程技术已经成为了一项非常重要的技能。随着计算机硬件的发展,越来越多的程序员开始关注多线程技术,希望通过多线程来提高程序的性能。Java作为一种广泛使用的编程语言,也提供了丰富的多线程支持。本文将详细介绍Java多线程的基本概念、原理、实现方法以及在生活中的应用,帮助读者更好地理解和掌握Java多线程技术。
假设你正在厨房做饭,而你需要同时炒两个不同的菜。在这个场景中,你可以认为你自己就是一个线程,而这两个菜分别代表另外两个线程。
当你炒第一个菜时,你需要先切好菜,然后放在锅里热油,接着把菜倒入锅中翻炒。这是一个典型的串行过程,因为每一步都必须按照顺序进行。但是,当你炒第二个菜时,如果你仍然按照这种方式来做,那么你需要等待第一个菜完成所有的步骤后才能开始第二个菜。这样显然是低效的。
在这种情况下,利用多线程就可以提高效率。你可以在炒第一个菜的同时,提前准备好第二个菜所需要的材料。当第一个菜还在锅中翻炒的时候,你就可以开始处理第二个菜,比如切菜、热油等。这样一来,就可以节省很多时间。
同样的道理也适用于编程。在单线程的情况下,如果程序中包含多个任务,那么必须等到一个任务完成后才能开始下一个任务。而在多线程的情况下,我们可以将不同的任务分配给不同的线程,使得它们可以同时运行。这样可以显著提高程序的效率。
先创建一个类继承Thread类,并重写run()方法,run方法里面写这个线程要执行的代码
class thread extends Thread{
@Override
public void run(){
//thread.currentThread().getName()得到当前线程的名字
System.out.println("线程启"+thread.currentThread().getName()+"动了");
}
}
然后创建对象,调用start方法启动线程
public static void main(String[] args) {
thread thread = new thread();
thread.start();
}
先创建一个类实现Runnable接口,再实现run()方法。
class thread1 implements Runnable{
public void run(){
System.out.println("线程启"+thread.currentThread().getName()+"动了");
}
}
再创建一个Thread对象并将已经实现Runnable接口的对象作为参数传入。同样通过start方法启动线程
Thread thread1 = new Thread(new thread1());
thread1.start();
注意 两种方法都不要直接通过run方法启动线程,因为直接调用run方法程序只会在同一个线程执行任务——不会新创建一个线程;其实调用start方法的时候,会先创建一个线程再执行run方法
建议大家用第二种,还是举做饭的例子:假如你现在要做连个菜,一个是炒菜、一个是披萨。如果用第一种方式,你要分别创建两个类来继承Thread。
public class Main {
public static void main(String[] args) {
CookFriedRice cookFriedRice = new CookFriedRice();
CookPizza cookPizza = new CookPizza();
cookFriedRice.start();
cookPizza.start();
}
}
class CookFriedRice extends Thread{
@Override
public void run(){
System.out.println("开始炒菜了!!!");
}
}
class CookPizza extends Thread{
@Override
public void run(){
System.out.println("开始做披萨了!!!");
}
}
如果采用实现Runnable接口来创建线程,那么我们只需要定义两个实现了Runnable接口的类,诶不是也要创建连个类吗???这里可用lambda表达式(不熟悉的朋友可以看这篇文章lambda表达式)
public class Main {
public static void main(String[] args) {
new Thread(()-> System.out.println("开始炒菜了!!!")).start();
new Thread(()-> System.out.println("开始做披萨了!!!")).start();
}
}
代码是不是简洁了许多,并且不同的线程还可依操作同一个对象,实现信息共享
public class Main {
public static void main(String[] args) {
test test = new test();
Thread thread1 = new Thread(test);
Thread thread2 = new Thread(test);
thread1.start();
thread2.start();
}
}
class test implements Runnable {
private int count = 0;
public void run() {
count++;
System.out.println(count);
}
}
线程的状态与生命周期
Java中的线程有6种状态,分别是:新建(New)、就绪(Runnable)、运行(Running)、阻塞(Blocked)、等待(Waiting)和终止(Terminated)。线程的生命周期包括以下几个阶段:新建、就绪、运行、阻塞、等待和终止。
我们还是通过做饭的例子来辅助理解:
在厨房里,厨师们正在准备一道菜,而每个厨师就像一个独立的任务处理器(也就是线程)。他们都有自己的任务列表,并且按照各自的顺序处理它们。
厨师的生命状态可以分为以下几个阶段:
新建状态 (New): 厨师被招聘进来并分配到相应的厨房区域进行工作。
就绪状态 (Runnable): 厨师准备好开始工作,等待主厨分配任务。
阻塞状态 (Blocked): 厨师被其他厨师暂时阻止了前进,例如在等用具清理完毕时。
运行状态 (Running): 厨师开始着手准备菜肴。
终止状态 (Terminated): 厨师完成了自己的任务并离开了厨房。
细节:
其实一个线程调用start后,程序可能在远行也可能没有远行,这取决于系统分配给这个线程的时间(在Java规范中,没有将正在远行单独当作一个状态,一个正在运行的线程也有可能处于可远行状态),系统会为每个线程分配时间片,时间片用完了cpu就会将运行的机会给下一个线程,正是因为这个过程非常快,我们察觉不到切换的过程,以至于这些线程看起来都是同时运行。大家可以类比视频和一帧帧照片。
下面介绍几个方法
1.yield(静态方法)
当线程在运行时,将运行的机会让给别的线程(礼让),但礼让不一定成功,只是将运行权交给了cpu,让cpu重新调度
2. join
这个方法可以让注线程阻塞,等待子线程结束为止
看个例子
public class Main {
public static void main(String[] args) throws InterruptedException {
Thread thread1 = new Thread(()-> {
for (int i = 0; i < 100; i++) {
System.out.println(i+": 子线程在运行!!!");
}
});
thread1.start();
for (int i = 0; i <100 ; i++) {
System.out.println(i+": 主线程在运行!!!");
}
}
public class Main {
public static void main(String[] args) throws InterruptedException {
Thread thread1 = new Thread(()-> {
for (int i = 0; i < 100; i++) {
System.out.println(i+": 子线程在运行!!!");
}
});
thread1.start();
for (int i = 0; i <100 ; i++) {
if(i%2==0)
thread1.join();
System.out.println(i+": 主线程在运行!!!");
}
}
}
本期内容就到这里,如果觉得写的不错的话可以关注下一期。
下期预告: