进程和线程

理解进程和线程

Posted by SkioFox on March 14, 2019

进程

一个程序就是操作系统的一个进程,英文名叫做 Process,一个进程下可以有很多线程。

线程

线程,英文名叫做 Thread,是 Java 程序执行的发动机。就是线程运行着我们的代码

  • 线程其实就是执行一个入口方法,执行完毕就结束了。比如我们之前写的程序,都是使用一个线程执行 main 方法,执行完毕后,线程就结束了
  • 线程在执行方法的时候,每次遇到方法调用,都会给当前的线程栈增加一层。这一层里保存的,就是线程当前的执行状态,比如当前方法的局部变量的值,当前方法执行到哪里了等
  • 所以线程栈里的每一条,都是方法已经开始执行但是还没有结束的方法。没有结束是因为它代码还没执行完,或者是在等待其调用的方法执行完
  • 每次方法的调用都会让线程栈增加,每次方法执行的结束都会让线程栈减少。

  • 创建自己的线程

package com.test.learnthread;

public class CreateThreadAppMain {

    private static final String TEXT = "太阳在这个平静的小村庄缓缓升起,又是开始了平常的一天。我们故事的主人公睡眼惺忪的起来\n" +
        "......";


    public static void main(String[] args) {
        // TODO 代码是被线程执行的,任何代码都可以通过Thread.currentThread()获取执行当前代码的线程
        System.out.println("程序开始,执行的线程名字叫做" + Thread.currentThread().getName());

        // TODO 改成2试试看? 1就时单个线程。改成2会有两个线程,多个线程会同时执行,同时输出内容。线程都有自己的执行栈,不会相互影响
        for (int i = 1; i <= 2; i++) {
            // TODO 学习创建线程的方法
            // TODO Runnable接口里的run是线程执行的方法,执行完毕,线程就结束了
            // TODO 理解代码是在线程里被执行的,同样的代码可以被多个线程执行。
            // TODO 暂停一下 Java ,看看有多少线程,每个线程的名字等信息
            Thread thread = new Thread(new PrintStoryRunnable(TEXT, 200 * i), "我的线程-" + i);
            // TODO 创建好线程之后,如果要启动线程,必须调用start方法,注意不是run方法
            thread.start();
        }

        System.out.println("启动线程结束,名字叫做" + Thread.currentThread().getName());
    }

    static class PrintStoryRunnable implements Runnable {
        private String text;
        private long interval;

        public PrintStoryRunnable(String text, long interval) {
            this.text = text;
            this.interval = interval;
        }

        @Override
        public void run() {
            try {
                double num = Math.random();
                System.out.println("执行这段代码的线程名字叫做" + Thread.currentThread().getName());
                printSlowly(text, interval);
                System.out.println(Thread.currentThread().getName() + "执行结束");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }


    public static void printSlowly(String text, long interval) throws InterruptedException {
        for (char ch : text.toCharArray()) {
            Thread.sleep(interval);
            System.out.print(ch);
        }
        System.out.println();
    }

}
  • java线程的状态

    avatar

  • 守护进程

    • 守护线程:英文名叫做 daemon thread。如果一个进程里没有线程,或者线程都是守护线程,那么进程就结束了。
    • 可以设置线程的优先级,优先级的作用不能保证,这和线程的运行状态以及机器本身的运行状态有关。是不是守护线程都可以设置线程优先级。 ```java package com.test.learnthread;

import java.util.ArrayList; import java.util.List; import java.util.concurrent.TimeUnit;

public class DaemonThreadAppMain {

private static final String TEXT = "太阳在这个平静的小村庄缓缓升起,又是开始了平常的一天。我们故事的主人公睡眼惺忪的起来\n" +
    "......";


public static void main(String[] args) throws InterruptedException {
    System.out.println("程序开始,执行的线程名字叫做" + Thread.currentThread().getName());

    for (int i = 1; i <= 1; i++) {
        Thread thread = new Thread(new PrintStoryRunnable(TEXT, 200 * i), "我的线程-" + i);
        // TODO 可以在start之前设置线程为守护线程
        thread.setDaemon(true);
        // TODO 可以随时改变线程(和是不是守护线程没有关系)的优先级,但是作用不能保证
        thread.setPriority(Thread.MAX_PRIORITY);
        thread.start();
    }
      // 可以输出部分文本,有5s可以打出部分文本
      // TimeUnit.SECONDS.toMillis(5) 5s转为毫秒 //        Thread.sleep(TimeUnit.SECONDS.toMillis(5));

    System.out.println("启动线程结束,名字叫做" + Thread.currentThread().getName());
}

static class PrintStoryRunnable implements Runnable {
    private String text;
    private long interval;

    public PrintStoryRunnable(String text, long interval) {
        this.text = text;
        this.interval = interval;
    }

    @Override
    public void run() {
        try {
            System.out.println("执行这段代码的线程名字叫做" + Thread.currentThread().getName());
            printSlowly(text, interval);
            System.out.println(Thread.currentThread().getName() + "执行结束");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}


public static void printSlowly(String text, long interval) throws InterruptedException {
    for (char ch : text.toCharArray()) {
        Thread.sleep(interval);
        System.out.print(ch);
    }
    System.out.println();
}

}

- 线程的 interrupt 方法
> 线程的 interrupt 无法真的像这个方法的名字那样让线程中断。线程的 interrupt 无法真的像这个方法的名字那样让线程中断。

- 多线程

> 多线程状态下去操作成员变量会导致混乱不能得到一致的结果,两个线程都会去改变其中的变量数据。

> 线程修改数据,人多手杂,一个线程在改,另一个线程也在改。读取当前值,修改为新的值,写入新的值这三个步骤并非是连续执行的,可能有别的线程的代码乱入。而且现代计算机的 CPU 都有缓存,让问题就更不可预测。

```java
// ChangeData.class
package com.test.learnthrread;

public class ChangeData implements Runnable {

    private long delta;

    private long loopCount;

    private DataHolder dataHolder;

    public ChangeData(long delta, long loopCount, DataHolder dataHolder) {
        this.delta = delta;
        this.loopCount = loopCount;
        this.dataHolder = dataHolder;
    }


    @Override
    public void run() {
        for (int i = 0; i < loopCount; i++) {
            dataHolder.change(delta);
        }
        dataHolder.print();
    }
}
// DataHolder.class
package com.test.learnthrread;

public class DataHolder {
    private long number = 0;

    public void change(long delta) {
        number += delta;
    }

    public void print() {
        System.out.println("Number=" + number);
    }

}
// 单线程修改数据
package com.test.learnthrread;

public class SingleThreadSimple {
    public static void main(String[] args) {

        // TODO 对一个数据进行相同次数的加减,而且也没有溢出,最后的结果应该是0

        DataHolder dataHolder = new DataHolder();

        ChangeData increase = new ChangeData(2, Integer.MAX_VALUE, dataHolder);

        increase.run();

        ChangeData decrease = new ChangeData(-2, Integer.MAX_VALUE, dataHolder);

        decrease.run();

    }
}

// 多线程修改数据
package com.test.learnthrread;

public class MultiThreadChaos {
    public static void main(String[] args) {
        // TODO 同样的运算,安排在两个线程里做,结果就不一样了
        DataHolder dataHolder = new DataHolder();
        // ChangeData实现了Runnable接口
        Thread increaseThread = new Thread(new ChangeData(-2, Integer.MAX_VALUE, dataHolder));
        Thread decreaseThread = new Thread(new ChangeData(2, Integer.MAX_VALUE, dataHolder));
        System.out.println("执行开始");
        increaseThread.start();
        decreaseThread.start();
    }
}

那么如何去解决多线程中所产生的这种相互影响的问题呢?使用同步控制去锁住需要控制的方法或者代码块,使每次只有一个线程去执行。

  • 同步控制之synchronized
// ChangeData.class
package com.test.learnthrread;

public class ChangeData implements Runnable {

    private long delta;

    private long loopCount;

    private DataHolder dataHolder;

    public ChangeData(long delta, long loopCount, DataHolder dataHolder) {
        this.delta = delta;
        this.loopCount = loopCount;
        this.dataHolder = dataHolder;
    }


    @Override
    public void run() {
        for (int i = 0; i < loopCount; i++) {
//            dataHolder.change(delta);
            DataHolder.changeStatic(delta);
        }
//        dataHolder.print();
        DataHolder.printStatic();
    }
}
// DataHolder.class
package com.test.learnthrread;

public class DataHolder {

    private Object lockObj = new Object();

    private long number = 0;
    private static long numberStatic = 0;

    // TODO 一个synchronized解决问题,使用synchronized修饰change方法,保证每次只有一个线程调用方法,其中只有该线程能修改涉及的成员变量。会让其他的线程停住。因此会很慢。
    public synchronized void change(long delta) {
        number += delta;
    }
    // TODO 一个synchronized解决问题,synchronized修饰静态方法
    public synchronized static void changeStatic(long delta) {
        numberStatic += delta;
    }

    public void print() {
        System.out.println("Number=" + number);
    }

    public static void printStatic() {
        System.out.println("static Number=" + numberStatic);
    }

}

package com.test.learnthrread;

public class MultiThreadChaos {
    public static void main(String[] args) { 
        // 使用两个dataHolder去在两个线程执行,两个线程会互相不影响,各自使用自己的dataHolder对象,会很快。
        // 只使用于对象级别的方法,不适用于静态方法,因为静态方法是以类进行调用,每次只能有一个线程进入。
//        DataHolder dataHolder = new DataHolder();
//        DataHolder dataHolder2 = new DataHolder();
        // Thread increaseThread = new Thread(new ChangeData(-2, Integer.MAX_VALUE/50, dataHolder));
        // Thread decreaseThread = new Thread(new ChangeData(2, Integer.MAX_VALUE/50, dataHolder2));
        Thread increaseThread = new Thread(new ChangeData(-2, Integer.MAX_VALUE/50, null));
        Thread decreaseThread = new Thread(new ChangeData(2, Integer.MAX_VALUE/50, null));
        System.out.println("执行开始");
        increaseThread.start();
        decreaseThread.start();
    }
}
  • 同步控制之wait notify

    当多个线程的互动,需要等待和和被唤醒的时候,就可以考虑使用这个语法。 ```java package com.test.waitnotify;

import java.util.concurrent.TimeUnit;

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

    Object locker = new Object();

    int workingSec = 2;
    int threadCount = 5;
    for (int i = 0; i < threadCount; i++) {
        new Thread(() -> {
            // 2. 每个线程开始工作
            System.out.println(getName() + ":线程开始工作……");
            try {
                // 3. 某个线程线程进入locker开始等待
                synchronized (locker) {
                    // 5. 某个线程沉睡2S,依次每个线程沉睡2s
                    sleepSec(workingSec);
                    System.out.println(getName() + ":进入等待");
                    // >> TODO wait 方法必须在进入相应对象的synchronized块中才能调用
                    // >> TODO 执行 wait 方法之后,自动失去对象的 monitor,也就是说别的线程可以进入这个对象的synchronized代码块了
                    locker.wait();
                    // >> TODO 被唤醒的线程,就相当于执行过了wait方法,开始向下执行。
                    // >> TODO 如果wait不是synchronized块中的最后一行,那么第一件事就是"排队"获取之前失去的monitor
                    // >> TODO 排队加引号是因为synchronized是非公平的,也就是说,不是谁先等待谁就能先获得
                    // 9.每个被唤醒的线程执行下面的代码
                    System.out.println(getName() + ":线程继续……");
                    sleepSec(2);
                    System.out.println(getName() + ":结束");
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        }, "工作线程" + i).start();
    }
    // 1. 创建5个线程时,主线程会继续执行,因此会从这里开始
    System.out.println("------------- 唤醒线程开始sleep -------------");
    // TODO 如果执行notify的时候,线程还没有进入wait状态,那么notify是没有效果的
    // TODO 先notify,后进入wait,就是所谓的 lost notification问题,可能造成线程无法进行
    // TODO 如果让唤醒的线程 sleep 的比worker短(sleep 时间 +1变-1,或者干脆不sleep),也就是先进行notify,那么就可能会造成这个问题
    // TODO 为什么说可能呢?因为synchronized还是阻碍了notify的执行,但是notify有机会在wait前执行了
    // 4. 主线程沉睡3s,因此会在第一个进入等待和第二个进入等待的线程的中间执行
    sleepSec(workingSec + 1);
    // 6. 主线程继续执行
    System.out.println("------------- 唤醒线程sleep结束 -------------");
    // 7. 进入synchronized代码块,逐个唤醒线程
    synchronized (locker) {
        // >> TODO notify/notifyAll 方法必须在进入相应对象的synchronized块中才能调用 //            System.out.println("------------- 开始唤醒所有 -------------"); //            locker.notifyAll();

        for (int i = 0; i < threadCount; i++) {
            System.out.println("------------- 开始逐个唤醒 -------------");
            locker.notify();
        }
    }

}

private static void sleepSec(int sec) {
    try {
        Thread.sleep(TimeUnit.SECONDS.toMillis(sec));
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}

private static String getName() {
    return Thread.currentThread().getName();
} }
- 多线程经典模型(生产者和消费者)
> 生产的任务必需被消费且只能消费一次,使用List和wait notify实现生产者消费者
```java
// Consumer.java
package com.test.waitnotify.producerconsumer;

import java.util.Queue;

public class Consumer<T> {
    private Queue<T> tasks;

    public Consumer(Queue<T> tasks) {
        this.tasks = tasks;
    }

    public T consume() throws InterruptedException {
        // 锁住
        synchronized (tasks) {
            // TODO 如果不用while,用if,会怎么样? task不能为空
            while (tasks.size() == 0) {
                System.out.println("消费者线程进入等待:" + Thread.currentThread().getName());
                // >> TODO wait方法会释放monitor
                tasks.wait();
            }
            // 拿出任务
            T ret = tasks.poll();
            // >> TODO 调用notify或者notifyAll的时候,必须已经获得对象的monitor,也就是在对象的synchronized块中
            // 等待唤醒
            tasks.notifyAll();
            return ret;
        }
    }
}
// Producer.class
package com.test.waitnotify.producerconsumer;

import java.util.Queue;

public class Producer<T> {

    private Queue<T> tasks;

    private int maxTaskCount = 0;

    public Producer(Queue<T> tasks, int maxTaskCount) {
        this.tasks = tasks;
        this.maxTaskCount = maxTaskCount;
    }

    public void produce(T task) throws InterruptedException {
        // 锁住 为了一个个线程进行
        synchronized (tasks) {
            // TODO 如果这个检查不在synchronized块里会怎么样呢?
            // TODO 如果如果不用while会怎么样呢? tasks.size() >= maxTaskCount 确保不会超过最大任务数
            while (tasks.size() >= maxTaskCount) {
                System.out.println("生产者线程进入等待:" + Thread.currentThread().getName());
                // >> TODO wait方法会释放monitor
                tasks.wait();
            }
            // 任务放入队列
            tasks.add(task);
            // >> TODO 调用notify或者notifyAll的时候,必须已经获得对象的monitor,也就是在对象的synchronized块中
            // Cpmsumer 空的情况
            tasks.notifyAll();
        }
    }
}
// ProducerConsumerAppMain.class
package com.test.waitnotify.producerconsumer;

import java.util.LinkedList;
import java.util.Queue;
import java.util.concurrent.TimeUnit;

public class ProducerConsumerAppMain {

    public static final Object LOCKER = new Object();


    public static void main(String[] args) {
        Queue<String> urls = new LinkedList<>();

        Consumer<String> consumer = new Consumer<>(urls);
        Producer<String> producer = new Producer<>(urls, 1024);
        // 创建100个工作线程
        for (int i = 0; i < 100; i++) {
            Thread consumerThread = new Thread(() -> {
                while (true) {
                    try {
                        // 拿到任务
                        String url = consumer.consume();
                        // 处理任务
                        processURL(url);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }, "消费者-" + i);
            // 开始
            consumerThread.start();
        }
        // 创建3个生产线程
        for (int i = 0; i < 3; i++) {
            Thread producerThread = new Thread(() -> {
                while (true) {
                    try {
                        // 生产url
                        String url = produceURL();
                        // 放入队列
                        producer.produce(url);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }, "生产者-" + i);
            producerThread.start();
        }

        // TODO 如果想给生产者消费者做一个统计,统计每个生产者消费者所生产/消费的task的数量,应该
        // TODO 1)使用哪种数据结构?
        // TODO 2)如何保证线程安全?
        // TODO 3)怎么将统计结果输出到控制台?

    }
    // 生产url
    private static String produceURL() {
        StringBuilder ret = new StringBuilder();
        ret.append("www.");
        for (int i = 0; i < 6; i++) {
            int rand = ((int) (Math.random() * 1000)) % 26;
            char ch = (char) (rand + 'a');
            ret.append(ch);
        }
        ret.append(".com");
        return ret.toString();
    }
    // 处理url
    private static void processURL(String url) throws InterruptedException {
        System.out.println("开始处理:" + url);
        Thread.sleep(TimeUnit.SECONDS.toMillis(1));
        System.out.println("处理完成:" + url);
    }

}
  • 线程同步之join ```java package com.test.join;

import java.util.ArrayList; import java.util.List;

public class ThreadJoinAppMain {

private static final List<String> CONTENTS = new ArrayList<>();

private static long WORKING_DURATION = 0;

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

    long mainStart = System.currentTimeMillis();

    List<Thread> threads = new ArrayList<>();
    // 1.创建模拟10个线程抓取网页
    for (int i = 0; i < 10; i++) {
        Thread thread = new Thread(() -> {
            // 2. 线程开始工作
            System.out.println(Thread.currentThread().getName() + ":开始抓取网页内容");
            long start = System.currentTimeMillis();
            // 模拟抓取网页
            String content = getContentFromWeb();
            long threadWorkingDuration = System.currentTimeMillis() - start;
            System.out.println(Thread.currentThread().getName() + ":抓取网页内容结束");
            synchronized (CONTENTS) {
                CONTENTS.add(content);
                WORKING_DURATION += threadWorkingDuration;
            }
        }, "线程" + i);
        // 假如这里只是添加而不启动线程去调用join方法,会导致该线程立即返回。因此必需确保线程已经起来isAlive检查
        thread.start();
        threads.add(thread);
    }

    // TODO 2. 主线程sleep一下,为了让线程都起来
    Thread.sleep(1);
    // 3. 主线程开始join
    System.out.println(" ------------ 主线程开始 join  ------------ ");
    // 4. 循环调用线程的join,join执行完,说明线程全部结束
    for (Thread thread : threads) {
        try {
            String name = thread.getName();
            System.out.println(" ------------ 主线程开始join " + name + " ------------ ");
            thread.join();
            System.out.println(" ------------ 主线程join " + name + " 结束 ------------ ");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    // 5. 10个线程结束
    System.out.println(" ------------ 主线程join结束,获取的内容为: ------------ ");

    CONTENTS.forEach(s -> {
        System.out.print(s.length() + ":");
        System.out.println(s);
    });

    long mainWorkDuration = System.currentTimeMillis() - mainStart;

    System.out.println("工作线程累计工作时间:" + WORKING_DURATION);
    System.out.println("主线程工作时间:" + mainWorkDuration);
}
private static String getContentFromWeb() {
    StringBuilder ret = new StringBuilder();
    int len = ((int) (Math.random() * 1000000)) % 4096 + 1024;
    for (int i = 0; i < len; i++) {
        int rand = ((int) (Math.random() * 1000)) % 26;
        char ch = (char) (rand + 'a');
        ret.append(ch);
        try {
            Thread.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    return ret.toString();
}

}

> 多线程的意义就是让工作并发的处理,使用更多的资源(CPU,磁盘,网络等),以便让工作更快的完成。

- 死锁
> 形成死锁的条件:在获取新的资源之前,没有释放之前获取的资源。简单来说程序永远不能获取到资源

```java
// 查看线程状态 jps jstack -l 进程号    
// DeadLockAppMain.class
package com.test.deadlock;

public class DeadLockAppMain {

    public static void main(String[] args) throws InterruptedException {
        System.out.println("程序开始");

        AppResources appResources = new AppResources();
        Thread t1 = new Thread(new Task1(appResources), "Thread-For-Task1");
        t1.start();
        Thread t2 = new Thread(new Task2(appResources), "Thread-For-Task2");
        t2.start();

        t1.join();
        t2.join();
        System.out.println("程序结束");
    }
}
// AppResources.class
package com.test.deadlock;

public class AppResources {

    // TODO 系统中有两个或者多个资源,如果不按照顺序申请,而且申请到一个后,再申请另一个的时候不释放原来的资源锁,就可能会死锁
    private Object resourcePrinter = new Object();

    private Object resourceInput = new Object();

    public Object getResourcePrinter() {
        return resourcePrinter;
    }

    public void setResourcePrinter(Object resourcePrinter) {
        this.resourcePrinter = resourcePrinter;
    }

    public Object getResourceInput() {
        return resourceInput;
    }

    public void setResourceInput(Object resourceInput) {
        this.resourceInput = resourceInput;
    }
}

// Task1.class
package com.test.deadlock;

import java.util.concurrent.TimeUnit;

public class Task1 implements Runnable {

    private AppResources appResources;

    public Task1(AppResources appResources) {
        this.appResources = appResources;
    }

    @Override
    public void run() {
        // 必需锁住
        synchronized (appResources.getResourceInput()) {
            System.out.println("Task1得到了Input资源");
            System.out.println("Task1开始工作……");
            // TODO 申请到input资源后,模拟工作2秒
            try {
                Thread.sleep(TimeUnit.SECONDS.toMillis(2));
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            // TODO 然后在不释放input锁的情况下,继续申请printer资源
            System.out.println("Task1尝试去获取Printer资源");
            // 必需锁住
            synchronized (appResources.getResourcePrinter()) {
                System.out.println("Task1得到了Printer资源");
                System.out.println("Task1继续工作……");
                try {
                    Thread.sleep(TimeUnit.SECONDS.toMillis(3));
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}
// Task2.class
package com.geekbang.deadlock;

import java.util.concurrent.TimeUnit;

public class Task2 implements Runnable {

    private AppResources appResources;

    public Task2(AppResources appResources) {
        this.appResources = appResources;
    }

    @Override
    public void run() {
        // TODO 申请资源顺序不同,可能会造成死锁
//        differentSeq();

        // TODO 申请资源顺序相同,可以避免死锁,但是会降低资源的使用效率
        sameSeq();
    }

    private void differentSeq(){
        synchronized (appResources.getResourcePrinter()) {
            // TODO 先申请printer资源
            System.out.println("Task2得到了Printer资源");
            System.out.println("Task2开始工作……");
            try {
                Thread.sleep(TimeUnit.SECONDS.toMillis(3));
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            // TODO 工作3秒中,在不释放printer资源的情况下,继续申请input资源
            System.out.println("Task2尝试去获取Input资源");
            synchronized (appResources.getResourceInput()) {
                System.out.println("Task2得到了Input资源");
                System.out.println("Task2继续工作……");
                try {
                    Thread.sleep(TimeUnit.SECONDS.toMillis(2));
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    private void sameSeq(){
        synchronized (appResources.getResourceInput()) {
            System.out.println("Task2得到了Input资源");
            System.out.println("Task2开始工作……");
            try {
                Thread.sleep(TimeUnit.SECONDS.toMillis(3));
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("Task2尝试去获取Printer资源");
            synchronized (appResources.getResourcePrinter()) {
                System.out.println("Task2得到了Printer资源");
                System.out.println("Task2继续工作……");
                try {
                    Thread.sleep(TimeUnit.SECONDS.toMillis(2));
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }

}

如何避免死锁:按顺序获取系统的资源, 避免资源未释放的同时再互相获取资源造成死锁。

  • 线程的那些资源共享,那些资源不共享

共享的资源:

a. 堆 由于堆是在进程空间中开辟出来的,所以它是理所当然地被共享的;因此new出来的都是共享的(16位平台上分全局堆和局部堆,局部堆是独享的)

b. 全局变量 它是与具体某一函数无关的,所以也与特定线程无关;因此也是共享的

c. 静态变量虽然对于局部变量来说,它在代码中是“放”在某一函数中的,但是其存放位置和全局变量一样,存于堆中开辟的.bss和.data段,是共享的

d. 文件等公用资源 这个是共享的,使用这些公共资源的线程必须同步。Win32 提供了几种同步资源的方式,包括信号、临界区、事件和互斥体。

独享的资源:

a. 栈 栈是独享的

b. 寄存器 这个可能会误解,因为电脑的寄存器是物理的,每个线程去取值难道不一样吗?其实线程里存放的是副本,包括程序计数器PC

  • 进程间的通信方式有哪些

总共有八种:

1、无名管道( pipe ):半双工的通信方式,数据只能单向流动且只能在具有亲缘关系的进程间使用

2、高级管道:将另一个程序当作一个新的进程在当前程序进程中启动,则这个进程算是当前程序的子进程,

3、有名管道,:也是半双工的通信方式,但是允许没有亲缘进程之间的通信

4、消息队列(message queue):消息队列是有消息的链表,存放在内核中,并由消息队列标识符标识,消息队列克服了信号传递信息少,管道只能承载无格式字节流以及缓冲区大小受限的缺点

5、信号量(semophore):信号量是一个计数器,可以用来控制多个进程对共享资源的访问,它常作为一种锁机制,防止某进程正在访问共享资源时,其他进程也访问该资源,

6、信号(sinal):用于通知接受进程某个事件已经发生

7、共享内存(shared memory):共享内存就是映射一段能被其他进程所访问的内存。这段共享内存由一个进程创建,但是多个进程可以访问,共享内存是最快的IPC 方式,往往与其他通信机制配合使用

8、套接字(socket):可用于不同机器之间的进程通信

a