简单学习->线程的创建

线程的概念

线程(Thread) ,也叫"轻量级进程",是为了实现并发编程而引入的.一个进程里可以有多个线程,像我们人体也由多个细胞组织构造,各个组织相互配合;

(关于线程,大家可以看看这里简单学习->线程的概念-CSDN博客来了解线程)

创建线程

如何使用java来创建线程

线程是操作系统里的概念,我们不能直接就操作线程,而是使用操作系统提供的api来操作线程;而 Java针对系统api进行了封装,这样我们就可以直接使用Java就可以操作线程 ;

Thread 类

在Java中,我们使用Thread 类就可以进一步操作线程,可以使用Thread类来创建线程 ;

Thread类里有两个重要的方法

  1. run()
  2. start()
run() 方法

我们创建一个线程时,是需要这个线程完成一些工作,那么在创建线程时,就告诉这个线程,要做什么工作,将任务"交给"线程;

run() 方法: 描述了线程要完成的任务;(当线程完成了它的任务,也就是线程的run方法执行完了,那么线程就会自动销毁)

例如:

(线程的run方法里是打印"hello world",那么这个线程的任务就是打印"hello world") 

start() 方法

run()方法只是描述了线程要做什么,怎么创建线程? => 就是通过 start() 方法, 来把线程创建出来;

start()方法: 会真正的调用系统api , 创建出线程,然后让线程完成run()里面的任务;

(run()方法我们一般不会主动调用,我们应该调用start()方法,通过start()方法调用系统api创建出线程,让线程去调用run()方法,让线程去完成run()里的任务) 

创建一个线程

我们来简单学习,如何通过Thread类来创建一个线程,并且让线程执行任务;

1.创建一个MyThread类, 继承Thread类,并且MyThread类重写Thread的run()方法 ;

(此时,重写的run方法里的就是线程要执行的操作)

2.通过MyThread构造出对象,然后调用start() 方法 ;(start是Thread类里原有的方法)

(通过start()方法来调用系统api,创建出线程)

3.执行结果输出: hello world 

虽然我们没有手动调用run()方法,但是通过start()方法,创建出线程,线程就会自行调用run()方法,完成run()里面的工作;

代码

//创建MyThread继承Thread类
class MyThread extends Thread{
    @Override
    public void run() {                        //重写Thread的run方法
        System.out.println("hello world");    //run方法里就是线程要完成的任务
    }
}

public class Test3 {
    public static void main(String[] args) {
        //创建我们自己的MyThread对象,来操作线程 ;
        MyThread myThread = new MyThread() ;
        myThread.start();   //执行start()方法,调用系统api创建出线程 ;
    }
}

 线程和线程之间都是相互独立的,并发执行的,这里我们将稍微修改一下代码:

运行输出结果

可以看到一会输出main,一会输出MyThread;

因为要运行一个Java进程,里面就要有一个主线程,而这个主线程的入口方法就是main();

(可以试试写一个类,但是不写main方法,然后点击运行,idea会报错,因为没有main入口方法)

代码的基本执行顺序:

(最后两个线程都在while死循环里,由于并发执行,可以看到一会输出"main",一会输出"MyThread")

使用jconsole 来观察线程 ;

jconsole是JDK自带的工具,可以观察java进程里的多线程的情况 ;

使用jconsole

1.jconsole在我们安装的JDK的bin目录下 ;

2.执行我们的多线程代码,保证代码是在运行中; 

//创建MyThread继承Thread类
class MyThread extends Thread{
    @Override
    public void run() {
        while (true){
            System.out.println("MyThread");
        }
    }
}

public class Test3 {
    public static void main(String[] args) {
        MyThread myThread = new MyThread() ;
        myThread.start();
        while (true){
            System.out.println("main");
        }
    }
}

3. 双击运行桌面上的jconsole ;

如果这里的本地进程没有你的进程,那么返回idea查看自己的代码是否是正在运行,或者右击jconsole,使用管理员身份运行 ;

4.点击不安全连接 

5.在这点击线程,我们就可以看到,目前正在运行的的线程的情况 ;

可以在下方可以看到各个线程和线程的信息

(左边是各个线程,右边是线程的信息) 

通过jconsole就可以看到我们的确是用start()创建出了线程 ;(在运行java进程,除了我们创建的,还有很多其他线程)

了解怎么创建线程,就再来详细了解run 和 start 的区别

详细了解run() 和 start() 的区别 

1.run()是线程的入口方法,描述了线程要做什么,不会创建出线程;

2.start()则是会调用系统api,在系统中创建出线程,然后在让线程去调用run();

3.run()方法是线程的入口方法,我们不直接调用run(),而是让start()间接去调用run() ;

举个例子 

厂里的螺丝太多了,打不完,我要雇佣人来帮我"打螺丝";  那么 "打螺丝"这个任务就是run(), "雇佣别人" 就是 start() ;

 我会自己打螺丝吗? 当然不会. 我要雇佣别人, 来帮我打螺丝(所以我自己不会调用run()方法) , 那么我就雇佣别人(start) 过来, 然后别人 就会 完成 打螺丝(run) 这个任务, 当螺丝打完了(run方法执行完了), 那么雇佣过来的人就可以 领工资 解散了(run执行完了,线程销毁了);

代码例子:

1.调用start() 方法, 雇佣别人来打螺丝; 

 运行结果:

(运行结果两个线程并发执行,可以在jconsole里看到有两个线程)

如果说main主线程是"我自己" , 那么Thread-0线程就是 我雇佣 来的人 ;

2.不调用start()方法,直接调用run()方法, 不雇佣别人,自己打螺丝;(这样我就不用打螺丝,而是在监工, 螺丝是工人在打) 

运行结果

(一直在输出打螺丝", jconsole里也只有main里一个主线程) 

如果main线程是"我自己"的话,这不调用start(),而是直接调用run()方法,那么就只有 "我自己" 在打螺丝(不调用start()创建线程,那么就是普通的方法调用,调用了run方法,run里是个死循环,就一直输出"打螺丝",不会执行到下面的 "我是监工,不打螺丝"); 

代码:

class WorkerThread extends Thread {
    @Override
    public void run() {
        while (true){
            System.out.println("打螺丝");
        }
    }
}

public class Test1 {
    public static void main(String[] args) {
          WorkerThread worker = new WorkerThread();
          worker.run();   // 直接调用run
          //worker.start();    // 调用 start() , 雇佣别人来打螺丝
          while (true){
              System.out.println("我是监工,不打螺丝");   
          }
    }
}

 创建线程的常见方法

1. 继承Thread , 重写run方法 
class MyThread extends Thread {
    @Override
    public void run() {
        System.out.println("继承Thread , 重写run");
    }
}

public class Test2 {
    public static void main(String[] args) {
        MyThread myThread = new MyThread();
        myThread.start();
    }
}
2. 实现 Runnable 接口, 重写run()方法

上面 的 继承Thread,重写的run()方法; 这个run任务, 只有MyThread对象创建的线程才能使用,如果我们要其他线程也 能够去执行这个run任务 => 利用 Runnable ;

Runnable 接口里只有一个 run()方法; 可以把Runnable看作一个单独的可执行的任务, 这个任务可以 给这个线程执行,也可以交给 别的线程 执行;(我这个Runnable任务,可以给这个Thread创建的线程来执行, 也可以 给别的 实体来执行);

通过向上转型,创建Runnable对象, 然后用Runnable对象,构造出Thread对象 ;

class MyRunnable implements Runnable{       // 创建MyRunnable类实现 Runnable接口
    @Override
    public void run() {                      //重写Runnable的 run 方法 ;
        System.out.println("My Runnable");
    }
}
public class Test2 {
    public static void main(String[] args) {
        //通过向上转型,创建出Runnable对象 ,
        Runnable runnable = new MyRunnable();
        // 然后用 Thread对象来接收 Runnable对象
        Thread t1 = new Thread(runnable);
        t1.start();   // 此时 t1 创建出来的 线程,会执行上面 MyRunnable里的 run任务 ;
        
        // 不止是 t1 可以用, 其他 Thread对象也能执行这个Runnable任务;
        Thread t2 = new Thread(runnable);
        t2.start();   
    }
}

 运行结果:

 (这样将要执行的任务提取出来,就不只是一个对象能用,其他的也执行这个Runnable任务 )

 使用 实现Runnable创建线程 和 继承Thread创建线程 的区别

1.解耦合: 降低 任务 和 Thread的耦合; (同一个螺丝, 我可以给路人甲打, 我也可以给路人乙打,就不会说非要这路人甲才能打这个螺丝)

2.Runnable是一个 单独的可执行的 任务, 这个任务可以交给Thread创建的线程来执行,也可以给其他实体来执行(这个Runnable任务,能给Thread创建的线程来执行,也可以给别的,后面我们会学习到)

3.创建线程主要有两步: 1.是确定要执行的任务是什么 2.是调用系统api创建出线程(我们用Runnable就可以把任务单独提取出来)

3. 继承Thread,重写run方法 (用匿名内部类)

前面1.的还要专门创建类出来,没有必要;

创建Thread对象时,使用匿名内部类,继承Thread,重写run ;

public class Test2 {
    public static void main(String[] args) {
       Thread t1 = new Thread(){
           @Override
           public void run() {
               System.out.println("匿名内部类,继承Thread,重写run");
           }
       };
       t1.start();
    }
}

这样就创建了一个 继承Thread 并且重写了 run方法 的匿名内部类, 然后用 Thread 来接收;就不用特意创建一个类出来 ; 

4. 实现Runnable接口,重写run方法(使用匿名内部类)

上面2.实现Runnable接口,重写run方法,还要创建一个类出来,也有的没必要;

创建一个 实现 Runnable接口, 重写run方法的匿名内部类对象,然后用 Runnable 来接收

 public static void main(String[] args) {
           Runnable runnable = new Runnable() {     
               @Override
               public void run() {
                   System.out.println("匿名内部类,实现Runnable,重写run");
               }
           };
           Thread t1 = new Thread(runnable);
           t1.start();
    }

创建了一个 实现 Runnable接口 ,重写run方法的 匿名内部类对象 , 并用 Runnable 来接收,就不要特意创建一个类 ;

当然,也可以更便捷,将匿名内部类对象,直接用来构造 Thread对象 ;

public class Test2 {
    public static void main(String[] args) {
           Thread t2 = new Thread(new Runnable(){  // 创建出来的匿名内部类对象,直接传给Thread ;
               @Override
               public void run() {
                   System.out.println("匿名内部类,实现Runnable,重写run,");
               }
           });
           t2.start();
    }
}

构造Thread对象时可以传Runnable,所以传一个上面的匿名内部类对象也是可以的 ; 

5.使用 Lambda 表达式 (最常用)

Runnable接口里只有 一个 run 方法 , 所以Runnable本来就是一个 函数式接口 ;

而且可以用 Runnable任务 ,来构造Thread 对象 ;

在创建 Thread对象时 ,使用 Lambda 表达式 ,代替上面 4.的匿名内部类对象, 作为参数来构造Thread 对象 ;

public class Test2 {
    public static void main(String[] args) {
         Thread t1 = new Thread(()->{     // Lambda代替 Runnable ;
             System.out.println("Lambda表达式"); //run没有返回值,所以不需要return
         });
        t1.start();
    }
}

使用Lambda表达式来创建出线程,是最常用的方式,方便,快捷;但上面的方法最后都不要忘记调用start() ;

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值