美文网首页
java线程和kotlin协程对比

java线程和kotlin协程对比

作者: 蓝Renly | 来源:发表于2018-08-17 17:40 被阅读0次

1.线程创建的2种方式及其区别

第一种方式:实现Runnable接口
public class MyRunnable implements Runnable {
    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            System.out.println("执行的第"+(i+1)+'次');
            try {
                Thread.sleep(1000L);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
public class Main {
    public static void main(String[] args) {
        System.out.println("主线程开始执行了");
        MyRunnable mr = new MyRunnable();
        Thread thread = new Thread(mr);
        thread.start();
        System.out.println("主线程结束执行了");
    }
}
第二种方式:继承Thread类
public class MyThread extends Thread {
    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            System.out.println("打印第"+i+"次");
            try {
                Thread.sleep(100L);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
public class Main {
    public static void main(String[] args) {
        System.out.println("主线程开始执行了");
        MyThread mt = new MyThread();
        mt.start();
        System.out.println("主线程结束执行了");
    }
}

区别:多个线程需要共享同一数据时候需要使用Runnable接口形式;比如,多个窗口同时卖电影票案例(涉及线程安全,需要同步).

2.线程中断(线程中断,设置标记)

1.线程中断:使用interrupt方法中断线程:
    注意事项:1)如果判断的线程代码里面有sleep(),那么中断就会失效,因为sleep里面有InterruptedException;
/*--------------- 使用interrupt方法中断线程 -------------------------*/
public class Main {
    public static void main(String[] args) {
        ThreadInterrupt ti = new ThreadInterrupt();
        ti.start();
/*--------------- 需求:主线程睡2000ms后结束ti线程 -----------------------*/
        try {
            Thread.sleep(30L);
            ti.interrupt();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
public class ThreadInterrupt extends Thread {
  private Long a = 0L;
    @Override
    public void run() {
        for (int i = 0; i < 6000; i++) {
            if (isInterrupted())return;
            System.out.println("打印第"+(i+1)+"次");
//假如下面的sleep没有被注释,那么上面的if (isInterrupted())return;就不会起作用
           /* try {
                Thread.sleep(500L);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }*/
        }
       /* while (true){
            if (isInterrupted())return;
            System.out.println("哒哒哒"+a);
            a++;
        }*/
    }
}
2.线程中断:自定义标记中断线程:
    注意事项:1)如果线程里面有sleep()方法,那么就自定义标记来中断线程
    步骤:1.在继承类或者实现类中定义一个旗帜变量(Boolean类型);
        2.增加该变量的set方法;
        3.在main方法中,调用该变量set方法,置为true或者false,再在子类的线程中进行判断然后return;
/*------------------- 自定义标记中断线程 --------------------------------*/
public class Main {
    public static void main(String[] args) {
        ThreadInterrupt ti = new ThreadInterrupt();
        ti.start();
        /*------------------需求:主线程睡2000ms后结束ti线程------------------------*/
        try {
            Thread.sleep(3000L);
            ti.setNeedInterrupt(true);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
public class ThreadInterrupt extends Thread {
    private Boolean needInterrupt = false;
    public void setNeedInterrupt(Boolean needInterrupt) {
        this.needInterrupt = needInterrupt;
    }
    @Override
    public void run() {
        for (int i = 0; i < 6000; i++) {
            if (needInterrupt) return;
            System.out.println("打印第" + (i + 1) + "次");
            try {
                Thread.sleep(100L);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

3.线程join

join:就是将并行线程合并为串行线程.
public class ThreadJoin {
    public static void main(String[] args) {
        System.out.println("主线程开始");
        MyThread mt = new MyThread();
        mt.start();
        try {
            mt.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("主线程结束");
    }
}
//注意观察程序运行结果

4.守护线程

setDaemon():随着主线程的死亡而死亡(不会立即死亡,具体死亡时间不可控)
注意:因为守护线程会跟着主线程的死亡而死亡.如果不想守护线程死亡太早,可以让主线程睡眠,或者将守护线程串行到主线程!
public class main {
    public static void main(String[] args) {
        System.out.println("主线程开始执行");
        MyThread mt = new MyThread();
        mt.setDaemon(true);
        mt.start();
        /*try {
            mt.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }*/
        /*try {
            Thread.sleep(2000L);//主线程睡
        } catch (InterruptedException e) {
            e.printStackTrace();
        }*/
        System.out.println("主线程结束执行");
    }
}

5.线程池

程序启动一个线程的成本比较高,因为涉及到要与操作系统进行交互,而使用线程池可以很好的提高性能,尤其是当程序中要创建大量生命周期很短的线程时,更应该考虑线程池,线程池每个线程结束后,并不会死,而是到线程池中进入等待状态;
根据核数定义线程池中线程数量:Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors() + 1)
public class MainApp {
    public static void main(String[] args) {
        MyRunnable mr = new MyRunnable();
        ExecutorService service = Executors.newFixedThreadPool(5);
        //定义线程池数量的一种方式:使用核数加减1;
        Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors() + 1);
        service.execute(mr);//无返回值
        service.submit(mr);//有返回值
        Future<?> submit = service.submit(mr);
        System.out.println(submit);//java.util.concurrent.FutureTask@7ea987ac
    }
}

6.线程创建方式3:CallAble方式

public class Main {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        MyCallAble mca = new MyCallAble();
        ExecutorService service = Executors.newFixedThreadPool(3);
        service.execute(callAble);
        //错误,execute()方法不能执行实现CallAble的类对象
        Future<String> submitmca = service.submit(mca);
        String strmca = submitmca.get();//返回CallAble接口中call()方法内的返回值
        System.out.println(strmca);
        //注意:通过实现CallAble的线程对象,只能放进线程池中执行!
        //Thread th = new Thread(mca);CallAble
    }
}
public class MyCallAble implements Callable<String> {
    @Override
    public String call() throws Exception {
        for (int i = 0; i < 16; i++) {
            System.out.println("执行的第"+i+"次");
            Thread.sleep(100L);
        }
        return "执行了MyCallAble接口方法";
    }
}

7.创建第一个协程

1.配置gradle环境]
group = "pers.lansir"
version = "1.0-SNAPSHOT"
plugins{
    kotlin("jvm")
}
repositories{
    mavenCentral()
    jcenter()
}
dependencies{
    compile(kotlin("stdlib"))
    compile("org.jetbrains.kotlinx:kotlinx-coroutines-core:0.22.5")
}
2.协程创建:launch{}
import kotlinx.coroutines.experimental.launch
fun main(args: Array<String>) {
    println("主线程开始执行")
    launch {
        (1..10).forEach {
            println("打印第"+it+"次")
        }
    }
    println("主线程结束执行")
    Thread.sleep(300L)
}

8.launch函数分析

1.查看如下源代码,可知launch是一个函数,有四个参数,前三个是默认参数,第4个参数是函数类型;并且返回值类型是Job类型.
2.查看源码,可知CoroutineContext = DefaultDispatcher,也就是等价于=>CommonPool
3.查看commonPool发现,这其实就是一个线程池,并且是一个单例,继承CoroutineDispatcher(抽象类),ForkJoinPool又是CoroutineDispatcher的子类,因此,commonPool也就是ForkJoinPool线程池.

//1.lanuch函数查看
public expect fun launch(
    context: CoroutineContext = DefaultDispatcher,
    start: CoroutineStart = CoroutineStart.DEFAULT,
    parent: Job? = null,
    block: suspend CoroutineScope.() -> Unit
): Job
//2.lanuch函数的第一个参数
public actual val DefaultDispatcher:CoroutineDispatcher = CommonPool

9.ForkJoinPool和协程的正确启动方式

1.ForkJoinPool的Description :   ForkJoinPool里面的线程时守护线程,跟着主线程的死亡而死亡
2.协程启动2种方式,1种join()串行,1种主线程sleep()
//1.ForkJoinPool举例说明
fun main(args: Array<String>) {
    /*------------------------普通线程方式实现-------------------------*/
    println("主线程开始执行")
    val mr = MyRunnable()
    /*val  thread = Thread(mr)
    thread.start()*/
    //println("主线程结束执行")
    /*------------------------ForkJoinPool--------------------------*/
    val pool = ForkJoinPool(5)
    pool.execute(mr)
    println("主线程结束执行")
}
class MyRunnable:Runnable{
    override fun run() {
        (1..10).forEach {
            println("打印"+it)
        }
    }
}
//2.正确启动协程的方法
fun main(args: Array<String>) = runBlocking{
    //ForkJoinPool  每一个线程都是守护线程
    val job = launch {
//        println("hello")
        (1..10).forEach {
            println("打印"+it)
            Thread.sleep(1000L)
        }
    }
    /*-----------第一种方法: 让主线程睡眠一段时间--------------*/
//    Thread.sleep(1000L)
    /*------------------ 第二种方法:线程join()--------*/
    job.join()
}

10.协程原理

1.遇到耗时任务,线程直接回到线程池中,如果有其它任务就执行其他任务;
2.耗时任务结束之后,从线程池中空闲的线程恢复执行(那个线程时处于空闲状态的,就那个线程去执行耗时后的任务)
3.耗时任务必须是挂起函数(比如delay()),如果是Thread的阻塞式任务,线程也不会回到线程池中执行其它任务(比如遇到sleep())
fun main(args: Array<String>) = runBlocking{
    //是不是同一个线程池?是
    val job = launch {
        println("协程执行前线程:${Thread.currentThread().name}")
        //耗时任务
//        delay(2000)
        Thread.sleep(2000L)

        println("协程执行后线程:${Thread.currentThread().name}")
    }
    launch {
        //耗时任务
//        delay(2000)
        Thread.sleep(2000L)
    }

    launch {
        //耗时任务
//        delay(2000)
        Thread.sleep(2000L)
    }
    launch {
        //耗时任务
//        delay(2000)
        Thread.sleep(2000L)
    }
    launch {
        //耗时任务
//        delay(2000)
        Thread.sleep(2000L)
    }
    launch {
        //耗时任务
//        delay(2000)
        Thread.sleep(2000L)
    }
    launch {
        //耗时任务
//        delay(2000)
        Thread.sleep(2000L)
    }
    launch {
        //耗时任务
//        delay(2000)
        Thread.sleep(2000L)
    }

    Thread.sleep(3000L)
}

11.挂起函数

1.suspend修饰的函数就是挂起函数,标记这个函数可以被挂起执行;
2.2.挂起函数只能在协程或者挂起函数里面执行;
3.标记成挂起函数后,这个函数可以被挂起执行,但是如果函数里面结果可以直接返回,自动决定不用再挂起;
fun main(args: Array<String>) = runBlocking{//主协程
    val job = launch {
        println("挂起前逻辑")
        delay(1000)
        println("挂起后逻辑")
    }

    job.join()
}

/**
 * 挂起函数
 */
suspend fun myDelay(){
    println("执行")
    Thread.sleep(2000L)//不能挂起执行的
}

12.协程和线程效率对比

协程效率比线程高很多(因为协程有非阻塞模式,也就是遇到了耗时任务,可以不用一直等待,去执行其他任务)
fun main(args: Array<String>) = runBlocking{
    val startTime = System.currentTimeMillis()
    /*---------------------------- 线程 ----------------------------*/
    //10万条线程  打印.
//    val threadList = List<Thread>(100000){
//        MyThread()
//    }
//    threadList.forEach { it.start() }
//    threadList.forEach { it.join() }
//    val endTime = System.currentTimeMillis()
//    println("线程耗时:${endTime-startTime}")//9442
    /*---------------------------- 协程 ----------------------------*/
    //10万条协程  打印.
    val coroutineList = List(100000){
        launch {
            println(".")
        }
    }
    coroutineList.forEach { it.join() }
    val endTime = System.currentTimeMillis()
    println("线程耗时:${endTime-startTime}")//934
    //后台开发
}
class MyThread:Thread(){
    override fun run() {
        println(".")
    }
}

13.协程取消

fun main(args: Array<String>) = runBlocking {
    //2000之后协程停下来
    val job = launch {
        (1..10).forEach {
            println("打印:"+it)
            delay(500L)
//            Thread.sleep(500L)//cancle就失效了
        }
    }
    Thread.sleep(2000L)
    //协程取消
    job.cancel()
    job.join()
}

14.线程取消实例

class Main {
    public static void main(String[] args) {
        /*-------------- 使用interrupt方法中断线程 ------------------*/
        /**
         * 使用interrupt中断线程  需要在线程执行代码中通过isInterrupted进行线程状态的判断
         * 线程里面有sleep  中断实效了  因为sleep会抛出InterruptedException
         */
        MyThread thread = new MyThread();
        thread.setName("线程1");
        thread.start();
        try {
            Thread.sleep(3000L);
            //停止子线程
//            thread.stop();//太暴力
//            thread.interrupt();
            /*----------------- 自定义标记中断线程 -------------------*/
            thread.setNeedInterrupt(true);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
class MyThread extends Thread {
    private int a = 0;
    private boolean needInterrupt = false;

    public void setNeedInterrupt(boolean needInterrupt) {
        this.needInterrupt = needInterrupt;
    }
    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            if (needInterrupt) return;
            System.out.println("打印" + i);
            try {
                sleep(1000L);
            } catch (InterruptedException e) {
                System.out.println("出现异常" + e.getMessage());
                e.printStackTrace();
            }
        }
//        while (true){
//            //如果线程被中断就不用再执行了
//            if(isInterrupted())return;
//            System.out.println("打印数据了"+a);
//            a++;
//        }
    }
}

15.线程取消失效的处理

通过isActive来判断
fun main(args: Array<String>) = runBlocking {
    //2000之后协程停下来
    val job = launch {
        (1..10).forEach {
            if(!isActive)return@launch
            println("打印:"+it)
            Thread.sleep(500L)
        }
    }
    Thread.sleep(2000L)
    println("协程取消之前的状态:${job.isActive}")
    //协程取消
    job.cancel()//setInterrupt
    println("协程取消之后的状态:${job.isActive}")
    job.join()
}

16.async启动协程

launch启动协程,不能获取协程执行的结果
async可以获取协程中执行的结果

//1
fun main(args: Array<String>)= runBlocking {
    println("主线程开始执行")
    var async = async {
        println("async执行开始")
        delay(200L)
        println("async执行结束")
    }
    async.await()//相当于把线程串到主线程上了
    println("主线程结束执行")
}
//2
fun main(args: Array<String>) = runBlocking {
    //启动协程
    val defer1 = async { job1() }
    val defer2 = async { job2() }
    val result1 = defer1.await()
    val result2 = defer2.await()
    println(result1)
    println(result2)
}
//挂起函数定义任务
suspend fun job1():String{
    println("job1开始执行了")
    delay(2000L)
    println("job1执行结束了")
    return "job1的返回值"
}
suspend fun job2():String{
    println("job2开始执行了")
    delay(2000L)
    println("job2执行结束了")
    return "job2的返回值"
}

17..协程的上下文

fun main(args: Array<String>)= runBlocking {
    println("主线程开始执行")
    launch {
        (1..10).forEach {
            println("默认:执行的第$it 次,${Thread.currentThread()}")
            //Thread[ForkJoinPool.commonPool-worker-1,5,main]
        }
    }.join()
    launch(CommonPool) {
        (1..10).forEach {
            println("CommonPool:执行的第$it 次,${Thread.currentThread()}")
            //Thread[ForkJoinPool.commonPool-worker-1,5,main]
        }
    }.join()
    //运行在主线程里面
    launch(Unconfined) {
        (1..10).forEach {
            println("Unconfined:执行的第$it 次,${Thread.currentThread()}")
            //Thread[main,5,main]
        }
    }.join()
    //运行在父协程的上下文
    launch(coroutineContext) {
        (1..10).forEach {
            println("coroutineContext:执行的第$it 次,${Thread.currentThread()}")
            //Thread[main,5,main]
        }
    }.join()
    //运行在父协程的上下文
    launch(newFixedThreadPoolContext(3,"LanSir")) {
        (1..10).forEach {
            println("3,LanSir:执行的第$it 次,${Thread.currentThread()}")
            //Thread[LanSir-1,5,main]
        }
    }.join()
    println("主线程结束执行")
}

相关文章

网友评论

      本文标题:java线程和kotlin协程对比

      本文链接:https://www.haomeiwen.com/subject/jxutiftx.html