java并发编程之美-阅读记录1

1.1什么是线程?

  在理解线程之前先要明白什么是进程,因为线程是进程中的一个实体。(线程是不会独立存在的)

  进程:是代码在数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,线程则是进程中的一个执行路径,一个进程中至少会有一个线程,进程中的多个线程共享进程的资源。

  线程:是cpu分配的基本单位。

  

  由上图可看出,一个进程中会有多个线程,多个线程共享堆和方法区,但是每一个线程都会有自己的栈和程序计数器。

为什么要将栈和程序计数器设置为线程私有的呢?
    前边说线程是cpu执行的基本单位,而cpu一般是使用时间片轮转方式轮询占用的,所以当当前线程cpu时间片使用完毕后,要让出cpu,等待下一次轮到自己的时候在调用。
那问题就来了,线程是如何知道之前执行到哪了?
    程序计数器就是为了记录之前让出cpu时执行到的地址,等下次再次执行时就可以从程序计数器中获取之前执行到的位置,继续向下执行。(程序计数器是不会记录native方法执行的地址的,它只记录java代码执行的地址),而栈则是存储现场的局部变量,一遍之后再次使用

1.2线程的创建和运行

  三种方式:继承Thread、实现Runnable接口、使用FuthreTask方式(实现Callable接口)。

package com.nxz.blog.otherTest;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

public class TestThread {

    static class MyThread1 extends Thread {
        @Override
        public void run() {
            System.out.println("extend-Thread:"+this.getName());
        }
    }

    static class MyThread2 implements Runnable {

        @Override
        public void run() {
            System.out.println("implements-Runnable:"+Thread.currentThread());
        }
    }

    static class MyThread3 implements Callable<String> {

        @Override
        public String call() throws Exception {

            System.out.println("implements-Callable:"+Thread.currentThread());
            return "Callable接口";
        }
    }

    public static void main(String[] args) {
        MyThread1 t1 = new MyThread1();
        t1.start();

        Thread t2 = new Thread(new MyThread2());
        t2.start();

        FutureTask<String> futureTask = new FutureTask<>(new MyThread3());
        new Thread(futureTask).start();
        try {
            // 阻塞,等待执行完毕,并返回结果
            String res = futureTask.get();
            System.out.println(res);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }

        System.out.println("main-Thread");
    }
}

  上述代码执行结果:

extend-Thread:Thread-0
implements-Runnable:Thread[Thread-1,5,main]
implements-Callable:Thread[Thread-2,5,main]
Callable接口
main-Thread

  创建线程后会调用start方法,代表当前线程已经处于就绪状态(也就是说该线程已经获取了除cpu资源外的其他资源,等待cpu执行时间片),当获取了cpu资源后,该线程才处于运行状态,一旦run方法(对于extent Thread来说)执行完毕,该线程就处于终止状态。

  使用继承的好处:在run方法内可以直接使用this来获取当前线程(而另两个的this则没有getName等一些方法),而不需要Thread.currentThread()。

1.3线程通知和等待

  以下三个方法都是Object类的方法,为所有对象公用

  ①wait()

    当一个线程调用wait()方法后,该线程就会被阻塞挂起,知道被notify/notifyAll调用或者其他线程调用了intercept方法中断了该线程

  ②wait(long timeout)

    表上边一个多了一个超时时间,在等待期间如果没有其他线程唤醒(notify/notifyAll)该线程,该线程仍然会在超过timeout时间后自动返回

  ③wait(long timeout, int nanos)  :两个参数,毫秒timeout,纳秒nanos,其内部还是调用的wait(long timeout)方法,只不多对于第二个参数纳秒,如果其范围是大于0,小于一百万的话(1毫秒= 100 0000纳秒),则将timeout加一秒

    public final void wait(long timeout, int nanos) throws InterruptedException {
        if (timeout < 0) {
            throw new IllegalArgumentException("timeout value is negative");
        }
        if (nanos < 0 || nanos > 999999) {
            throw new IllegalArgumentException(
                                "nanosecond timeout value out of range");
        }
        if (nanos > 0) {
            timeout++;
        }
        wait(timeout);
    }

  ④notify()/notifyAll()

    唤醒在同一个共享变量上等待的线程,区别:一个唤醒一个,一个唤醒所有

  需要注意的:

  *虚假唤醒:

    一个线程可以从挂起状态变为可以运行状态(也就是被唤醒),即使该线程没有被其他线程调用notify()、notifyAll()方法进行通知,或者被中断,或者等待超时,这就是虚假唤醒

  (在java并发编程之美中这样描述的,感觉不太清楚,找了其他资料有的是这样描述的:在多核处理器下,其他线程的信号可能会唤醒多个线程(阻塞在同一个条件变量上的线程),结果就是,多个被唤醒的线程,前一个执行代码逻辑后,另外的线程再次执行同样的代码后会出现问题)

    例子:在下边代码中分别为生产者和消费者,假如在一个生产者和一个消费者的情况下(也就是单线程),是没有问题的,不存在虚假唤醒,假如在多线程环境下,当一个生产者完成之后,调用notifyAll方法后,会唤醒其他生产者和所有的消费者,当第一个消费者消费完成之后,在有消费者消费,此时就会出现问题(也就是出现了虚假唤醒),解决该问题的方法,就是将消费者和生产者中的条件判断if(queue.size() == 1) 和if(queue.isEmpty()) 改为   while( queue.size() == 1) 和while( queue.isEmpty() ) ,即改为循环判断,当被唤醒是,每一个线程都重新判断是否符合条件

    static Queue queue;

    static class ProductThread extends Thread {
        @Override
        public void run() {
            synchronized (queue) {
                if (queue.size() == 1) {// 假设队列最多存一个对象
                    try {
                        queue.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }

                queue.add(new Object());
                queue.notifyAll();
            }
        }
    }

    static class ConsumerThread extends Thread {
        @Override
        public void run() {

            synchronized (queue) {
                if (queue.isEmpty()) {
                    try {
                        queue.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }

                queue.poll();
                queue.notifyAll();
            }
        }
    }

1.4等待线程执行完毕   的join方法

  当调用join方法(该方法属于Thread类)时,就会等待线程执行完毕后,在继续向下运行

  例子:

package com.nxz.blog.otherTest;

public class TestThread002 {

    static class MyThread extends Thread{
        @Override
        public void run() {
            System.out.println("myThread");
        }
    }

    public static void main(String[] args) throws InterruptedException {
        MyThread t1 = new MyThread();
        t1.setName("myThread");
        t1.start();

        // 如果注释掉 下边代码的话,main方法的输出应该会是  先main-thread 然后再试mythread,
        // 放开的话 main主线程就会等待t1线程执行完毕后,在向下运行
        t1.join();

        System.out.println("main-thread");
    }
}

1.5sleep方法休眠

  Thread中的静态方法sleep(),当一个线程调用了sleep方法后,会短暂的让出cpu执行权(也就是不参与cpu的调度),但是线程拥有的锁等资源是不释放的,这一点和wait方法不同

1.6让出cpu执行权yield方法

  Thread中的静态方法yield(),当一个线程调用了Thread.yield()时,表名当前线程放弃cpu的使用权,yield之后,当前线程处于就绪状态。线程调度器会从包括该线程的所有线程中随机选出一个线程分配cpu执行时间片。

1.7中断线程

  java中的线程中断是一种线程间的协作模式,通过设置线程的中断标志并不能直接中断线程,而是被中断的线程根据中断标志自行处理。

  void interruupt() :中断线程

  boolean isInterrupted() :检测当前现车鞥是否中断(调用isInterrupted()方法的实例的中断标志)

  boolean interrupted(): 检测当前线程是否被中断(内部是获取当前线程的中断标志而不是调用interruupted()方法的实例的中断标志)

1.8线程的上下文切换

  在多线程编程中线程的个数一般都是大于cpu的个数的,而每个cpu在同一时刻只能被一个线程使用。为了让用户感觉实在同时执行,cpu资源的分配采用时间片轮询的侧率。

  上下文切换:就是一个线程使用完时间片之后,将cpu资源让给其他线程使用,并且线程状态转变为就绪状态。

1.9线程死锁

  多个线程在执行过程中,因争夺资源而造成相互等待的状态,如没有其他因素,线程会一直等待,而无法继续运行。

  死锁产生的条件:1、互斥 2、请求并持有资源 3、不可剥夺 4、环路等待

1.10守护线程和用户线程

  守护线程:类似垃圾回收线程

  用户线程:main函数启动,就是一个简单的用户线程

  jvm停止的时机:在用户线程全都执行完毕后,jvm就会停止(无论有没有守护线程)

1.11ThreadLocal

  当创建了一个ThreadLocal变量之后,多个线程操作这个变量时,实际上操作的是自己本地内存里的变量,也就是说每个线程都会复制一份变量到自己的本地内存。

  THreadLocal是怎么和线程关联起来的呢?

    ThreadLocal其实就是一个空壳,当调用ThreadLocal的set或get方法时,其内部是通过set方法把value放到调用线程的threadlocals变量中(ThreadLocal.threadLocalMap),也就是说本地变量不是存在ThreadLocal实例中,而是存入Thread中的threadlocals中。

    public void set(T value) {
        Thread t = Thread.currentThread();     // 当调用ThreadLocal的set方法时,其实是获取的是当前线程Thread中的ThreadLocalMap变量,如果变量为null,则新建一个ThreadLocalMap,如果变量存在,则之间将当前线程和value存入定制的map中(ThreadLocalMap)
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
    }
  // 从源码getMap中可以看出,其实获取的是当前线程中的threadLocals变量
    ThreadLocalMap getMap(Thread t) {
        return t.threadLocals;
    }

    void createMap(Thread t, T firstValue) {
        t.threadLocals = new ThreadLocalMap(this, firstValue);
    }
    public T get() {     // ThreadLocal的get方法,同样是获取的是当前线程Thread中的threadLocals变量(也就是ThreadLocalMap类型的数据)
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null) {
            ThreadLocalMap.Entry e = map.getEntry(this);
            if (e != null) {
                @SuppressWarnings("unchecked")
                T result = (T)e.value;
                return result;
            }
        }
        return setInitialValue();
    }

  

    

原文地址:https://www.cnblogs.com/nxzblogs/p/11318671.html

时间: 2024-10-11 07:43:44

java并发编程之美-阅读记录1的相关文章

java并发编程之美-阅读记录11

java并发编程实践 11.1ArrayBlockingQueue的使用 有关logback异步日志打印中的ArrayBlockingQueue的使用 1.异步日志打印模型概述 在高并发.高流量并且响应时间要求比较小的系统中同步打印日志在性能上已经满足不了了,这是以因为打印本身是需要写磁盘的,写磁盘操作会暂时阻塞调用打印日志的业务系统,这会造成调用线程的响应时间增加.    ----- >>> 异步日志打印,是将打印日志任务放入一个队列后就返回,然后使用一个线程专门从队列中获取日志任务,

java并发编程之美-阅读记录2

2.1什么是多线程并发编程 并发:是指在同一时间段内,多个任务同时在执行,并且执行没有结束(同一时间段又包括多个单位时间,也就是说一个cpu执行多个任务) 并行:是指在单位时间内多个任务在同时执行(也就是多个cpu同时执行任务) 而在多线程编程实践中,线程的个数一般是多于cpu的个数的 2.2为什么要多线程并发编程 多个cpu同时执行多个任务,减少了线程上下文切换的开销 2.3线程安全问题 共享资源:就是说该资源可以被多个线程持有,或者说能够被多个线程访问. 对共享资源的修改会造成线程安全问题.

java并发编程之美-阅读记录5

java并发包中的并发List 5.1CopeOnWriteArrayList 并发包中的并发List只有CopyOnWriteArrayList,该类是一个线程安全的arraylist,对其进行的修改操作都是在底层的一个复制数组上进行的,也就是使用了写时复制策略. 该类的结构: public class CopyOnWriteArrayList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable

《Java并发编程之美》(翟陆续著)高清pdf

<Java并发编程之美> 阿里巴巴技术专家力作,用代码说话.用实例验证,并发编程没有这么难!<Java并发编程的艺术>*作者方腾飞老师好评推荐! ? 百度网盘链接: https://pan.baidu.com/s/12oEEeDEO_YofImkpQA1bLA 提取码: pmkh  内容简介  · · · · · · 并发编程相比 Java 中其他知识点的学习门槛较高,从而导致很多人望而却步.但无论是职场面试,还是高并发/ 高流量系统的实现,却都离不开并发编程,于是能够真正掌握并发

Java并发编程之美之并发编程线程基础

什么是线程 进程是代码在数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,线程则是进程的一个执行路径,一个进程至少有一个线程,进程的多个线程共享进程的资源. java启动main函数其实就是启动了一个JVM的进程,而main函数所在的线程就是这个进程的一个线程,也称主线程. 进程和线程关系 一个进程有多个线程,多个线程共享进程的堆和方法区资源,但是每个线程有自己的程序计数器和栈区域. 程序计数器是一块内存区域,用来记录线程当前要执行的指令地址.如果执行的是native方法,那么pc计

[Java并发编程之美]第2章 并发编程的其他基础知识 补充知识

synchronized与volatile关键字 一. synchronized synchronized是Java语法中的一个内置锁的实现.synchronized关键字解决了代码块或者方法上的同步问题,同一时间,只有一个线程能够通过并执行.保证线程安全:内存可见性和原子性提供了并发场景的一个共享资源访问的解决方案. 当我们说synchronized锁住的是一个JVM对象时,真正发挥作用的是对象头上所指向的monitor对象(监视器机制:Java锁的底层实现). synchronized有两种

[Java并发编程之美]第1章 线程基础(待更新)

第1章 线程 线程与进程 进程是操作系统资源分配和调度的基本单位,但cpu资源是分配到线程的,也就是线程是CPU分配的基本单位. 线程自己的栈资源中,存放的局部变量是线程私有的,其他线程无法访问,除此之外栈还存线程的调用栈帧. 线程创建 三种方式:实现Runnable接口的run方法:继承Thread类并重写run方法:使用FutureTask方式. 线程等待与通知 1 wait() 线程先要事先获得共享变量上的监视器锁,然后当一个线程调用一个共享变量的wait()方法,该线程会被阻塞挂起,并且

【java并发编程艺术学习】(三)第二章 java并发机制的底层实现原理 学习记录(一) volatile

章节介绍 这一章节主要学习java并发机制的底层实现原理.主要学习volatile.synchronized和原子操作的实现原理.Java中的大部分容器和框架都依赖于此. Java代码 ==经过编译==>Java字节码 ==通过类加载器==>JVM(jvm执行字节码)==转化为汇编指令==>CPU上执行. Java中使用的并发机制依赖于JVM的实现和CPU的指令. volatile初探 volatile是是轻量级的synchronized,它在多处理器开发中保证了共享变量的可见性.可见性

java并发编程6.取消与关闭

如果外部代码能在某个操作正常完成之前将其置入"完成"状态,那么这个操作就可以称为可取消的. Java没有提供任何机制来安全地终止线程.但它提供了中断,这是一种协作机制,能够使一个线程终止另一个线程的当前工作. 其中一种协作机制能设置某个"已请求取消"的标志,而任务将定期地查看该标志,如果设置了这个标志,那么任务将提前结束. 自定义取消机制 /** * 素数生成器 */ private class PrimeGenerator implements Runnable{