欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

线程的基本认识及操作

程序员文章站 2022-03-08 18:58:16
...

线程的基本介绍

什么是线程

线程是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。

进程的执行逻辑

线程的基本认识及操作

为什么会有线程

多线程的本质是:合理的利用多核心CPU资源来实现线程的并行处理,来实现同一个进程内的多个任务的并行执行,同时基于线程本身的异步执行特性,提升任务处理的效率。

  • 在多核CPU中,利用多线程可以实现真正意义上的并行执行
  • 在一个应用进程中,会存在多个同时执行的任务,如果其中一个任务被阻塞,将会引起不依赖该任务的任务也被阻塞。通过对不同任务创建不同的线程去处理,可以提升程序处理的实时性
  • 线程可以认为是轻量级的进程,所以线程的创建、销毁比进程更快

线程的应用场景

为什么要用多线程

  • 异步执行
  • 利用多CPU资源实现真正意义上的并行执行

线程的价值

线程的基本认识及操作

线程的应用场景

  • 使用多线程实现文件下载
  • 后台任务:如定时向大量(100W以上)的用户发送邮件
  • 异步处理:记录日志
  • 多步骤的任务处理,可根据步骤特征选用不同个数和特征的线程来协作处理,多任务的分割,由一个主线程分割给多个线程完成

如何在Java中应用多线程

Java中使用多线程的方式

  • 继承Thread类
  • 实现Runnable接口
  • 实现Callable接口
public class ThreadDemo1  extends  Thread{
    @Override
    public void run() {
        System.out.println("当前线程:"+Thread.currentThread().getName());
    }

    public static void main(String[] args) {
        ThreadDemo1 t = new ThreadDemo1();
        t.start();
    }
}
public class ThreadRunableDemo implements  Runnable {
    @Override
    public void run() {
        System.out.println("当前线程:"+Thread.currentThread().getName());
    }

    public static void main(String[] args) {
        ThreadRunableDemo t = new ThreadRunableDemo();
        Thread t1 = new Thread(t);
        t1.start();
    }
}
public class ThreadCallableDemo implements Callable<String> {
    @Override
    public String call() throws Exception {
        System.out.println("当前线程:"+Thread.currentThread().getName());
        return "线程:"+Thread.currentThread().getName()+"执行成功";
    }

    public static void main(String[] args) {
        try {
            ExecutorService service = Executors.newFixedThreadPool(1);
            Future<String> submit = service.submit(new ThreadCallableDemo());
            System.out.println(submit.get());
        } catch (Exception e){
            e.printStackTrace();
        }finally {
            System.out.println("执行完成");
        }
    }
}

Java线程的生命周期

Java线程从创建到销毁,一共经历6个状态

  • NEW:初始状态,线程被构建,但是还没有调用start方法
  • RUNNABLED:运行状态,JAVA线程把操作系统中的就绪和运行两种状态统一称为“运行中”
  • BLOCKED:阻塞状态,表示线程进入等待状态,也就是线程因为某种原因放弃了CPU使用权,阻塞也分为几种情况
  • WAITING: 等待状态
  • TIME_WAITING:超时等待状态,超时以后自动返回
  • TERMINATED:终止状态,表示当前线程执行完毕

线程的基本认识及操作

Thread.join的使用及原理

Thread.join的作用是保证线程执行结果的可见性

Thread.join的原理

Thread.join的本质其实是wait/notifyall
线程的基本认识及操作
使用示例:

线程的基本认识及操作

线程的基本认识及操作

wait和notify的使用

案例:

ThreadProducer 类:

public class ThreadProducer implements Runnable{
    private Queue<String> bags;
    int size;

    public ThreadProducer(Queue<String> bags, int size) {
        this.bags = bags;
        this.size = size;
    }

    @Override
    public void run() {
        int i = 0;
        while (true) {
            i++;
            synchronized (bags) {
                while (size == bags.size()) {
                    System.out.println("背包满了");
                    try {
                        bags.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }

                }
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                bags.add("bag" + i);
                System.out.println("生产者生产:" + i);
                bags.notifyAll();
            }
        }
    }
}

ThreadConsumer 类:

public class ThreadConsumer implements Runnable{
    private Queue<String> bags;
    int size;

    public ThreadConsumer(Queue<String> bags, int size) {
        this.bags = bags;
        this.size = size;
    }

    @Override
    public void run() {
        while (true) {
            synchronized (bags) {
                while (bags.isEmpty()) {
                    System.out.println("背包为空");
                    try {
                        bags.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }

                }
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("消费者消费:" + bags.remove());
                bags.notifyAll();
            }
        }
    }
}

测试类:

public class TestBags {
    public static void main(String[] args) {
        Queue<String> queue = new LinkedList<>();
        int size =10;

        ThreadProducer producer = new ThreadProducer(queue,size);
        ThreadConsumer consumer = new ThreadConsumer(queue,size);

        Thread t1 = new Thread(producer);
        Thread t2 = new Thread(consumer);

        t1.start();
        t2.start();
    }
}

执行结果:

线程的基本认识及操作

  • 从上面的案例来看,其实wait/notify本质上其实是一种条件的竞争,至少来说,wait和notify方法一定是互斥存在的,既然要实现互斥,那么synchronized就是一个很好的解决方法
  • wait和notify是用于实现多个线程之间的通信,而通信必然会存在一个通信的载体,比如我们小时候玩的游戏,用两个纸杯,中间用一根线连接,然后可以实现比较远距离的对话。而这根线就是载体,那么在这里也是一样,wait/notify是基于synchronized来实现通信的。也就是两者必须要在同一个频道也就是同一个锁的范围内

如何正确终止一个线程

正确的终止一个线程一定是run方法里面的代码执行完成。

Thread.interrupted & Thread.interrupt

当其他线程通过调用当前线程的interrupt方法,表示向当前线程打个招呼,告诉他可以中断线程的执行了,至于什么时候中断,取决于当前线程自己。

hread.interrupted()对设置中断标识的线程复位,并且返回当前的中断状态。

代码示例:

public class InterruptDemo {

    private static int i;

    public static void main(String[] args) throws InterruptedException {
        Thread thread=new Thread(()->{
            while(true){
                //true标识被中断过
                if(Thread.currentThread().isInterrupted()){
                    System.out.println("before:"+Thread.currentThread().isInterrupted());
                    Thread.interrupted(); // 对中断标识复位 false
                    System.out.println("after:"+Thread.currentThread().isInterrupted());
                }
            }
        });
        thread.start();
        TimeUnit.SECONDS.sleep(1);
        thread.interrupt(); //中断
    }
}
输出:
before:true
after:false

这里为什么不用stop是因为stop使用后线程立即停止,并不能保证run方法的完成执行完成。

相关标签: 并发编程