Android小知识-Java多线程的基础知识了解下

本平台的文章更新会有延迟,你们能够关注微信公众号-顾林海,包括年末前会更新kotlin由浅入深系列教程,目前计划在微信公众号进行首发,若是你们想获取最新教程,请关注微信公众号,谢谢!java

十月份离职,在家修养一个多月,这一个多月作了不少事,本身的微信公众号开通了,博客也换了一种风格,在简书和掘金分享一些Android方面的小知识,这一个多月看了些书,有技术相关的,也有非技术相关的,忽然间以为的这种生活也挺不错的,这五年买了不少书,加起来最起码有四五箱的书,之前上班忙,只有晚上回来看个一两个小时,如今闲了,想全天看就全天看,读书是一生的事,喜欢读书,这样不管在何时都会有本身的思考和见地,不会一味地迎合或沉沦,从而失去立场,失去本身。固然在进行自我提高的同时也在看看有没有一些工做机会,目前刚回到上海,但愿本身能找到一家与之奋斗的公司,一块儿成长下去。web

闲话就扯到这里,下面进入正文。编程


在讲到多线程有必要了解下什么是进程,在百度百科上是这么定义进程的:进程是操做系统结构的基础;是一次程序的执行;是一个程序及其数据在处理上顺序执行时所发生的活动;是程序在一个数据集合上运行的过程,它是系统进行资源分配和调度的一个独立单位。安全

百度百科对进程的定义比较抽象,举个例子,咱们在电脑打开一个程序exe,那这个exe就能够理解成一个进程,进程是受操做系统管理的基本运行单元。那线程又是什么,线程是在进程中独立运行的子任务,好比打开腾讯视频(进程),你一边在看视频,一边在下载视频,同时在看视频时数据的传输等等,这些同时执行的任务都是线程,利用多线程能够同一时间内运行更多不一样种类的任务。微信

public class Client {

    public static void main(String[] args){
        //输出main
        System.out.println(Thread.currentThread().getName());
    }

}
复制代码

经过currentThread方法获取当前的线程名,上面这个程序在main入口函数中打印当前线程的名称,发现默认就有一个叫作main线程在执行main()方法中的代码。多线程

在Java中实现多线程编程的方式有两种,一种是继承Thread类,另外一种是实现Runnable接口,下面这个程序就使用第一种方式继承Thread类:ide

public class Task extends Thread {

    @Override
    public void run() {
        super.run();
        System.out.println("执行相关任务");
    }
}
复制代码

Task类继承Thread,run方法中打印一句“执行相关任务”。函数

public class Client {

    public static void main(String[] args){
       Thread thread=new Task();
       thread.start();
       System.out.println("任务执行完毕!");
    }

}
复制代码

在main函数中先建立Task实例并执行Task线程,接着打印“任务执行完毕!”,运行下看看什么结果。测试

任务执行完毕!
执行相关任务
复制代码

发现先打印“任务执行完毕!”,后打印“执行相关任务”,也就是在使用多线程时,代码的运行结果与代码执行顺序或调用顺序是无关的。线程是一个子任务,CPU以不肯定的方式,或者说是以随机的时间来调用线程中的run方法。spa

若是咱们继承了Thread类,就不能继承其它类了,Java不支持多继承,那怎么办呢?幸亏Java提供了Runnable接口,接下来看第二种方式实现Runnable接口来建立线程。

public class Task implements Runnable {
    @Override
    public void run() {
        System.out.println("执行相关任务");
    }
}
复制代码

很简单,Task类实现了Runnable接口并实现run方法,怎么使用这个Task,和上面的Client同样,代码以下:

public class Client {

    public static void main(String[] args){
       Runnable runnable=new Task();
       Thread thread=new Thread(runnable);
       thread.start();
       System.out.println("任务执行完毕!");
    }

}
复制代码

在编写多线程时容易遇到数据共享问题,多个线程能够访问一个变量,看下面程序:

public class Task implements Runnable {

    private int mTaskCount=0;

    @Override
    public void run() {
        mTaskCount++;
        System.out.println("执行第"+mTaskCount+"任务");
    }
}
复制代码

在Task线程中对mTaskCount进行递增,下面是Client代码:

public class Client {

    public static void main(String[] args) {
        Runnable runnable = new Task();
        Thread thread_1 = new Thread(runnable);
        Thread thread_2 = new Thread(runnable);
        Thread thread_3 = new Thread(runnable);
        Thread thread_4 = new Thread(runnable);
        thread_1.start();
        thread_2.start();
        thread_3.start();
        thread_4.start();
        System.out.println("任务执行完毕!");
    }

}
复制代码

输出以下:

任务执行完毕!
执行第2任务
执行第2任务
执行第3任务
执行第4任务
复制代码

发现两个线程都打印了mTaskCount为2,产生了“非线程安全”问题,非线程安全主要是指多个线程对同一个对象中的同一个实例变量进行操做时会出现值被更改、值不一样步的状况,影响程序的执行流程。在某些JVM中,mTaskCount的操做分红3个步骤,第一取得原有mTaskCount值,第二计算mTaskCount+1,第三对mTaskCount进行赋值;在这3个步骤中,若是遇到多个线程同时访问,会出现指令重排序的问题,也就是非线程安全问题。那怎么解决呢?能够在run方法前加上synchronized关键字:

public class Task implements Runnable {

    private int mTaskCount=0;

    @Override
   synchronized public void run() {
        mTaskCount++;
        System.out.println("执行第"+mTaskCount+"任务");
    }
}
复制代码

这样输出时mTaskCount是依次递增的,在run方法前加上synchronized关键字,使多个线程在执行run方法时,以排队的形式进行处理。当一个线程试图调用run方法前,先判断run方法有没有上锁,若是上锁了,说明有其余线程在执行run方法,必须等其余线程执行完run方法,加锁的这段代码称为“互斥区”或“临界区”。一个线程想要执行同步方法里的代码时,须要先获取锁,若是获取不到锁,须要不断的尝试拿这把锁,直到可以拿到为止。

接着了解下Thread经常使用的几种方法:

  • isAlive()方法用于判断当前的线程是否处于活动状态,活动状态就是线程已经启动且还没有终止,线程处于正在运行或准备开始运行的状态,就认为线程是“存活”的。

  • sleep()方法的做用是在指定的毫秒数内让当前“正在执行的线程”休 眠(暂停执行)。

  • getId()方法的做用是获取线程的惟一标识。

线程的开启是如此的简单,但咱们有时须要在知足必定条件后关闭线程,这时如何去作呢?

能够经过interrupt()方法来中止线程,但interrupt()方法仅仅是在当前线程中打了一个中止的标记,并非真的中止线程。在Java的SDK中,Thread提供了两种方法用于判断线程的状态是否是中止,分别是interrupted()方法,用于测试当前线程是否已经中断,还有一个就是isInterrupted()方法,用于测试线程是否已经中断。

先看Thread.interrupted()方法的使用:

public class Task implements Runnable {

    @Override
   synchronized public void run() {
        for (int i=0;i<1000;i++){
            System.out.println("i="+i);
        }
    }
}
复制代码

在Task线程中经过for循环打印0到999。

public class Client {

    public static void main(String[] args) {
        Runnable runnable = new Task();
        Thread thread = new Thread(runnable);
        thread.start();
        try {
            Thread.sleep(10);
            thread.interrupt();
            System.out.println(Thread.interrupted());
            System.out.println(Thread.interrupted());
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

}
复制代码

启动Task线程后,执行暂停10毫秒后调用interrupt()方法,最后打印出Thread.interrupted()方法两次,咱们看打印结果:

i=0
i=1
i=2
...
i=248
false
false
i=249
...
i=998
i=999
复制代码

经过interrupt()方法并不能中止Task线程,而执行Thread.interrupted()方法,输出两次都为false,也就是说Thread.interrupted()方法是用于测试当前线程是否已经中断,这个当前线程指的是main线程,它从未中断过,因此打印的结果是两个false,这里先看如何使main线程产生中断效果,看下面代码:

public class Client {

    public static void main(String[] args) {
        Thread.currentThread().interrupt();
        System.out.println(Thread.interrupted());
        System.out.println(Thread.interrupted());
    }

}
复制代码

打印:

true
false
复制代码

经过Thread.currentThread().interrupt()给当前main线程打上中止的标志,那为何第二次输出Thread.interrupted()方法时是false呢?官方文档对interrupted()方法的解释以下:测试当前线程是否已经中断。线程的中断状态由该方法清除。换句话说,若是连续两次调用该方法,则第二次调用将返回false。也就说interrupted()方法具备清除状态的功能。

isInterrupted()方法与interrupted()方法相比,isInterrupted()方法并不具备清除状态,也就是咱们给Task线程执行interrupt()方法后,Task线程就被打上了中断状态,无论执行多少次isInterrupted()方法都会返回true。

既然知道了interrupt()的做用,若是先执行task线程的interrupt()方法,这时Task线程被打上中断状态,而后再在Task的run方法中经过判断Thread.interrupted()是否为true,若是为true就退出循环,代码以下:

public class Task implements Runnable {

    @Override
   synchronized public void run() {

        for (int i=0;i<1000;i++){
            if(Thread.interrupted()){
                break;
            }
            System.out.println("i="+i);
        }
    }
}
复制代码

Client代码以下:

public class Client {

    public static void main(String[] args) {
        Runnable runnable = new Task();
        Thread thread = new Thread(runnable);
        thread.start();
        try {
            Thread.sleep(10);
            thread.interrupt();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

}
复制代码

打印:

i=0
i=1
i=2
...
i=284
i=285
i=286
i=287
i=288
复制代码

这样不就能够在外部中断了Task线程,这种方式虽然能够中止了Task线程,但若是在for语句下打印一句话,代码:

public class Task implements Runnable {

    @Override
   synchronized public void run() {

        for (int i=0;i<1000;i++){
            if(Thread.interrupted()){
                break;
            }
            System.out.println("i="+i);
        }
        System.out.println("不该该打印");
    }
}
复制代码

打印:

i=0
i=1
i=2
...
i=223
i=224
i=225
不该该打印
复制代码

发现for循环语句下面的的println仍是打印出来了,这时能够在判断Thread.interrutped()语句中经过抛出异常来退出,代码以下:

public class Task implements Runnable {

    @Override
    synchronized public void run() {
        try {
            for (int i = 0; i < 1000; i++) {
                if (Thread.interrupted()) {
                    throw new InterruptedException();
                }
                System.out.println("i=" + i);
            }
            System.out.println("不该该打印");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }
}
复制代码

打印:

i=0
i=1
i=2
...
i=106
i=107
i=108
java.lang.InterruptedException
    at com.book.demo.demo01.Task.run(Task.java:10)
    at java.lang.Thread.run(Thread.java:745)
复制代码

这种方式叫作异常法退出。

固然也能够经过return来退出线程:

public class Task implements Runnable {

    @Override
    synchronized public void run() {
        for (int i = 0; i < 1000; i++) {
            if (Thread.interrupted()) {
                return;
            }
            System.out.println("i=" + i);
        }
        System.out.println("不该该打印");

    }
}
复制代码

关于多线程的相关知识后面还有不少相关文章,等不及的能够在微信公众号上查看,谢谢!


838794-506ddad529df4cd4.webp.jpg
相关文章
相关标签/搜索