线程
线程是程序运行的基本执行单元。当操作系统(不包括单线程的操作系统,如微软早期的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 有三种创建线程的方法:
通过继承
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();通过实现
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();通过实现
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”