线程

线程是程序运行的基本执行单元。当操作系统(不包括单线程的操作系统,如微软早期的DOS)在执行一个程序时,会在系统中建立一个进程,而在这个进程中,必须至少建立一个线程(这个线程被称为主线程)来作为这个程序运行的入口点。因此,在操作系统中运行的任何程序都至少有一个主线程。

进程和线程是现代操作系统中两个必不可少的运行模型。在操作系统中可以有多个进程,这些进程包括系统进程(由操作系统内部建立的进程)和用户进程(由用户程序建立的进程);一个进程中可以有一个或多个线程。进程和进程之间不共享内存,也就是说系统中的进程是在各自独立的内存空间中运行的。而一个进程中的线程可以共享系统分派给这个进程的内存空间。

线程不仅可以共享进程的内存,而且还拥有一个属于自己的内存空间,这段内存空间也叫做线程栈,是在建立线程时由系统分配的,主要用来保存线程内部所使用的数据,如线程执行函数中所定义的变量。

在操作系统将进程分成多个线程后,这些线程可以在操作系统的管理下并发执行,从而大大提高了程序的运行效率。虽然线程的执行从宏观上看是多个线程同时执行,但实际上这只是操作系统的障眼法。由于一块CPU同时只能执行一条指令,因此,在拥有一块CPU(单核)的计算机上不可能同时执行两个任务。而操作系统为了能提高程序的运行效率,在一个线程空闲时会撤下这个线程,并且会让其他的线程来执行,这种方式叫做线程调度。我们之所以从表面上看是多个线程同时执行,是因为不同线程之间切换的时间非常短,而且在一般情况下切换非常频繁。假设我们有线程A和B。在运行时,可能是A执行了1毫秒后,切换到B后,B又执行了1毫秒,然后又切换到了A,A又执行1毫秒。由于1毫秒的时间对于普通人来说是很难感知的,因此,从表面看上去就象A和B同时执行一样,但实际上A和B是交替执行的。

多线程是异步编程的一种方式,但需要注意,在处理异步任务的时候,经常遇到并发与并行的概念,开发者应对这两者有清晰的认识

并发是一种编程属性,即使在单核的机器上也可以执行,而并行是执行硬件的属性(同时执行)

单线程、多线程与Dart异步框架

Java 多线程

线程生命周期

Java从一开始就提供了锁(通过synchronized类和方法)、Runnable以及线程。2004年,Java 5又引入了java.util.concurrent包,它能以更具表现力的方式支持并发,特别是ExecutorService[插图]接口(将“任务提交”与“线程执行”解耦)、Callable以及Future,后两者使用泛型(也是从Java 5首次引入)生成一个高层封装的Runnable或Thread变体,可以返回执行结果。ExecutorService既可以执行Runnable也可以执行Callable。这些新特性促进了多核CPU上并行编程的发展。

之后的版本Java依然持续地改进着对并发的支持,因为程序员们越来越需要更加高效地使用多核CPU的处理能力。Java 7为了使用fork/join实现分而治之算法,新增了java.util.concurrent.RecursiveTask, Java 8则增加了对流和流的并行处理(依赖于新增的Lambda表达式)的支持。通过支持组合式的Future(基于Java 8 CompleteFuture实现的Future), Java进一步丰富了它的并发特性,Java 9提供了对分布式异步编程的显式支持(“发布-订阅”协议,更具体地说,通过java.util.concurrent.Flow接口)。

Java 有三种创建线程的方法:

  1. 通过继承 Thread

    // 1. 继承Thread类
    public class MyThread extends Thread{
    
        // 2. 实现run方法
        @Override
        public void run() {
            for (int i= 0;i< 10;i++){
                System.out.println("我是子线程:"+i);
            }
        }
    }
    

    启动代码

    // 3. 实例化自定义的线程类,并调用start()方法启动线程
    new MyThread().start();
    
  2. 通过实现 Runnable 接口

    // 1. 实现Runnable接口
    public class MyRunnable implements Runnable {
    
        // 2. 实现run方法
        @Override
        public void run() {
            for (int i= 0;i< 10;i++){
                System.out.println("我是子线程:"+i);
            }
        }
    }
    

    启动代码

    // 3. 实例化Thread类时传入自定义Runnable,并调用start()方法启动线程
    new Thread(new MyRunnable()).start();
    
  3. 通过实现 Callable 创建

线程安全

三个原则:

  • 原子性
  • 可见性
  • 有序性

访问共享资源不同步的示例

// 声明共享资源类
public class Res {
    private int count = 1000;

    public void consume(int num){
        try {
            Thread.sleep(150);
            if (count >= num){
                count -= num;
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public int getCount(){
        return count;
    }
}

创建10个线程

Res r = new Res();
for (int j =0;j<10;j++){
   new MyThread(r).start();
}

解决方法,通过synchronized关键字,对资源访问加锁

多线程实现生产-消费者模型

生产者消费者

声明一个资源类

public class Resource {
    //资源初始化个数
    private int initNum;
    //资源最大个数
    private int maxNum;

    public Resource(int init, int Max) {
        this.initNum = init;
        this.maxNum = Max;
    }

    //生产资源
    public synchronized void produce() {
        if (initNum < maxNum) {
            initNum ++;
            System.out.println(Thread.currentThread().getName() + "\t生产... " + "当前总计" + initNum + "个资源");
            // 唤醒全部线程
            notifyAll();
        } else {
            try {
                System.out.println("资源已满," + Thread.currentThread().getName() + "进入休息");
                // 暂停当前线程,并释放对象锁
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    //消耗资源
    public synchronized void consume() {
        if (initNum > 0) {
            initNum --;
            System.out.println(Thread.currentThread().getName() + "\t消费... " + "当前总计" + initNum + "个资源");
            notifyAll();
        } else {
            try {
                System.out.println("资源已消耗完," + Thread.currentThread().getName() + "进入等待");
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

消费者线程

// 消费者
class Consumer extends Thread {
    private Resource resource;

    public Consumer(Resource resource, String name) {
        super(name);
        this.resource = resource;
    }

    @Override
    public void run() {
        while (true) {
            try {
                //模拟耗时
                Thread.sleep(300);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            resource.consume();
        }
    }
}

生产者线程

// 生产者
class Producer extends Thread {
    private Resource resource;

    public Producer(Resource resource, String name) {
        super(name);
        this.resource = resource;
    }

    @Override
    public void run() {
        while (true) {
            try {
                Thread.sleep(300);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            resource.produce();
        }
    }
}

测试代码

// 创建资源
Resource res = new Resource(0,20);
// 创建生产者线程
new Consumer(res,"消费者A").start();
new Consumer(res,"消费者B").start();
new Consumer(res,"消费者C").start();
// 创建消费者线程
new Producer(res,"生产者A").start();
new Producer(res,"生产者B").start();
new Producer(res,"生产者C").start();

公众号“编程之路从0到1”

20190301102949549

Copyright © Arcticfox 2021 all right reserved,powered by Gitbook文档修订于: 2022-05-01 12:20:20

results matching ""

    No results matching ""