java线程三种创建方式与线程池的应用

前言:多线程下程序运行的结果是随机的,以下案例代码的运行结果仅供参考

一 通过继承Thread线程创建的方法与执行的步骤

/*
1 继承Thread 2重写run方法 3创建线程继承类的子类
4 调用start(一个线程只能通过调用一次start来进行启动)
 */
class MyThread extends Thread{
    
    
    public MyThread(String name){
    
    
        //调用Thread类中的构造方法才能顺序初始化成功线程名
        super(name);
     }
    @Override
    public void run() {
    
    
        for (int i = 0; i < 3; i++) {
    
    
            //线程名(默认):Thread-0,currentThread是获取当前的线程
            System.out.println(Thread.currentThread().getName() + i);
        }
    }
}

public class ThreadTest {
    
    
    /*
    当前主线程是main线程,我们在此又开了个名为myThread的线程,main线程与thread线程的执行时顺序是随机的
    由于myThread的启动和被分配cpu时间片都需要时间,所以通常是main线程先执行

    只有start才能启动一个线程,启动后需要抢占cpu资源,抢占成功才能执行线程中的run方法,如果直接调用线程的run方法
    相当于在主线程main中正常的调用一个对象myThread的run方法

    若没有手动设置线程名称时,会自动分配一个线程的名称,如线程对象myThread会被自动分配线程名称Thread-0
    设置线程名称是为了区分当前正在执行的线程是哪一个线程,所以在设置线程名称时应避免重复!
     */
    public static void main(String[] args) throws InterruptedException {
    
    
        MyThread myThread = new MyThread("myThread");
        //start方法是由新建态=> 就绪态。有两步①启动线程②调用线程的run方法
        myThread.start();
        System.out.println("========以下是main线程的执行步骤========");
        for (int i = 0; i < 3; i++) {
    
    
            System.out.println(Thread.currentThread().getName()+":"+i);
        }
    }
}

在这里插入图片描述
二 通过实现Runnble的方法

/*
①创建一个Runnable的实现类②实现类去重写接口的run方法③创建实现类的对象
④将该对象作为参数传入到Thread构造器来构造一个Thread类型的对象⑤通过该thread对象调用时start
 */
class MyRun implements Runnable{
    
    
    @Override
    public void run() {
    
    
        for (int i = 0; i < 3; i++) {
    
    
            //线程名(默认):Thread-0,currentThread是获取当前的线程
            System.out.println(Thread.currentThread().getName() + i);
        }
    }
}

public class ThreadTest {
    
    
    public static void main(String[] args) throws InterruptedException {
    
    
        Thread thread = new Thread(new MyRun());
        thread.setName("mythread");
        thread.start();
        System.out.println("========以下是main线程的执行步骤========");
        for (int i = 0; i < 3; i++) {
    
    
            System.out.println(Thread.currentThread().getName()+":"+i);
        }
    }
}

在这里插入图片描述

三、实现Callable的方式
方法三比方法二好,原因①call方法可以有返回值 ②Callable支持泛型③call可以抛出异常信息而run中不能

/*
相比较Runable接口,call()方法新增捕获和抛出异常和返回值的功能,实现步骤如下:
①创建一个Callable的实现类并重写call方法,将线程需要执行的任务写到call方法中
②实例化一个该实现类的对象callable
③将callable作为参数构造一个FutureTask的对象ftt
④将ftt作为参数(Runnable接口类型的引用target)构造Thread对象thread
⑤thread.start()启动线程
补充:call的返回值类型即为Callable的泛型类型,通过ftt.get()可以返回call方法的返回值
 */
class MyCall implements Callable<Integer>{
    
    

    @Override
    public Integer call() throws Exception {
    
    
        for (int i = 0; i < 3; i++) {
    
    
            System.out.println(Thread.currentThread().getName() +  ":" +i);
        }
        return 1;
    }
}

public class ThreadTest {
    
    
    public static void main(String[] args) throws InterruptedException, ExecutionException {
    
    
        Callable<Integer> callable = new MyCall();
        FutureTask<Integer> ftt = new FutureTask<Integer>(callable);
        Thread thread = new Thread(ftt);
        thread.start();
        System.out.println(ftt.get());
    }
}

在这里插入图片描述
四、通过线程池的方式
由于线程的创建与销毁都是很耗费资源的工作,在高并发的情况下对性能影响非常大,解决:可以提前创建多个线程放入线程池,使用的时不是现造而是去线程池中取,使用完不是销毁而是重新放回线程池,避免频繁的创建于销毁,实现重复利用,类似于公交车,优势非常明显,降低响应时间(节约了创建线程的时间)节约系统资源(重复利用线程池的线程减少了创建和销毁线程的频率)提高系统系统,利于管理(例如可以通过参数指定线程池大小,池内最大线程数,线程保持时间)

class MyRun implements Runnable{
    
    
    @Override
    public void run() {
    
    
        System.out.println("这是MyRun");
    }
}

class MyCall implements Callable{
    
    

    @Override
    public Object call() throws Exception {
    
    
        return 1;
    }
}
/*
1 创建一个指定线程数量的线程池对象service  
2 将service强转为ThreadPoolExecutor以便设置线程池的属性
3 需要传入Runnable或Callable接口实现类的对象(匿名对象)作为参数执行指定的线程的操作
4 关闭连接池
*/

public class ThreadTest {
    
    
    public static void main(String[] args) throws InterruptedException, ExecutionException {
    
    
        //1 创建一个指定线程数量的线程池对象service
        ExecutorService service = Executors.newFixedThreadPool(10);
        //2 将service强转为ThreadPoolExecutor以便设置线程池的属性
        ThreadPoolExecutor executor = (ThreadPoolExecutor)service;
        //3 需要传入Runnable或Callable接口实现类的对象(匿名对象)作为参数执行指定的线程的操作
        service.execute(new MyRun());
        service.submit(new MyCall());
        //4 关闭连接池
        service.shutdown();

    }
}

推荐学习:线程的安全问题与三种解决方法

猜你喜欢

转载自blog.csdn.net/wwwwwww31311/article/details/113374042