初识多线程

Posted ccsert

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了初识多线程相关的知识,希望对你有一定的参考价值。

多线程学习

  • 什么是线程
  • Java多线程的使用

    什么是线程

    这里先引用一下百度给出的概念

    线程是系统能够进行运算调度的最小单位。他被包含在进程只中,是进程中的实际运作单位。

这里只摘取简单的引用,便于通俗的理解,毕竟百度百科上面的东西太过于专业化,专业名词太多。

上面有一个名词进程,这里解释一下进程

一般我们打开一个软件,比如qq,微信,这个时候就启动了一个进程,在Windows任务管理器中我们可以看到它们
技术图片
红框部分就是一个进程

好了我们大概算是通俗的理解了什么是进程
在当代面向线程设计的计算机结构中,进程是线程的容器。程序是指令、数据及其组织形式的描述,进程是程序的实体。

这里用一个简单的例子演示一下

public class test1 {

    private static class ther extends Thread {
        @Override
        public void run() {
            for (int i = 0; i < 10; i++) {
                try {
                    TimeUnit.MICROSECONDS.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("启动了一个线程");
            }
        }
    }

    public static void main(String[] args)  {
        ther ther = new ther();
        ther.start();
        for (int i = 0; i < 10; i++) {
            try {
                TimeUnit.MICROSECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("主函数执行了");
        }
    }
}

我们看一下执行结果,暂时不要理会现在的写法

主函数执行了
启动了一个线程
主函数执行了
启动了一个线程
主函数执行了
启动了一个线程
启动了一个线程
主函数执行了
主函数执行了
启动了一个线程
主函数执行了
启动了一个线程
主函数执行了
启动了一个线程
主函数执行了
启动了一个线程
启动了一个线程
主函数执行了
主函数执行了
启动了一个线程

我们可以看见它是交替运行的
平时我们的代码都是串行的,也就是顺序执行,而现在这钟情况就是并行的状态,这就是线程一个比较直观的概念

Java多线程的使用

线程的创建

这里列举几种线程常用的几种方式

通过继承Thread然后实现它的run()方法来进行创建线程

private static class exThread extends Thread{
        @Override
        public void run() {
            System.out.println("继承thread实现run方法创建线程");
        }
    }

通过实现Runnable创建一个线程

private static class impRunnable implements Runnable{
        @Override
        public void run() {
            System.out.println("通过实现Runnable创建一个线程");
        }
    }

通过实现Callable创建一个线程,这种创建方式是有返回池的

private static class impCallable implements Callable{
        @Override
        public Object call() throws Exception {
            System.out.println("通过实现Callable创建一个线程,它是有放回值的");
            return "ssuccess";
        }
    }

线程的启动

这里列举5钟启动线程的方式

public static void main(String[] args) {
        new exThread().start();//通过直接创建对象然后调用start();方法启动一个线程
        new Thread(new exThread()).start();//通过new一个线程类然后传入需要启动的线程,然后调用线程类的start方法启动
        new Thread(()->{
            System.out.println("lambda表达式创建并启动一个线程");
        }).start();
        Thread thread = new Thread(new FutureTask<String>(new impCallable()));
        thread.start();//通过newThread传入FutureTask启动
        ExecutorService executorService = Executors.newCachedThreadPool();
        executorService.execute(()->{
            System.out.println("线程池启动并创建");
        });
        executorService.shutdown();//停止
    }

执行结果如下

继承thread实现run方法创建线程
继承thread实现run方法创建线程
lambda表达式创建并启动一个线程
通过实现Callable创建一个线程,它是有放回值的
线程池启动并创建

稍微总结一下:
线程常用的创建并启动的方式有两种
1.通过new Thread().start();
2.通过new Thread(Runnable).start();
还有一种是使用线程池,不过本质上还是使用上面的两种方式
当然你也可以使用lambda表达式,本质上没什么区别写法不同而已

线程的方法

sleep线程休眠

这个方法很有意思,比如说,你可在你的代码某些业务中加个线程休眠5分钟,然后老板说你这个程序太慢了,你就可以说加钱调优,还能按照百分比调整程序的运行速度。是不是很实用?
开个玩笑,开个玩笑
我们来看下使用方式

public static void main(String[] args) {
        testSleep();
    }
    static void  testSleep(){
        new Thread(()->{
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("我先睡一会");
        }).start();
    }

执行结果我们可以看出它过了一会才执行
线程休眠以后什么时候醒过来,根据sleep里的参数决定,时间到了就醒了
线程休眠通俗理解:就是当前我不运行了,别的线程照样可以运行

Yield方法,翻译过来是让步的意思

我们先来看一下用法然后在来解释一下含义

static void testYield(){
        new Thread(()->{
            for (int i=0;i<100;i++){
                System.out.println("A----"+i);
                if (i%10==0){
                    Thread.yield();
                }
            }
        }).start();;

        new Thread(()->{
            for (int i=0;i<100;i++){
                System.out.println("B----"+i);
                if (i%10==0){
                    Thread.yield();
                }
            }
        }).start();;
    }

这里就不演示打印结果了,不是很有代表性
讲一下这个方法的作用

一个线程在执行过程中假如调用yieid这个方法,当前线程会先停止下来进入等待队列,这也就是让步的意思,但是当回到等待队列以后,在系统的调度算法里,它依然有可能把刚回到等待队列的线程继续执行,当然更大的可能性还是先执行之前等待队列的线程,所以yieid的意思是,我虽然让出了,但是能不能抢到执行机会我不管。

join方法,也就是加入的意思

先看写法

Thread thread2 = new Thread(() -> {
            System.out.println("thread1执行了一下,现在到我执行了");
        });

        Thread thread1 = new Thread(() -> {
            System.out.println("我先执行一次,然后等下一个线程执行完在执行");
            try {
                thread2.join();
                System.out.println("thread2------执行完了,到我执行了");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });

        thread1.start();
        thread2.start();

在当前线程1中加入你要执行的线程2,等线程2执行完了在继续执行线程1,这样达到了按照顺序执行线程的目的,当然,你还可以在线程2中去join线程3,以此类推,其实就是将并行的线程串联起来了。

线程状态

常见的6种线程状态

当我们在new一个线程的时候,但是还没有调用start()方法的时候,该线程处于新建状态

线程对象调用start方法的时候,它会被线程调度器进行执行,这个时候线程就已经交给操作系统来执行了,在操作系统执行的过程中,这整个状态叫做Runnable,Runnable内部又有两个状态,一个是Ready就绪状态,一个是Running运行状态,就绪状态故名思意,我已经准备好了,但是还没运行,这个时候是cpu去处理什么时候去运行的。当真正交给cpu去运行的时候,当前线程就处理Running状态了。(当调用yield方法时,当前线程就会由Running状态,变为Ready状态,当被cpu再次执行的时候又会进入Running状态)

如果线程顺利执行完了就会进入Teminated结束状态,结束以后当前线程的使命就结束了,状态无法回退。

在Runnable状态还存在其他几种状态,TimeWaiting等待,Waiting等待Blocked阻塞
在同步代码块中,没有得到锁就会进入Blocke阻塞状态,当获得锁以后就进入Ready就绪状态。
在线程运行过程中如果调用了wait(),join(),LockSupport.park()就会进入到Waiting状态,调用notifiall(),LockSupport.unpark()就会回到Running状态。
TimeWaiting,看名字就知道是按照时间等待,等时间到了自己会回去,Thread.sleep(time),wait(time),jion(time),LockSupport.parkNanos(),LockSupport.parkUntil()这些都是关于时间等待的方法。

上面的所有状态均是由jvm进行管理的,jvm在使用线程的时候也要经过操作系统,jvm就是一个在操作系统上运行的程序。
线程挂起就是在当前线程执行过程中cpu去执行另外一个线程了,这属于cpu的调度,当在执行另外一个线程的时候,当前线程就是被挂起了。

这里是我画的一个简陋的流程图

技术图片

以上是关于初识多线程的主要内容,如果未能解决你的问题,请参考以下文章

初识多线程之基础知识与常用方法

初识多线程

初识多线程

进程线程区别,和线程初识

多线程 Thread 线程同步 synchronized

Java核心知识---初识线程