美文网首页
从头开始学多线程

从头开始学多线程

作者: 小大宇 | 来源:发表于2021-07-16 10:37 被阅读0次

    《Java多线程编程核心技术》第一章读书笔记

    进程:正在执行的程序。进程是OS分配资源的最小单位。
    线程:正在执行的程序的子任务。线程是程序执行的最小单位。
    使用多线程也就是在使用异步。
    同步是什么?同步的概念就是共享
    异步是什么?异步的概念就是独立
    执行stop()方法,强制停止了某个线程,可能导致某些重要的释放资源代码没有执行。
    stop()方法会让线程释放同步锁。
    调用线程的suspend方法,线程暂停,但是并不释放同步锁。
    suspend方法极其容器造成死锁,因为它不释放同步锁
    静态方法Thread#yield()方法,相当于一个不固定时间的sleep(),所以不释放同步锁。
    A线程启动了B线程,那么B线程的优先级继承了A线程,所以B线程的优先级与A线程相同。
    守护线程的作用是对其它线程提供服务。
    如果程序中没有非守护线程了,那么守护线程就自动销毁了。典型的守护线程就是垃圾回收线程。

    Java多线程编程核心技术-----第二章读书笔记

    方法里面的变量是临时变量,在栈内创建。多次调用则多次创建临时变量,线程安全。
    同步方法的同步锁是本对象,静态同步方法的同步锁是当前类的字节码文件,即.class文件。
    当一个线程在得到对象锁,再次请求该对象锁是可以的。这就是synchronized锁的可重入性。
    线程出现异常会自动释放同步锁。
    同步不能被继承。需要为重写的方法加上synchronized修饰符,来保证同步。
    即与synchronized(类.class)文件 本质相同。
    避免使用字符串作为同步代码块的同步锁,因为字符串共享常量池。
    同步代码块中的同步锁引用指向的是一个堆内存中的对象。只要这个对象所在的内存地址不变,也不影响此对象作为锁的使用。
    volatile修饰的变量,内存存放在公共区,所有线程都能看到。让JVM禁止指令重排序优化
    volatile关键字最致命的缺点是:不支持原子性。所以还需对变量的操作加锁。
    synchronized在进入同步代码块之前,更新当前线程的工作内存,保证与公共内存的数据一致。
    i++和 ++j 都是对变量进行自增操作,结果是i和j都自增。但是其表达式的值不一样,前者是i,后者是j+1。

    Java多线程编程核心技术-----第三章读书笔记

    调用wait()或notify()等方法的对象,必须是同步锁对象,否则抛出IllegalMonitorStateException.
    wait()使当前执行代码的线程进入等待状态(冷冻暂停),并释放同步锁。
    sleep方法不释放同步锁。
    Object#wait
    Thread#sleep
    notify()随机使得一个wait状态的线程恢复执行。
    notify()需要在同步代码中执行,此线程执行完才释放同步锁。
    wait状态的线程必须要得到notify()或者notifyAll()才能被唤醒,否则一直处于wait状态。
    wait()/notify() 要结合 Synchronized 关键字一起使用,因为他们都需要首先获取该对象的对象锁;
    线程等待的原因
    sleep()方法的睡眠时间到了
    调用了wait()方法进入wait状态
    线程重新进入可执行状态的情况:
    sleep()方法的睡眠时间到了
    线程被notify()唤醒了
    wait(long)方法功能是:等待某一时间内是否有线程对其唤醒,超过此时间就自动唤醒。释放同步锁
    多生产多消费出现假死。假死出现的主要原因是有可能连续唤醒同类,解决办法是将异类也一同唤醒。
    伪代码:

    生产者 if(有产品){lock.wait} 生产 lock.notify  
    如果有产品,我就休息。不然进行生产,并通知消费者消费。
    
    消费者 if(没产品){lock.wait} 消费 lock.notify  
    如果没有产品,我就等待。不然的话就消费,消费完就通知生产。
    
    package com.ssi.domains.Add;
     
    /**
     * Created by jay.zhou on 2018/7/24.
     */
    public class Product {
        public String state = "无货";
     
        public void create() {
            state = "有货";
        }
     
        public void consume() {
            state = "无货";
        }
     
        public static void main(String[] args) {
            Product product = new Product();
            Object lock = new Object();
            ThreadA threadA = new ThreadA(product,lock);
            ThreadB threadb = new ThreadB(product,lock);
            threadA.start();
            threadb.start();
        }
    }
     
    class ThreadA extends Thread {
        private Product product;
        private Object lock;
     
        public ThreadA(Product product, Object lock) {
            this.product = product;
            this.lock = lock;
        }
     
        //生产者线程
        @Override
        public void run() {
            while (true) {
                try {
                    synchronized (lock) {
                        if (product.state.equals("有货")) {
                            lock.wait();
                        }
                        product.create();
                        System.out.println("生产者生产出货物");
                        lock.notify();
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
     
    class ThreadB extends Thread {
        private Product product;
        private Object lock;
     
        public ThreadB(Product product, Object lock) {
            this.product = product;
            this.lock = lock;
        }
     
        //消费者线程
        @Override
        public void run() {
            while (true) {
                try {
                    synchronized (lock) {
                        if (product.state.equals("无货")) {
                            lock.wait();
                        }
                        product.consume();
                        System.out.println("消费者消费完货物");
                        lock.notify();
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    

    A线程的任务中出现 B.join(); 那么A线程需要等待B线程任务执行完毕再继续执行。线程A调用 otherThread.join()方法,那么线程A进入 WAITING 状态,内部使用的是 wait()方法实现,所以会释放同步锁。
    ThreadLocal实现了每个线程内部都有自己的共享变量。
    SimpleDateFormat是线程不安全的,解决方法是为每个线程创建一个sdf对象,而不是共享。
    阿里巴巴:不要把SimpleDateFormat定义为静态变量,如果定义为静态变量,一定要加锁
    继承ThreadLocal类,重写initialValue()方法。创建此MyThreadLocal类对象,调用get()返回值就是此键值对的初始值。

    Java多线程编程核心技术-----第四章读书笔记

    调用ReentrantLock对象的lock()方法获取锁,调用unlock()方法释放锁。
    如果不调用lock.unlock()释放锁,那么即使线程任务结束,仍然不会释放锁。抛出异常也不会释放锁。
    Condition对象通过ReentrantLock对象生成。Condition condition = lock.newCondition();
    condition.await()方法让持有lock锁的对象暂停,并释放同步锁。
    condition.signal();一次只唤醒一个暂停的线程。
    condtion在调用await()方法和signal()方法之前,需要先调用lock.lock(),保证在同步环境中才可执行。
    Lock lock = new ReentrantLock(boolean b);
    如果boolean为true,那么此锁就是公平锁。公平体现在:先请求锁的线程优先获取锁。默认是非公平锁,进入可执行状态的线程,进行争夺同步锁
    if(lock.tryLock()){ A代码; }else{ B代码; } 如果获取不到同步锁,那么就执行B了,不然干等等到什么时候。
    if(lock.tryLock(3,TimeUnit.SECONDS)){ A代码; }else{ B代码; } 如果等了3秒还没有锁,那么执行B代码。
    ReentrantReadWriteLock是读写锁。
    多个读锁之间不互斥,读锁与写锁互斥,写锁与写锁互斥。
    创建读写锁

    ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
    lock.readLock().lock();
    lock.readLock().unlock();
    lock.writeLock().lock()
    lock.writeLock().unlock();
    

    ReentranceLock 等 同步组件,内部都用了 队列同步器AQS 实现。
    队列同步器(简称:同步器)AbstractQueuedSynchronizer(英文简称:AQS)
    AQS使用了一个 int 成员变量表示同步状态,标记共享变量的状态(是否已经被其他线程锁住等)
    AQS通过内置的 先进先出Node队列来完成资源获取和线程的排队工作。

    Java多线程编程核心技术-----第五章读书笔记

    JDK自带了一个定时任务类Timer类
    Timer类的主要作用就是设置任务,TimerTask类用于封装任务。

    Java并发编程的艺术-----第二章读书笔记

    并发执行的速度有时不如串行,原因是线程有创建与上下文切换的开销。
    如何减少上下文切换?
    使用CAS操作进行无锁并发编程。多线程竞争锁的时候,会引起上下文切换。JUC包下的原子操作类,它们都是使用的CAS操作,可以避免使用同步锁。
    dump可查看线程信息
    编程中避免死锁的办法
    避免一个线程同时获得多个锁
    使用定时锁。
    volatile关键字原理:将最先的变量的值写入到主内存中,使其它线程中缓存了该变量的内存地址失效
    锁一共有4中状态,级别从低到高分别是:无锁状态、偏向锁状态、轻量级锁状态、重量级锁状态。
    原子操作:不可被中断的操作。
    Java是如何实现原子操作的:循环执行CAS操作,直到成功为止。
    ABA问题:使用带版本号(时间戳)的原子操作类来避免。

    for(;;){
     
        int currentValue =  getCurrentValue();
        boolean succ = automicInteger.compareAndSet(currentValue,++currentValue);
        if(succ){
             break;
        }
     
    }
    

    Java并发编程的艺术-----第三章读书笔记

    Java线程之间的通信由Java内存模型(JMM)控制
    每个线程都有一个私有的本地内存


    image.png

    Java并发编程的艺术-----第四章读书笔记

    如何查看线程的状态?
    在终端输入 jps ,我们运行的Java类的名字是ThreadState。所以JPS可以看到我的Java代码的进程ID为3068,


    image.png

    再通过 jstack 3068 查看进程的中的所有线程信息。
    如下图可以发现,线程名为"BolckedThreadB"的线程的状态为 TIMED_WAITING ,说明此线程调用了 Thread.sleep()方法。


    image.png
    为什么 stop()、suspend()方法被废弃。因为这两个方法一个立即释放同步锁(导致脏读,资源没清理),一个不释放同步锁(导致死锁)。
    关键字synchronized表示:同一时刻,只能有一个线程处于临界区中。它能保证变量的可见性与排他性。
    可见性分析:在释放锁的时候,将线程私有变量刷新到主存。获取锁的时候,将私有内存中变量的值废弃,重新从主存中获取最新变量的值。

    读写锁通过分离读锁与写锁,使得并发性能比一般其它的排他锁有了很大提升。

    相关文章

      网友评论

          本文标题:从头开始学多线程

          本文链接:https://www.haomeiwen.com/subject/jakmpltx.html