大数据必学Java基础(七十六):创建线程的三种方式

Posted Lansonli

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了大数据必学Java基础(七十六):创建线程的三种方式相关的知识,希望对你有一定的参考价值。

文章目录

创建线程的三种方式

一、继承Thread类

二、实现Runnable接口

三、实现Callable接口


创建线程的三种方式

一、继承Thread类

在学习多线程之前,以前的代码是单线程的吗?

不是,以前也是有三个线程同时执行的。

现在我想自己制造多线程  --》 创建线程 ?

线程类  --》 线程对象

package com.lanson.test01;

/**
 * @author : Lansonli
 * 线程类叫:TestThread,不是说你名字中带线程单词你就具备多线程能力了(争抢资源能力)
 * 现在想要具备能力,继承一个类:Thread,具备了争抢资源的能力
 */
public class TestThread extends Thread
    /*
    一会线程对象就要开始争抢资源了,这个线程要执行的任务到底是啥?这个任务你要放在方法中
    但是这个方法不能是随便写的一个方法,必须是重写Thread类中的run方法
    然后线程的任务/逻辑写在run方法中
     */
    @Override
    public void run() 
        //输出1-10
        for (int i = 1; i <= 10 ; i++) 
            System.out.println(i);
        
    
package com.lanson.test01;

/**
 * @author : Lansonli
 * 测试类
 */
public class Test 
    //这是main方法,程序的入口
    public static void main(String[] args) 
        //主线程中也要输出十个数:
        for (int i = 1; i <= 10 ; i++) 
            System.out.println("main1-----"+i);
        

        //制造其他线程,要跟主线程争抢资源:
        //具体的线程对象:子线程
        TestThread tt = new TestThread();
        //tt.run();//调用run方法,想要执行线程中的任务 -->这个run方法不能直接调用,直接调用就会被当做一个普通方法
        //想要tt子线程真正起作用比如要启动线程:
        tt.start();//start()是Thread类中的方法

        //主线程中也要输出十个数:
        for (int i = 1; i <= 10 ; i++) 
            System.out.println("main2-----"+i);
        
    

运行结果:

 

设置读取线程名字 

1、setName,getName方法来进行设置读取

package com.lanson.test01;

/**
 * @author : Lansonli
 * 线程类叫:TestThread,不是说你名字中带线程单词你就具备多线程能力了(争抢资源能力)
 * 现在想要具备能力,继承一个类:Thread,具备了争抢资源的能力
 */
public class TestThread extends Thread
    /*
    一会线程对象就要开始争抢资源了,这个线程要执行的任务到底是啥?这个任务你要放在方法中
    但是这个方法不能是随便写的一个方法,必须是重写Thread类中的run方法
    然后线程的任务/逻辑写在run方法中
     */
    @Override
    public void run() 
        //输出1-10
        for (int i = 1; i <= 10 ; i++) 
            System.out.println(this.getName()+i);
        
    
package com.lanson.test01;

/**
 * @author : Lansonli
 * 测试类
 */
public class Test 
    //这是main方法,程序的入口
    public static void main(String[] args) 
        //给main方法这个主线程设置名字:
        //Thread.currentThread()作用获取当前正在执行的线程
        Thread.currentThread().setName("主线程");
        //主线程中也要输出十个数:
        for (int i = 1; i <= 10 ; i++) 
            System.out.println(Thread.currentThread().getName()+"1-------"+i);
        

        //制造其他线程,要跟主线程争抢资源:
        //具体的线程对象:子线程
        TestThread tt = new TestThread();
        tt.setName("子线程");
        //tt.run();//调用run方法,想要执行线程中的任务 -->这个run方法不能直接调用,直接调用就会被当做一个普通方法
        //想要tt子线程真正起作用比如要启动线程:
        tt.start();//start()是Thread类中的方法

        //主线程中也要输出十个数:
        for (int i = 1; i <= 10 ; i++) 
            System.out.println(Thread.currentThread().getName()+"2-------"+i);
        
    

2、通过构造器设置名字

package com.lanson.test01;

/**
 * @author : Lansonli
 * 线程类叫:TestThread,不是说你名字中带线程单词你就具备多线程能力了(争抢资源能力)
 * 现在想要具备能力,继承一个类:Thread,具备了争抢资源的能力
 */
public class TestThread extends Thread
    public TestThread(String name)
        super(name);//调用父类的有参构造器
    
    /*
    一会线程对象就要开始争抢资源了,这个线程要执行的任务到底是啥?这个任务你要放在方法中
    但是这个方法不能是随便写的一个方法,必须是重写Thread类中的run方法
    然后线程的任务/逻辑写在run方法中
     */
    @Override
    public void run() 
        //输出1-10
        for (int i = 1; i <= 10 ; i++) 
            System.out.println(this.getName()+i);
        
    

 

习题:买火车票

1、原理:每个窗口都是一个线程对象

2、代码

package com.lanson.test02;

/**
 * @author : Lansonli
 */
public class BuyTicketThread extends Thread 
    public BuyTicketThread(String name)
        super(name);
    
    //一共10张票:
    static int ticketNum = 10;//多个对象共享10张票
    //每个窗口都是一个线程对象:每个对象执行的代码放入run方法中
    @Override
    public void run() 
        //每个窗口后面有100个人在抢票:
        for (int i = 1; i <= 100 ; i++) 
            if(ticketNum > 0)//对票数进行判断,票数大于零我们才抢票
                System.out.println("我在"+this.getName()+"买到了从北京到哈尔滨的第" + ticketNum-- + "张车票");
            
        
    
package com.lanson.test02;

/**
 * @author : Lansonli
 */
public class Test 
    public static void main(String[] args) 
        //多个窗口抢票:三个窗口三个线程对象:
        BuyTicketThread t1 = new BuyTicketThread("窗口1");
        t1.start();
        BuyTicketThread t2 = new BuyTicketThread("窗口2");
        t2.start();
        BuyTicketThread t3 = new BuyTicketThread("窗口3");
        t3.start();
    

二、实现Runnable接口

代码:

package com.lanson.test03;

/**
 * @author : Lansonli
 * TestThread实现了这个接口,才会变成一个线程类
 */
public class TestThread implements Runnable
    @Override
    public void run() 
        //输出1-10数字:
        for (int i = 1; i <= 10 ; i++) 
            System.out.println(Thread.currentThread().getName()+"----"+i);
        
    
package com.lanson.test03;

/**
 * @author : Lansonli
 */
public class Test 
    public static void main(String[] args) 
        //创建子线程对象:
        TestThread tt = new TestThread();
        Thread t = new Thread(tt,"子线程");
        t.start();

        //主线程里面也是打印1-10数字:
        for (int i = 1; i <= 10 ; i++) 
            System.out.println(Thread.currentThread().getName()+"---"+i);
        
    

运行结果:

习题:买火车票

代码

package com.lanson.test04;

/**
 * @author : Lansonli
 */
public class BuyTicketThread implements Runnable 
    int ticketNum = 10;
    @Override
    public void run() 
        for (int i = 1; i <= 100 ; i++) 
            if(ticketNum > 0)
                System.out.println("我在"+Thread.currentThread().getName()+"买到了北京到哈尔滨的第" + ticketNum-- + "张车票");
            
        
    
package com.lanson.test04;

/**
 * @author : Lansonli
 */
public class Test 
    //这是main方法,程序的入口
    public static void main(String[] args) 
        //定义一个线程对象:
        BuyTicketThread t = new BuyTicketThread();
        //窗口1买票:
        Thread t1 = new Thread(t,"窗口1");
        t1.start();
        //窗口2买票:
        Thread t2 = new Thread(t,"窗口2");
        t2.start();
        //窗口3买票:
        Thread t3 = new Thread(t,"窗口3");
        t3.start();
    

实际开发中,方式1 继承Thread类   还是  方式2 实现Runnable接口这种方式多呢?

方式2

  1. 方式1的话有 Java单继承的局限性,因为继承了Thread类,就不能再继承其它的类了
  2. 方式2的共享资源的能力也会强一些,不需要非得加个static来修饰

Thread类 Runnable接口 有联系吗?

三、实现Callable接口

对比第一种和第二种创建线程的方式发现,无论第一种继承Thread类的方式还是第二种实现Runnable接口的方式,都需要有一个run方法, 但是这个run方法有不足:

  • 没有返回值
  • 不能抛出异常

基于上面的两个不足,在JDK1.5以后出现了第三种创建线程的方式:

实现Callable接口

实现Callable接口好处:

(1)有返回值  

(2)能抛出异常

缺点:线程创建比较麻烦

package com.lanson.test05;

import java.util.Random;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

/**
 * @author : Lansonli
 */
public class TestRandomNum implements Callable<Integer> 
    /*
    1.实现Callable接口,可以不带泛型,如果不带泛型,那么call方式的返回值就是Object类型
    2.如果带泛型,那么call的返回值就是泛型对应的类型
    3.从call方法看到:方法有返回值,可以跑出异常
     */
    @Override
    public Integer call() throws Exception s
        return new Random().nextInt(10);//返回10以内的随机数
    


class Test
    //这是main方法,程序的入口
    public static void main(String[] args) throws ExecutionException, InterruptedException 
        //定义一个线程对象:
        TestRandomNum trn = new TestRandomNum();
        FutureTask ft = new FutureTask(trn);
        Thread t = new Thread(ft);
        t.start();
        //获取线程得到的返回值:
        Object obj = ft.get();
        System.out.println(obj);
    

 


 

  • 📢博客主页:https://lansonli.blog.csdn.net
  • 📢欢迎点赞 👍 收藏 ⭐留言 📝 如有错误敬请指正!
  • 📢本文由 Lansonli 原创,首发于 CSDN博客🙉
  • 📢停下休息的时候不要忘了别人还在奔跑,希望大家抓紧时间学习,全力奔赴更美好的生活✨

 

以上是关于大数据必学Java基础(七十六):创建线程的三种方式的主要内容,如果未能解决你的问题,请参考以下文章

大数据必学Java基础(七十八):线程安全问题

大数据必学Java基础(七十七):线程的生命周期和常见方法

大数据必学Java基础(七十五):多线程与程序进程线程之间概念详解

大数据必学Java基础(七十九):线程通信问题

大数据必学Java基础(二十五):数组的三种初始化方式

大数据必学Java基础(七十四):对象流ObjectInputStream和ObjectOutputStream介绍