多线程基础

线程和进程区别与联系

进程是操作系统资源分配的最小单位,一个进程里的多个线程共享该进程的所有资源。
线程是CPU调度的最小单位,必须依赖进程而存在。

并行与并发

线程并行指多个线程同时工作,是基于多核CPU实现的。而并发是一段时间内的并发量,并发是依赖时间区间存在的。

为什么要使用多线程技术

  1. 多线程可以使得CPU利用率最大化
  2. 加快系统响应时间,提高用户体验
  3. 使代码模块化、异步化、简单化

多线程带来的问题

  1. 线程安全:由于同一进程内的线程共享系统资源,可能会由于资源竞争导致线程不安全
  2. 死锁:为了解决线程不安全问题,引入锁。但是如果锁使用不当,会导致死锁产生
  3. 资源耗尽:线程消耗系统资源,线程太多,可能导致系统资源消耗殆尽,引起死机

线程启动与中止

线程启动方式
根据jdk官方自述,线程归结为两种启动方式,一是创建Thread子类,第二种是实现Runable接口。

线程中止
不建议使用stop()强行终止线程,容易导致线程执行到一半就被中止执行了,过于霸道。
推荐使用interrupt()来改变中断状态为true,告诉线程可以考虑停止了,我们甚至都不用关心最终这个线程是否会停止。这种方式会更加“柔和”,符合Java给线程的定义:线程是协作式的,而不是抢占式

interrupt()方法仅仅改变线程的中断状态,不实际中断线程的运行。

调用interrupt()后,会将线程的中断标志设置为true,这时候程序可以通过while(t.isInterrupt()){}控制程序的运行。如果程序不对当前线程中断状态做判断,interrupt()并不会中断线程。所以处于死锁中的线程无法被中断

对比下面三段代码,发现捕捉线程中断的异常后,会重置中断状态
这样带来的好处是,可以在catch(InterruptedException e){… …}中释放当前线程占有的其他资源后再次确认中断。

//线程中断后,中断状态为true,并且t线程结束运行
public static void main(String[] args) {
        Thread t = new Thread(new Runnable() {
            @Override
            public void run() {
                while(!Thread.currentThread().isInterrupted()){
                    System.out.println("当前线程的中断状态为:"+ Thread.currentThread().isInterrupted());//false 
                }
            }
        });
        t.start();
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        t.interrupt();
        System.out.println("调用t.isInterrupted()后,当前线程的中断状态为:"+ t.isInterrupted());//true
    }
//线程中断后,中断状态为false,并且t线程不会结束。因为捕捉异常后,重置了线程中断状态,所以总是while(true)
public static void main(String[] args) {
        Thread t = new Thread(new Runnable() {
            @Override
            public void run() {
                while(!Thread.currentThread().isInterrupted()){
                    System.out.println("当前线程的中断状态为:"+ Thread.currentThread().isInterrupted());//false
                    try {
                        Thread.sleep(50);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        });
        t.start();
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        t.interrupt();
        System.out.println("调用t.isInterrupted()后,当前线程的中断状态为:"+ t.isInterrupted());//false
    }
//线程中断后,中断状态为false,并且t线程结束运行。因此在catch()中再次调用了中断线程方法
public static void main(String[] args) {
        Thread t = new Thread(new Runnable() {
            @Override
            public void run() {
                while(!Thread.currentThread().isInterrupted()){
                    System.out.println("当前线程的中断状态为:"+ Thread.currentThread().isInterrupted());
                    try{
                        Thread.currentThread().sleep(50);
                    }catch (InterruptedException e) {
                        e.printStackTrace();
                        Thread.currentThread().interrupt();//如果不在catch中调用interrupt(),线程不会中止,因为抛出中断异常时,程序重置了中断状态
                        System.out.println("当前线程的中断状态为:"+  Thread.currentThread().isInterrupted());//true
                    }
                }
            }
        });
        t.start();
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        t.interrupt();
        System.out.println("调用t.isInterrupted()后,当前线程的中断状态为:"+ t.isInterrupted());//false
    }

interrupted()和isInterrupted()方法异同
同:两个方法都可以获取当前线程的中断状态,返回boolean值
异:interrupted()是一个静态方法,isInterrupted()是一个普通方法。当调用interrupted()后,会重置中断状态为false,而调用isInterrupted()后,中断状态不变。

可以对一个线程对象调用两次start()方法吗?
不行,会抛出线程状态异常。

public static void main(String[] args){
        Thread t = new Thread(new Runnable() {
            @Override
            public void run() {
                while(!Thread.currentThread().isInterrupted()){
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        });
        t.start();
        t.start();//抛出异常IllegalThreadStateException。表示该线程已经启动过了
    }

线程状态转换
可以看到,sleep()中的线程处于阻塞状态,这时候调用interrupt(),线程重新回到就绪状态。这也说明了interrupt()可以打断sleep()中的线程。

思考一下,如果一个线程正在运行状态,调用了interrupt()方法,该线程会处于什么状态?
答案是仍然是运行状态。因为interrupt()并不实际中断线程,而是改变中断状态,剩下的交给程序处理。
多线程基础_第1张图片
其他线程相关方法
yeild():让出当前线程的CPU执行权,但不会释放线程的锁资源。执行yeild()方法的线程可能在让出CPU执行权后又马上重新获得CPU执行权。

join():可以将两个线程有顺序的执行。比如在B线程中调用的A线程的join(),B线程会等A线程执行完后再继续执行。当我们的多线程负责某些模块任务的时候,如果模块之间在某些情况下存在先后顺序,join()非常好用。

守护线程
Daemon(守护)线程是一种支持型线程,当虚拟机的所有非守护线程都中止了,守护线程会自动退出。JVM的垃圾回收线程就是一个守护线程。注意,在守护线程的run()方法内定义的finally不一定会执行。

//最后打印在控制台的语句有可能是"我是守护线程",也可能是"finally"。意味着finally块内容不一定执行
//其余情况下的finally块内容会正常执行
public static void main(String[] args) throws InterruptedException {
        Thread t = new Thread(new Runnable() {
            @Override
            public void run() {
                while(true){
                    try{
                        System.out.println("我是守护线程");
                    }finally {
                        System.out.println("finally");
                    }
                }
            }
        });
        t.setDaemon(true);
        t.start();
        Thread.sleep(100);
    }

你可能感兴趣的:(并发编程,java,多线程)