并发编程笔记01:线程的多种创建方式

235 阅读4分钟

一、线程创建的4种方式

1.1 继承Thread类的方式

public class MyThread01 extends Thread{

    @Override
    public void run() {
        System.err.println("=== 子线程执行…… ===");
    }

    public static void main(String[] args) throws InterruptedException {
        MyThread01 thread01 = new MyThread01();
        thread01.start();
    }
}

1.2 实现Runnable接口的方式

public class MyRunnableThread implements Runnable {
    public void run() {
            System.err.println("=== 子线程执行 ===");
    }

    public static void main(String[] args) {
        Thread thread = new Thread(new MyRunnableThread());
        thread.start();
    }
}

1.3 匿名内部类的方式

public class AnonymousThread {

    public static void main(String[] args) {
        new Thread(new Runnable() {
            public void run() {
                System.err.println("=== sub thread executed... ===");
            }
        }).start();
    }
}

1.4 基于线程池的方式

public class ExecutorServiceThread {

   private static ExecutorService threadPool = Executors.newFixedThreadPool(3);

    public static void main(String[] args) {
        for (int i = 0; i < 10; i++) {
            threadPool.submit(new Runnable() {
                public void run() {
                    System.err.println("=== "+Thread.currentThread().getName()+" ===");
                }
            });
        }
    }
}

执行效果:

=== pool-1-thread-1 ===
=== pool-1-thread-2 ===
=== pool-1-thread-3 ===
=== pool-1-thread-3 ===
=== pool-1-thread-3 ===
=== pool-1-thread-3 ===
=== pool-1-thread-3 ===
=== pool-1-thread-3 ===
=== pool-1-thread-3 ===
=== pool-1-thread-2 ===

1.5 有返回值的线程创建

public class MyFutureTask implements Callable<String> {

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        long l = System.currentTimeMillis();
        MyFutureTask myFutureTask = new MyFutureTask();
        FutureTask<String> task = new FutureTask<String>(myFutureTask);
        Thread thread = new Thread(task);
        thread.start();
        System.err.println("主线程继续执行其他业务");
        Thread.sleep(900);
        System.err.println("主线程获取子线程执行结果:"+task.get()+",耗时:"+(System.currentTimeMillis() - l)+"ms");
    }

    @Override
    public String call() throws Exception {
        System.err.println("模拟耗时业务……");
        Thread.sleep(1000);
        return "succ";
    }
}

执行效果;

主线程继续执行其他业务
模拟耗时业务……
主线程获取子线程执行结果:succ,耗时:1001ms

1.6 基于集合的流式编程的方式

通过Collection接口的parallelStream流来实现

public class ParallelStreamDemo {
    public static void main(String[] args) {
        List<Integer> list = Arrays.asList(1, 2, 3, 4);
        // 并行打印
        list.parallelStream().forEach(System.out::println);
        // 并行转换并求和
        int sum = list.parallelStream().mapToInt(value -> value).sum();
        System.err.println("结果:"+sum);
    }
}

执行效果:

2
1
3
4
结果:10

二、线程的中断:

通过查看Thread类下的方法可以看到,有一个被废弃的方法:stop(); 那么,为什么stop()方法会被废弃呢,由于执行stop()方法并不释放当前线程所持有的锁等资源,这样的结果显然不是我们想要的,取而代之,jdk推出了线程中断(interrupt)的概念;

2.1 线程的中断:

目前在jdk中关停一个线程的逻辑是这样的:调用interrupt()只是将中断标志位置位,我们需要自行根据中断标志位来执行结束该线程、释放锁资源等操作;
Thread类下关于interrupt的方法有如下三个:

2.1.1 public void interrupt()方法

只是给当前线程标记一下中断标志位,实际上线程还是会继续执行;

2.1.1 public static boolean interrupted()方法

检查当前线程的中断标志位,返回boolean值(已中断true/未中断false),此方法会 清除 中断标志位,即:在仅被中断一次的情况下,连续调用2次interrupted()方法时,第一次返回true,第二次将会返回false;

2.1.1 public boolean isInterrupted()方法

检查当前线程的中断标志位,返回boolean值(已中断true/未中断false),此方法 不会清除 中断标志位。
类方法interrupted()与对象方法isInterrupted()底层都是调用了私有的isInterrupted(boolean ClearInterrupted)方法,只是前者在调用时传入true,后者传入false:

public static boolean interrupted() {
    return currentThread().isInterrupted(true);
}
public boolean isInterrupted() {
    return isInterrupted(false);
}

2.2 show me the code:

2.2.1 结束线程:

public class MyThread01 extends Thread{

    @Override
    public void run() {
        while(!isInterrupted()){
            System.err.println("=== 子线程执行中…… ===");
        }
        System.err.println("===线程中断,模拟释放锁资源,模拟结束进程===");
    }

    public static void main(String[] args) throws InterruptedException {
        MyThread01 thread01 = new MyThread01();
        thread01.start();
        Thread.sleep(10);
        thread01.interrupt();
    }
}

执行效果:

=== 子线程执行中…… ===
=== 子线程执行中…… ===
=== 子线程执行中…… ===
=== 子线程执行中…… ===
=== 子线程执行中…… ===
===线程中断,模拟释放锁资源,模拟结束进程===

2.2.2 类方法interrupted() VS 对象方法isInterrupted()

public class InterruptCompare {
    public static void main(String[] args) {
        MyThread myThread = new MyThread();
        myThread.start();
        myThread.interrupt();
    }
}
class MyThread extends Thread{
    @Override
    public void run() {
        while(!isInterrupted()){
            // do something
        }
        // exit
        System.err.println("第一次调用isInterrupted():"+isInterrupted());
        System.err.println("第二次调用isInterrupted():"+isInterrupted());
        System.err.println("第一次调用interrupted():"+Thread.interrupted());
        System.err.println("第二次调用interrupted():"+Thread.interrupted());
    }
}

执行结果:

第一次调用isInterrupted():true
第二次调用isInterrupted():true
第一次调用interrupted():true
第二次调用interrupted():false

三、补充:创建线程时,如果继承Thread类的同时,实现了Runnable接口,将会怎样呢?

3.1 show me the code

public class MultiThreadDemo {
    public static void main(String[] args) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.err.println("=== this is from Runnable ===");
            }
        }){
            @Override
            public void run() {
                System.err.println("=== this is from Thread ===");
            }
        }.start();
    }
}

3.2 分析

要搞清楚这个问题,首先要梳理一下我们在new Thread(new Runnable(){...})的时候,到底发生了什么:

由此可见,Runnable部分的实现将被忽略

3.3 结果验证

=== this is from thread ===