多线程的三种创建方式

第一种:继承Thread类

主要步骤:

1.让子类继承Thread线程类

2.重写里面的run方法(run方法中写要执行的代码)
3.创建一个Thread对象,代表一个线程
4.启动线程

 每一块代码中都含有详细的注释,一定认真看完

 代码实现:

public class MyThread extends Thread{
    //1.让子类继承Thread线程类
    //2.重写里面的run方法
    @Override
    public void run() {
        //描述线程的执行任务
        for (int i = 0; i < 5; i++) {
            System.out.println("子线程MyThread线程输出" + i);
        }
    }
}
public class ThreadTest1 {
    //掌握线程的创建方式一:继承Thread类
    public static void main(String[] args) {
        //main方法是由一条默认的主线程负责执行

        //3.创建一个Thread对象,代表一个线程
       Thread t = new MyThread();
       //4.启动线程
        t.start();//main线程   t线程

        for (int i = 0; i < 5; i++) {
            System.out.println("主线程main输出" + i);
        }
    }
}

多线程的注意事项:

1.启动线程必须调用start方法,不是调用run方法,若调用run相当于普通方法的调用 ,而调start方法则向向CPU注册线程 

2.不要把主线程的任务放在子线程之前,不然永远是主线程的先跑完,之后才是子线程

 为什么启动一个线程不直接调用run(),而要调用start()启动?

1.如果直接调用run()方法,那么它就只是作为一个普通方法的调用,程序中依然只有一个主线程(main),并且只能顺序执行,需要等待run()方法执行结束后才能继续执行后面的代码。

2.创建线程的目的是为了更充分地利用CPU里面的资源,如果直接调用run()方法,就失去了创建线程的意义。

此方法创建多线程的优缺点

1.优点:编码简单

2.缺点:线程类已经继承了Thread类,无法继承其他类,不利于功能的扩展

 第二种:实现Runnable接口

 主要步骤:

1.定义一个任务类,实现Runnable接口

2.重写runnable中的run方法

3.创建任务对象

4.把任务对象交个一个线程对象处理

 代码实现:

//1.定义一个任务类,实现Runnable接口
public class MyRunnable implements Runnable{
    //2.重写runnable中的run方法

    @Override
    public void run() {
        //线程要执行的任务
        for (int i = 0; i < 5; i++) {
            System.out.println("子线程输出" + i);
        }
    }
}
public class ThreadTest2 {
    //掌握多线程的创建方式二:实现Runnable接口
    public static void main(String[] args) {
        //3.创建任务对象
        Runnable target = new MyRunnable();
        //任务对象没有start()方法
        //4.把任务对象交个一个线程对象处理

//       Thread 提供有参构造器: public Thread(Runnable target)
        new Thread(target).start();

        for (int i = 0; i < 5; i++) {
            System.out.println("主线程main输出" + i);
        }
    }
}

此方法的优缺点:

1.任务类只是实现接口,可以继续继承其他类,实现其它接口,扩展性强 

2.缺点:多创建一个runnalbe对象(可以忽略,相当于没有缺点)

第二种方法的匿名内部类写法

这里直接看代码:对于Lambda的使用,可以去看这里:

https://blog.csdn.net/liusaidh/article/details/135313919?spm=1001.2014.3001.5501

public class ThreadTest2_2 {
    //目标:掌握多线程创建方式二的匿名内部类的写法
    public static void main(String[] args) {
        //1.直接创建一个Runnable接口的匿名内部类形式(任务对象)
        Runnable target = new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 5; i++) {
                    System.out.println("子线程1输出" + i);
                }
            }
        };


        new Thread(target).start();

        //简化形式1:
        new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 5; i++) {
                    System.out.println("子线程2输出" + i);
                }
            }
        }).start();

        //因为Runnable接口为函数式接口,所以可以用Lambda表达式简化
        //简化形式2:
        new Thread(()-> {
                for (int i = 0; i < 5; i++) {
                    System.out.println("子线程3输出" + i);
                }
        }).start();


        for (int i = 0; i < 5; i++) {
            System.out.println("主线程main输出" + i);
        }
    }
}

第三种:用Callable接口和FutureTask类来实现

 前两种线程创建方式都存在一个问题:

假如线程执行完毕后,有一些数据要返回,而他们重写的run方法均不能直接返回结果 

怎么解决这个问题?那就要用到第三种方法了

主要步骤:

1.让这个类实现Callable接口

2.重写call方法 

3.创建一个Callable对象

4.把Callable对象封装成一个FutureTask对象(任务对象)

5.把任务对象交给一个Thread对象//6.获取线程执行完毕后的结果

代码:

import java.util.concurrent.Callable;
//1.让这个类实现Callable接口
public class MyCallable implements Callable {//填写要返回的类型
    private int n;

    public MyCallable(int n) {
        this.n = n;
    }


    //2.重写call方法
    @Override
    public String call() throws Exception {
        //描述线程的任务,返回线程执行后的结果
        //需求:求1-n的和,返回
        int sum = 0;
        for (int i = 1; i <= n; i++) {
            sum += i;
        }
        return "线程求出了1-" + n + "的和是:" + sum;
    }
}

 

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

public class ThreadTest3 {
    public static void main(String[] args) throws Exception {
        //3.创建一个Callable对象
        Callable call = new MyCallable(100);

        //4.把Callable对象封装成一个FutureTask对象(任务对象)
        //未来任务类对象作用:
        //1.是一个任务类对象,实现了Runnable接口
        //2.可以在线程执行完毕后,用未来任务类对象调用get方法获取线程执行完毕后的结果
        FutureTask f1 = new FutureTask<>(call);

        //5.把任务对象交给一个Thread对象
        new Thread(f1).start();

        //6.获取线程执行完毕后的结果
        //注意:如果执行到这里,假如上面的代码还没有执行完毕
        //这里的代码会暂停,等上面的代码执行完毕后才会获取结果

        String s = f1.get();
        System.out.println(s);
    }
}

此方法的优缺点:

1.优点:线程任务类只是实现接口,可以继续继承和实现接口,扩展性强;可以在线程执行完毕后去获取线程的执行结果

2.缺点:编码复杂一点

你可能感兴趣的:(java,算法)