多线程并发学习笔记(一):创建新线程的四种方式

什么是多线程?

通俗来说,一个.exe文件就是一个进程,一个进程可以包括多个线程。线程可以同时执行,每个线程处理不同的模块。比如在一个文本编辑器中,线程1用来接受用户键盘输入的数据,线程2显示数据到界面上,线程3保存数据,如果变成单线程,用户要输完数据后才能看见输入的数据并保存,用户体验非常不好。又比如服务器端响应浏览器请求时要完成多个文件下载,这也需要多线程。

———————————————————————————————————————————

创建新线程的四种方式

1.继承Thread类

public class myThread extends Thread{
/*
* 线程执行的代码在run()方法中
* */
    @Override
    public void run(){
        System.out.println(Thread.currentThread().getName());
        System.out.println("<这是一个子线程>");
    }

    public static void main(String[] args) {
        //启动线程是start()方法,不是run()方法
        System.out.println(Thread.currentThread().getName());
        System.out.println("<这是一个主线程>");
        new myThread().start();//调用start()方法不会立即被cpu执行,等待cpu调度从就绪状态到执行状态
        System.out.println("<主线程执行结束>");

    }
}

继承Thread类并且改写run()方法,要该线程要执行的代码块写入run()方法中。Thread.currentThread().getName()方法可以得到目前线程的名字。该程序运行的结果:

main
<这是一个主线程>
<主线程执行结束>
Thread-0
<这是一个子线程>

(tip:子线程并不一定是在主线程运行结束后运行的)

———————————————————————————————————————————

2.实现Runnable接口

public class ThreadRunnable implements Runnable{

    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName());
        System.out.println("<这是一个子线程>");
    }

    public static void main(String[] args) {
        new Thread(new ThreadRunnable(),"线程名20220403").start();
        //使用匿名内部类实现Runnable
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName());
                System.out.println("<这是一个子线程>");
            }
        }).start();
        //使用lambda表达式实现Runnable
        new Thread(()->{
            System.out.println(Thread.currentThread().getName());
            System.out.println("<这是一个子线程>");
        }).start();
    }
}

通过实现Runnable接口,改写其中的run()方法,在通过构造一个Thread对象来start它。

因为Runnable接口是函数式接口,所以可以用匿名内部类或者lambda表达式实现它。

上面的代码中就正常的实现了一个Runnable接口、用匿名内部类实现了一个Runnable接口、用lambda表达式实现了一个Runnable接口。在分别new Thread(Runnable r).start()创建了各自的新线程。(第一个new Thread中第二个参数用来改变这个线程的名字。)运行结果如下:

线程名20220403
<这是一个子线程>
Thread-0
<这是一个子线程>
Thread-1
<这是一个子线程>

———————————————————————————————————————————

3.实现Callable,再配合FutureTask创建新线程

public class CallableThread implements Callable<Integer> {
    @Override
    public Integer call() throws Exception {
        System.out.println(Thread.currentThread().getName()+"正在运行");
        System.out.println(Thread.currentThread().getName()+"运行结束,返回1");
        return 1;
    }

    public static void main(String[] args) {
        System.out.println(Thread.currentThread().getName()+"正在运行");
        CallableThread ct=new CallableThread();
        FutureTask<Integer> integerFutureTask=new FutureTask<Integer>(ct);
        new Thread(integerFutureTask).start();
        Integer result= null;
        try {
            result = integerFutureTask.get();//如果将获取返回值的语句注释掉
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName()+"运行结束,"+result);
    }
}

不同于Thread类和Runnable接口,Callable接口改写的是call()方法,并且可以指定一个泛型。

构建一个Callable接口实现类的对象,作为FutureTask构造对象的参数。(Callable与FutureTask指定的泛型需一致)再通过构造一个Thread对象来start FutureTask。

用Callable接口,真正的不同就是增加了一个返回值,当主线程或者其他线程需要这个返回值时,就会先完成Callable实现的接口,另所需要值的线程阻塞。

上面代码的运行结果:

main正在运行
Thread-0正在运行
Thread-0运行结束,返回1
main运行结束,1

如果将result获取返回值的语句去掉:

main正在运行
Thread-0正在运行
main运行结束,null
Thread-0运行结束,返回1

当main线程不需要Callable线程的返回值时,main线程可以先运行结束,不必等待。

(这里让子线程休眠了三秒钟,不然一般情况下子线程运行结束的比较快,

Thread.sleep(3000);//令当前线程休眠3000ms)

———————————————————————————————————————————

4.通过线程池创建新线程

public class myThread2 {
    public static void main(String[] args) {
        ExecutorService executorService= Executors.newCachedThreadPool();
        executorService.execute(()->{
            System.out.println(Thread.currentThread().getName());
            System.out.println("<这是一个子线程>");
        });
    }
}

简单的构造一个线程池对象,在execute()一下,execute()语句中用lambda表达式,相当于实现Runnable接口。

运行结果:

pool-1-thread-1
<这是一个子线程>

———————————————————————————————————————————

5.Spring@Async注解实现

(不是很懂,摊手手,小伙伴们可以自己查一下或者大佬在评论区给个方向吧)

———————————————————————————————————————————

在CPU上,线程其实并不是同时运行的。

一旦一个线程开始运行,它不一定始终保持运行。事实上,运行中的线程有时需要暂停,让其他线程有机会运行。线程调度的细节依赖于操作系统提供的服务。抢占式调度系统给每一个可运行线程一个时间片来执行任务。当时间片用完时,操作系统剥夺该线程的运行权,并给另一个线程一个机会来运行。当选择下一个线程时,操作系统会考虑线程的优先级。——《JAVA核心技术》

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值