多线程基础知识

所谓教条,就是生搬硬套,以开放的心态接受新概念,理解上下文,注重实践和经验。
Java的并发模型是传统的基于内存共享的并发模型

如何查看线程的状态(Jstack)

线程的生命周期

将线程比喻成人,是一种很好的理解方式,比喻成人的生老病死,人与人之间的协作,等等。

4. 线程安全问题 (原子性 可见性 有序性)

原子性 实例

public class Demo extends Thread {
    private static int num=100000;
    @Override
    public void run() {
        addNumber();
    }
// 如果不加synchronized 由于cpu的时间片的切换,有的减法操作丢失了他的执行权力,再次执行时数据产生偏差
   [synchronized]  static public void addNumber() {
        for (int i1 = 0; i1 < 5000; i1++) {
            num--;
        }
    }
    public static void main(String[] args) throws InterruptedException {
        Thread[] threads = new Thread[20];
        for (int i = 0; i < threads.length; i++) {
            new Thread(new Demo()).start();
        }
        System.out.println( "结果:" + num);
    }
}

可见性 实例

public class Demo  {
// volatile 使flag 可以被其他线程可见
    static volatile boolean flag =true;
    public static void main(String[] args) throws InterruptedException {
        new Thread(()->{
                    int i =0;
                    while (flag) {
                        i++;
                    }
                }
        ).start();
        Thread.sleep(1000); //主线程不成睡,会直接执行结束,不会显示答案
        flag=false;
    }

有序性 实例

CPU 的编译,指令优化会破坏语句的执行顺序

synchronized 对象锁用法

  1. 同步方法

  2. 同步静态方法

    synchronized static (this) {//对象
    }
    
  3. 同步方法块

    synchronized (this) {
    }
    

synchronized 对象锁实例

public class Demo3  implements  Runnable{
    static int num;
    synchronized static void add() { // 若果不是静态的方法,每一个对象一个方法,就不是争用资源,锁不住。
        for (int i = 0; i < 20000; i++) {
            num++;
        }
    }
    @Override
    public void run() {
        add();
    }
    public static void main(String[] args) throws InterruptedException {

        for (int i = 0; i < 10; i++) {
            Thread thread = new Thread(new Demo3());
            thread.start();
        }
        Thread.sleep(2000);
        System.out.println(num);

    }
}

线程的基本操作 (线程通信)

线程的三种创建方式- Callable 接口实现

public class CallableDemo implements Callable {
    @Override
    public Object call() throws Exception {
        return null;
    }
    @Override
    public void run() {
        System.out.println("runnable 实现的线程");
    }
    public static void main(String[] args) throws InterruptedException, ExecutionException {
        ExecutorService executor = Executors.newFixedThreadPool(2);
        Callable callable = new CallableDemo();
        Future<String> t1 = executor.submit(callable);
        String s = t1.get();
        System.out.println("开始消费");
    }
}

线程的三种创建方式- 继承Thread 类

public class Threaddemo extends Thread {
    @Override
    public void run() {
        System.out.println("hello");
    }

    public static void main(String[] args) {
        new Threaddemo().start();
    }
}

线程的三种创建方式- 实现runnable 接口

public class CallableDemo implements Runnable {
    @Override
    public void run() {
        System.out.println("runnable 实现的线程");
    }
    public static void main(String[] args) {
        Thread thread = new Thread(new CallableDemo());
        thread.start();
    }
}

join阻塞主线程实例

public class Demo3  implements  Runnable{
    static int num;
    @Override
    public void run() {
        for (int i = 0; i < 20000; i++) {
            num++;
        }
    }

    public static void main(String[] args) throws InterruptedException {
        for (int i = 0; i < 10; i++) {
            Thread thread = new Thread(new Demo3());
            thread.start();
//            thread.join(); //阻塞主线程,线程卓个执行 //阻塞主线程,卓个创建子线程执行,减少并发
        }
        Thread.sleep(2000);
        System.out.println(num);

    }
}
1. join 阻塞的是主线程
2. 因为多线程的执行是异步的,如果写代码时候,如果不阻塞主线程,有时候数据操作的值输出并不完整

wait notify 实例

main

public class Demo {
    public static void main(String[] args) throws InterruptedException {
        Queue<Integer> queue = new LinkedList<>();
        new Thread(new Puroduce(queue,100)).start();
        new Thread(new Consumer(queue,100)).start();

        Thread.sleep(1000);

        // 生产者一直生产, 直到到达100个,停止生产,阻塞 ---》 通知消费者消费
        // 消费者一直消费,没有可以消费的, 就停止消费, 阻塞;---》 通知生产者生产

    }
}

join 实例

interrupter 实例

yeild 实例

生产者

public class Puroduce implements Runnable {
    Queue<Integer> queue;
    private int size;

    public Puroduce(Queue<Integer> queue, int size) {
        this.queue = queue;
        this.size = size;
    }

    private void produce() throws Exception {
        int i = 0;
        while (true) {
            i++;
            synchronized (queue) {
                while (queue.size() == size) {
                    System.out.println("已经满了");
                    queue.wait();
                }
                queue.add(i);
                System.out.println("生产者开始生产: " + i);
                Thread.sleep(100);
                queue.notifyAll();
            }
        }
    }
    @Override
    public void run() {
        try {
            produce();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

消费者

public class Consumer implements Runnable {
    Queue<Integer> queue;
    private int size = 0;
    public Consumer(Queue<Integer> queue, int size) {
        this.queue = queue;
        this.size = size;
    }
    private void sonsume() throws Exception {
        int i = 0;
        synchronized (queue) {
            while (true) {
                i++;
                while (queue.isEmpty()){
                    System.out.println("已经消费完了");
                    queue.wait();
                }
                Integer remove = queue.remove();
                System.out.println("消费者开始消费: " + remove);
                Thread.sleep(100);
                queue.notifyAll();
            }
        }
    }
    @Override
    public void run() {
        try {
            sonsume();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

总结

synchronized 对象锁 锁住同样一个对象 让两个线程相互通信

JUC 包和AQS

什么是AQS

AQS(AbstractQueuedSynchronizer)是Java中的一个用于构建锁和同步器的框架,它是java.util.concurrent包中的核心组件之一。Doug Lea开发了AQS,它为实现依赖于先进先出(FIFO)等待队列的阻塞锁和相关的同步器(信号量、事件等)提供了一个基础框架。
AQS利用了一个整型的状态(state)变量来表示同步状态,通过内部的方法来改变这个状态,并且提供了一种高效的方式来管理线程的阻塞和唤醒。它是许多并发工具的基础,例如ReentrantLock、CountDownLatch、Semaphore、ReentrantReadWriteLock等。
AQS的工作原理:

1.状态(State): AQS使用一个整数(state)来表示同步状态,根据具体的同步器的需求,这个状态可以代表不同的意义。例如,在ReentrantLock中,状态为0表示未锁定状态,而大于0表示锁定状态,并且计数表示重入的次数。
2.等待队列: 当线程尝试获取同步状态失败时,AQS会将该线程包装成一个节点(Node)加入到等待队列中。等待队列是一个类型为Node的双向链表,用来管理所有等待获取同步状态的线程。
3.获取和释放同步状态: AQS定义了两种资源获取的方式:独占和共享。独占方式下,一个时间点只能有一个线程获取到资源。共享方式下,允许多个线程同时获取到资源。具体的同步器可以根据需求选择实现这两种方式中的一种或两种。

AQS的重要方法:

  1. protected boolean tryAcquire(int arg):尝试获取资源,成功则返回true,失败则返回false。
  2. protected boolean tryRelease(int arg):尝试释放资源,成功则返回true,失败则返回false。
  3. protected int tryAcquireShared(int arg):尝试获取共享资源。
  4. protected boolean tryReleaseShared(int arg):尝试释放共享资源。
  5. public final void acquire(int arg)、public final void acquireShared(int arg):调用这些方法的线程将会在获取不到资源时,被加入到等待队列中,并在条件满足时被唤醒。

AQS通过内部维护的等待队列来管理所有竞争资源的线程,当一个线程获取资源时,AQS会根据资源的状态和获取模式(独占/共享)来决定是否需要阻塞线程,当资源被释放时,会从等待队列中唤醒一个或多个线程来尝试获取资源。这个机制是构建锁和其他同步器的基础,使得开发者可以较为容易地实现安全的并发控制。

重入锁ReentrantLock

public class Demo3  implements  Runnable{
    static int num;
    static ReentrantLock lock = new ReentrantLock(); // 可以锁住非静态方法

    void add() {
        lock.lock();// 加锁
        for (int i = 0; i < 20000; i++) {
            num++;
        }
        lock.unlock();// 解锁
    }

    @Override
    public void run() {
        add();
    }

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

        for (int i = 0; i < 10; i++) {
            Thread thread = new Thread(new Demo3());
            thread.start();
        }
        Thread.sleep(2000);
        System.out.println(num);

    }
}
重入锁的意思,就是一个对象在访问多个争用资源时候,拿到了相同的锁之后,就不用再去获得锁

如果是共享锁,对象在访问多个争用资源的时候,获得了一个锁,别的对象没有锁不能访问该资源,而访问第二个争用资源时,又没有先释放锁,造成了死锁现象。

java 中如何使用线程池

线程池的类别

线程池的使用方法 Executors.newFixedThreadPool 实例

public class Demo4 implements  Runnable{
    static int num;
    synchronized static void add() {
        for (int i = 0; i < 20000; i++) {
            num++;
        }
    }
    @Override
    public void run() {
        add();
        System.out.println(Thread.currentThread().getName());
    }

    public static void main(String[] args) throws InterruptedException {
        ExecutorService executorService = Executors.newFixedThreadPool(2);
        for (int i = 0; i < 3; i++) {//启动3个任务
            executorService.execute(new Demo4());
        }
        Thread.sleep(200);
        System.out.println("最终结果: "+ num);
        executorService.shutdownNow();
    }
}

java 的对象结构

mark Word 结构信息

JOL 查看对象布局

锁升级

JMM 内存模型

posted @ 2020-11-18 22:48  庭有奇树  阅读(141)  评论(0编辑  收藏  举报