01-Java多线程、多线程的创建与停止(入门)

一.并发和并行

  • 并发是单位时间内可以处理的事件数目
  • 并行是同一时刻,可以同时运行的线程数量
  • 如果有10个cpu核心,同时可以运行10个线程,那么并行度就是10,每一个任务处理是5ms,那么每秒并发就是10*200=2000

二.查看已有线程

2.1 代码

public static void main(String[] args) {
        ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean();
        ThreadInfo[] threadInfos = threadMXBean.dumpAllThreads(false, false);
        for (ThreadInfo ti : threadInfos) {
            System.out.println("[" + ti.getThreadId() + "]" + ti.getThreadName());
        }

    }

2.2 输出:

[5]Attach Listener   -- 获取程序属性
[4]Signal Dispatcher   -- 分发信号
[3]Finalizer  --- 调用对象的finalize()方法的线程
[2]Reference Handler  -- 清除引用
[1]main   --主线程

三.启动一个线程

  • 创建线程的三种方法

3.1 继承Thread类

public class ThreadImpl extends Thread {

    @Override
    public void run() {
        System.out.println("继承Thread类实现线程...");
    }
}

3.2 实现Runnable接口

public class RunnableImpl implements Runnable {
    @Override
    public void run() {
        System.out.println("实现Runnable接口实现多线程...");
    }
}

3.3 实现Callable接口

public class CallableImpl implements Callable<String> {
    @Override
    public String call() throws Exception {
        Thread.sleep(50);
        System.out.println("实现Callable接口实现多线程...");
        return "callable implement";
    }
}

3.4 测试:

public class Test {

    public static void main(String[] args) throws ExecutionException, InterruptedException, TimeoutException {

        Thread t1 = new ThreadImpl();
        t1.start();
        t1.interrupt();//中断线程,将标志位置为true

        Runnable r1 = new RunnableImpl();
        Thread t2 = new Thread(r1);
        t2.start();

        Callable c1 = new CallableImpl();
        FutureTask<String> futureTask = new FutureTask<>(c1);
        Thread t3 = new Thread(futureTask);
        t3.start();
        //get方法是阻塞的,可以添加阻塞的超时时间
        System.out.println("获取futureTask的返回值:"+futureTask.get(51, TimeUnit.MILLISECONDS));
    }
}

  • 比较:
  • 继承Thread类: 没有返回值(单继承局限性)
  • 实现Runnable: 没有返回值(多接口实现,灵活)
  • 实现Callable: 可以返回结果

四.停止一个线程

4.1 线程停止

  • 线程执行完毕自然停止
  • 抛出异常,线程内部未处理异常,抛出异常的话线程就退出了
  • stop方法(不会释放资源)
  • interrupt:将线程的中断标志位值true,该方法并不会立即停止该线程,类似于告知该线程,尽快停止,至于线程何时停止,则由线程本身决定(线程本身有足够的灵活度来进行停止线程前的处理),在java中线程之间是协作式的,非抢占式的。

4.2 辨析 t1.interrupt(), t1.isInterrupted(), Thread.interrupted();

interrupt();
  • 中断线程,将标志位置为true
isInterrupted();
  • 判断当前线程是否处于中断状态,调用的是
    private native boolean isInterrupted(false);不会修改线程标志位
Thread.interrupted();
  • 判断当前线程是否处于中断状态,修改标志位为false,底层调用的是:
    private native boolean isInterrupted(true);

4.3 静态方法Thread#isInterrupted()

/**
     * Tests if some Thread has been interrupted.  The interrupted state
     * is reset or not based on the value of ClearInterrupted that is
     * passed.
     * 由传入的boolean值决定是否会修改字段标志位,isInterrupted不会修改,
     * 静态interupted方法会修改
     */
    private native boolean isInterrupted(boolean ClearInterrupted);
    
    方法1是Thread类提供的方法,通常是通过线程对象去中断某个线程。线程对象都是Thread类对象可以调用该方法,只有在Runnable的实现类的内部无法直接调用。

    方法2是Thread类提供的方法,通常是用于判定一个线程是否处于中断状态。如果线程是Thread子类,可以直接调用,如果是Runnable实现类,那么该类的内部只能Thread.currentThread().isInterrupted(),先获取当前线程对象,再调用方法,
    
    方法3通常是用于判定一个线程是否处于中断状态。因为是静态方法,方法内部就是通过Thread.currentThread()先获取的当前线程对象,在任何地方都可以调用。
    
    

4.4 方法对比

方法名称作用备注
interrupt()通过线程对象去中断某个线程Thread类的方法,通常是线程外部通过线程对象去中断对应的线程,本质是将线程中断标志位置为true
isInterrupted()通常是用于判定一个线程是否处于中断状态Thread类的方法,在Runnable实现类内部只能采用这样的方式调用Thread.currentThread().isInterrupted(),不会修改标志位,通常用来判断一个线程是否处于中断状态,本质是判断中段标志是否为true
Thread.interrupted()判定一个线程是否处于中断状态静态方法,内部通过Thread.currentThread()先获取的当前线程对象,判断标志位是否为true,若为true,还会修改标志位为false,比如在AQS的parkAndCheckInterrupt方法中会用该方法判断线程park结束之后,是否被中断了,并且会清除标志位让线程继续执行(其目的是判断线程park结束是被中断还是park被唤醒,但是又要让线程继续执行,因此要清除标志位)

五.InterruptedException异常

  • 一个进程在捕获到中断信号后(比如外部调用了这个线程的interrupt方法),进程会抛出该异常,并且会将中断标志位置为false,如此一来,外部调用的中断信号不会生效,该线程并不会停止。对应的处理方法是,线程内部捕获到该异常后,在catch到异常后主动调用interupt方法,将自身中断标志位置为true,当然这给线程留下来足够的线程中断所要做的清理工作。
  • 常用的阻塞方法都会抛出该异常,如sleep,join,此时可以感知到外部中断该线程(外部调用该线程的interrupt方法,线程内部会抛出异常,并将标志位置为false),如果采用自定义标志作为线程循环处理的标记,那么仅仅判断标记是不够的,因为循环内部可能阻塞,阻塞处就无法及时感知到外部对标志的修改,这里的判断条件还需要加上该线程的标志位,这样只要外部发出了中断该线程的信号,线程内部阻塞处就会抛出中断异常,循环条件就可以感知到内部的阻塞方法会抛出InterruptedException异常,

5.1 通过标志位中断线程

while(flag || !isInterrupted()){
    try{
        take();
        //一个阻塞方法,如果外部的flag改变了,因为阻塞在这里是无法及时响应
        //的。但是阻塞方法通常都会在线程被中断后抛出InterruptedException,需要在
        //捕获异常之后自身再次中断线程,这样就会再次回到while这里判断条件不成立,
        //继而退出逻辑,这样才能比较好的响应外部的中断信号
    }catch(InterruptedException e){
        Thread.currentThread().interrupt();
        //自己中断自己,将标志位置为false,并可以做必要的事情
    }
}

5.2.代码示例

public class EndThreadInterruptException {

    public static void main(String[] args) throws InterruptedException {

        //线程1抛出InterruptedException异常之后,将中断标志位置为false,所以线程1会继续执行
        Thread t1 = new Thread(new MyRunnableInterruptedException(),"Thread-1");
        t1.start();

        //线程2抛出InterruptedException异常之后,捕获异常并且显式调用自身的interupt方法,
        //所以线程2会中断
        Thread t2 = new Thread(new MyRunnableInterrupteSelf(),"Thread-2");
        t2.start();

        //线程3抛出InterruptedException异常之后,将中断标志位置为false,所以线程3会继续执行
        //线程3和线程1是一样的,只是是继承了Thread类,因此在判断自身的中断状态时可以直接调用//isInterrupted方法
        Thread t3 = new Thread(new MyThreadInterruptedException(),"Thread-3");
        t3.start();


        Thread.sleep(4000);
        t1.interrupt();
        t2.interrupt();
        t3.interrupt();



    }


    private static class MyRunnableInterruptedException implements Runnable {
        @Override
        public void run() {
            while (!Thread.currentThread().isInterrupted()) {
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    //sleep抛出InterruptedException后不主动调用interupt方法,线程并不会停止,因为抛出该异常后,线程的中断标志位会置为false
                    e.printStackTrace();
                    System.out.println(Thread.currentThread().getName() +"sleep抛出InterruptedException异常后,中断标志位:" + Thread.currentThread().isInterrupted());
                }
                System.out.println("线程" + Thread.currentThread().getName() + "执行...");
            }
        }
    }

    private static class MyRunnableInterrupteSelf implements Runnable {
        @Override
        public void run() {
            while (!Thread.currentThread().isInterrupted()) {
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    //sleep抛出InterruptedException后不主动调用interupt方法,线程并不会停止,因为抛出该异常后,线程的中断标志位会置为false
                    e.printStackTrace();
                    System.out.println(Thread.currentThread().getName() +"sleep抛出InterruptedException异常后,中断标志位:" + Thread.currentThread().isInterrupted());
                    //自己再次中断自己
                    Thread.currentThread().interrupt();
                    System.out.println(Thread.currentThread().getName() +"调用interrupt后,中断标志位:" + Thread.currentThread().isInterrupted());
                }
                System.out.println("线程" + Thread.currentThread().getName() + "执行...");
            }
        }
    }

    private static class MyThreadInterruptedException extends Thread {
        @Override
        public void run() {
            while (!this.isInterrupted()) {
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    //sleep抛出InterruptedException后不主动调用interupt方法,线程并不会停止,因为抛出该异常后,线程的中断标志位会置为false
                    e.printStackTrace();
                    System.out.println(Thread.currentThread().getName() +"sleep抛出InterruptedException异常后,中断标志位:" + Thread.currentThread().isInterrupted());
                }
                System.out.println("线程" + Thread.currentThread().getName() + "执行...");
            }
        }
    }


}

  • 输出
线程Thread-1执行...
线程Thread-2执行...
线程Thread-3执行...
java.lang.InterruptedException: sleep interrupted
	at java.lang.Thread.sleep(Native Method)
	at com.intellif.mozping.createthread.EndThreadInterruptException$MyThreadInterruptedException.run(EndThreadInterruptException.java:77)
	at java.lang.Thread.run(Thread.java:748)
java.lang.InterruptedException: sleep interrupted
	at java.lang.Thread.sleep(Native Method)
	at com.intellif.mozping.createthread.EndThreadInterruptException$MyRunnableInterruptedException.run(EndThreadInterruptException.java:42)
	at java.lang.Thread.run(Thread.java:748)
java.lang.InterruptedException: sleep interrupted
	at java.lang.Thread.sleep(Native Method)
	at com.intellif.mozping.createthread.EndThreadInterruptException$MyRunnableInterrupteSelf.run(EndThreadInterruptException.java:58)
	at java.lang.Thread.run(Thread.java:748)
线程Thread-2执行...
线程Thread-1执行...
线程Thread-3执行...
Thread-3 sleep抛出InterruptedException异常后,中断标志位:false
线程Thread-3执行...
Thread-1 sleep抛出InterruptedException异常后,中断标志位:false
线程Thread-1执行...
Thread-2 sleep抛出InterruptedException异常后,中断标志位:false
Thread-2调用interrupt后,中断标志位:true
线程Thread-2执行...
线程Thread-3执行...
线程Thread-1执行...
线程Thread-3执行...
线程Thread-1执行...
线程Thread-3执行...
  • 从输出看到,线程1和3还会继续运行,因为中断标志位抛出异常后置为false,线程并不会停止。而线程2则在捕获异常后再次中断自身后停止了运行。

六.并发编程要点

6.1 好处

  • 充分利用cpu的资源、加快用户响应的时间,程序模块化,异步化

6.2 风险和注意实现

  • 线程共享资源,存在冲突;
  • 容易导致死锁;
  • 启用太多的线程,就有搞垮机器的可能
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值