多线程---多线程基础知识

画个板子玩一玩STM32F030F4P6,也许是最便宜的32bit MCU 前言,什么是STM32F030F4P6 STM32目前最便宜的芯片之一应该就是这一块,STM32F030F4P6,这一个芯片没有USB,但是其他都有,引脚少,价格低廉,如果找不到原厂,可以买替代的国产芯片,性能也一样。 不管怎么样也是Cortex-M0,比8bit和16bit的性能都强,总的来说就是低价,引脚少,体积小的同时,还容易开发,有很多模拟输入可玩,还有SPI,硬件I2C等等 所以,这一款MCU真的是从理论来说又便宜又好用,非常适合把玩。 画个板,最小系统 STM32F030其.. 阅读详情

2. 线程的基本概念

2.1 启动线程的5中方法

  1. new MyThread().start()
  2. new Thread®.start
  3. new Thread(lambda).start()
  4. ThreadPool
  5. Future Callable and FutureTask

代码示例

package com.cyc.juc.c_000_00_threadbasic;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.FutureTask;

/**
 * @description 启动线程的5种方式
 * @author cyc
 * @date 2021-07-25 20:11:02
 */
public class T02_HowToCreateThread {
    static class MyThread extends Thread {
        @Override
        public void run() {
            System.out.println("Hello MyThread!");
        }
    }

    /**
     * 使用实现Runable方式更灵活, 因为MyRun类还可以继承其他类,而继承Thread方式,则不可再继承其他的类
     */
    static class MyRun implements Runnable {
        @Override
        public void run() {
            System.out.println("Hello MyRun!");
        }
    }

    /**
     * 实现Callable<T>接口可返回数据, 可以指定返回泛型
     */
    static class MyCall implements Callable<String> {

        @Override
        public String call() throws Exception {
            System.out.println("Hello MyCall!");
            return "success";
        }
    }

    //启动线程的5种方式
    public static void main(String[] args) throws Exception {
        //1. 继承Thread类
        new MyThread().start();

        //2. 实现Runnable接口
        new Thread(new MyRun()).start();

        //3. 使用lambda表示式
        new Thread(() -> {
            System.out.println("Hello Lambda!");
        }).start();

        //4. 实现Callable接口
        FutureTask<String> task = new FutureTask<>(new MyCall());
        Thread thread = new Thread(task);
        thread.start();

        //5. 线程池
        ExecutorService service = Executors.newCachedThreadPool();
        service.execute(() -> {
            System.out.println("Hello Thread Pool");
        });

        Future<String> f = service.submit(new MyCall());
        //Future.get()方法是线程阻塞的状态, 什么时候MyCall执行完了, 直到f.get拿到值为止,才能继续往下执行
        String s = f.get();
        System.out.println(s);
        service.shutdown();

        //以上这些创建线程的方式, 本质上是一种,都是new 一个Thread()对象出来, 然后调用他的start方法
    }

}

2.2 线程的状态

2.2.1 JAVA的6种线程状态

  1. NEW : 线程刚刚创建,还没有启动
  2. RUNNABLE : 可运行状态(包含READY和RUNNING两种细分状态)
  3. WAITING : 等待被唤醒
  4. TIMED_WAITING : 隔一段时间后自动唤醒
  5. BLOCKED : 被阻塞,正在等待锁
  6. TERMINATED : 线程结束

如下图

在这里插入图片描述

每种状态的代码演示

1. NEW
        Thread t1 = new Thread(() -> {
            System.out.println("2: " + Thread.currentThread().getState());
            for (int i = 0; i < 3; i++) {
                SleepHelper.sleepSeconds(1);
                System.out.println(i + " ");
            }
        });
        //这里还没有调用start方法, 所以线程是new状态
        System.out.println("1: " + t1.getState());
2. RUNNABLE (RUNNING+READY)
Thread t1 = new Thread(() -> {
    System.out.println("2: " + Thread.currentThread().getState());
    for (int i = 0; i < 3; i++) {
        SleepHelper.sleepSeconds(1);
        System.out.println(i + " ");
    }
});
//这里还没有调用start方法, 所以线程是new状态
System.out.println("1: " + t1.getState());
//这里调用start方法, 线程开始执行,状态是RUNNABLE, 其中RUNNABLE可分为ready和running,
// CPU时间片轮到这个线程了, 它就是running, 没有轮到这个线程, 它就是ready
t1.start();
//
t1.join();
System.out.println("3: " + t1.getState());
3. WAITING
Thread t2 = new Thread(() -> {
    //park用于挂起当前线程,什么时候被叫醒了 , 什么时候执行下面的
    LockSupport.park();
    System.out.println("t2 go on!");
    try {
        TimeUnit.SECONDS.sleep(5);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
});
t2.start();
//执行LockSupport.park(),这里睡一秒钟, 让当前线程完全执行起来,
TimeUnit.SECONDS.sleep(1);
//由于 LockSupport.park()将当前线程挂起了, 所以这里的线程状态是WAITING
System.out.println("4: " + t2.getState());
4. TIMED_WAITING
Thread t2 = new Thread(() -> {
    //park用于挂起当前线程,什么时候被叫醒了 , 什么时候执行下面的
    LockSupport.park();
    System.out.println("t2 go on!");
    try {
        TimeUnit.SECONDS.sleep(5);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
});
t2.start();
//执行LockSupport.park(),这里睡一秒钟, 让当前线程完全执行起来,
TimeUnit.SECONDS.sleep(1);
//由于 LockSupport.park()将当前线程挂起了, 所以这里的线程状态是WAITING
System.out.println("4: " + t2.getState());

LockSupport.unpark(t2);
//睡一秒钟, 确认t2已经被叫醒
TimeUnit.SECONDS.sleep(1);
//因为这里醒了之后, 会执行sleep,所以这里的状态是TIMED_WAITING
System.out.println("5: " + t2.getState());
5. BLOCKED

在多线程中, 使用synchronized给对象加锁,在锁未释放,其他线程获取不到时, 此时获取不到锁的线程为

阻塞状态(BLOCKED),因为synchronize经过操作系统的调度,所以,对于线程来说,也只有使用synchronized, 线程才会有BLOCKED状态, 其余锁对线程来说都是WAITING状态

		final Object o = new Object();

        Thread t3 = new Thread(() -> {
            synchronized (o) {
                System.out.println("t3 得到了锁 o");
            }
        });

        SleepHelper.sleepSeconds(1);

        //新启动一个线程, 获取这把锁, 并睡5秒钟
        new Thread(() -> {
            synchronized (o) {
                SleepHelper.sleepSeconds(5);
            }
        }).start();

        //主线程睡一秒, 保证上面的线程已经锁定了这把锁o
        SleepHelper.sleepSeconds(1);

        //t3线程启动,去尝试获取锁,此时获取不到锁
        t3.start();
        // 主线程睡一秒, 保证t3已经启动完成
        SleepHelper.sleepSeconds(1);
        //t3线程去拿这把锁, 拿不到, 所以状态是BLOCKED
        System.out.println("6: " + t3.getState());

和WAITING状态对比如下

 		final Lock lock = new ReentrantLock();

        Thread t4 = new Thread(() -> {
            //Lock属于JUC的锁, 使用CAS实现, CAS实现的锁, 会进入一种忙等待, 不会进入BLOCKED状态, 进入的是WAITING状态
            lock.lock();//这里省略try catch
            System.out.println("t4 获取到了锁");
            lock.unlock();
        });

        new Thread(() -> {
            lock.lock();
            //这里睡5秒, 也就是当前线程持有这把锁5秒
            SleepHelper.sleepSeconds(5);
            lock.unlock();
        }).start();

        //主线程睡一秒, 确保上面的线程完全启动
        SleepHelper.sleepSeconds(1);

        t4.start();
        //主线程睡一秒, 确保t4启动完毕
        SleepHelper.sleepSeconds(1);
        //由于上面的线程持有锁5秒, 减去上面睡的共计2秒, 释放锁时间还剩3秒,
        // 所以t4目前还是没有获取到锁, 此时的状态为WAITING
        System.out.println("7: " + t4.getState());
        Thread t5 = new Thread(() -> {
            LockSupport.park();
        });

        t5.start();

        SleepHelper.sleepSeconds(1);
        //除synchronized之外, 其余的锁对线程来说都是WAITING状态
        System.out.println("8: "+t5.getState());
6. TERMINATED
Thread t1 = new Thread(() -> {
    System.out.println("2: " + Thread.currentThread().getState());
    for (int i = 0; i < 3; i++) {
        SleepHelper.sleepSeconds(1);
        System.out.println(i + " ");
    }
});
//这里还没有调用start方法, 所以线程是new状态
System.out.println("1: " + t1.getState());
//这里调用start方法, 线程开始执行,状态是RUNNABLE, 其中RUNNABLE可分为ready和running,
// CPU时间片轮到这个线程了, 它就是running, 没有轮到这个线程, 它就是ready
t1.start();
//
t1.join();
//线程执行结束
System.out.println("3: " + t1.getState());

2.2 线程的打断

2.2.1 interrupt的三个方法

//Thread.java
public void interrupt()  // t.interrupt() 打断t线程(设置t线程的标志位f=true,并不是打断线程的运行)
public boolean isInterrupt() //t.isinterrupt() 查询打断标志位是否被设置(是不是曾经被打断过)
public static boolean interrupted() //Thread.interrupt() 查看"当前"线程是否被打断,如果被打断,则恢复标志位

代码示例

    public static void main(String[] args) {
        Thread t = new Thread(() -> {
            for (; ; ) {
                if (Thread.currentThread().isInterrupted()) {
                    //检查当前线程是否被设置过标志位, 如果设置过, 则打印结果,并跳出循环
                    System.out.println("Thread is interrupt!");
                    System.out.println(Thread.currentThread().isInterrupted());
                    //优雅的结束线程的方式, 设置标志位, 并在程序运行中检查标志位,如果设置了, 则结束该线程, 如下方的break
                    break;
                }
            }
        });
        t.start();
        SleepHelper.sleepSeconds(2);
        //两秒后 ,设置t线程的标志位
        t.interrupt();
    }
    public static void main(String[] args) {
        Thread t = new Thread(() -> {
            for (; ; ) {
                if (Thread.interrupted()) {
                    //interrupted会检查当前线程是否被打断,并重置标志位
                    System.out.println("Thread is interrupt!");
                    System.out.println(Thread.currentThread().isInterrupted());
                }
            }
        });
        t.start();
        SleepHelper.sleepSeconds(2);
        //两秒后 ,设置t线程的标志位
        t.interrupt();
    }

2.2.2 interrupt和sleep() wait() join()

sleep()方法在睡眠的时候,不到时间是没有办法唤醒的,这个时候可以用interrupt设置标志位,然后必须的catchInterruptException来进行处理,决定继续睡或者是别的逻辑,(自动进行中中断标志位复位)

public class T07_Interrupt_and_sleep {

    public static void main(String[] args) {
        Thread t = new Thread(() -> {
            try {
                Thread.sleep(10000);
            } catch (InterruptedException e) {
                //线程被打断异常, 抛异常后, JAVA默认会将该线程的标志位重置
                System.out.println("Thread is interrupted!");
                System.out.println(Thread.currentThread().isInterrupted());
            }
        });
        t.start();

        SleepHelper.sleepSeconds(5);
        //5秒后, 给线程设置标志位
        //在线程sleep(),wait(),join()操作中, 设置标志位会导致抛InterruptedException异常,
        // 此时需手动捕获异常,并进行处理, 可以继续运行, 也可以结束线程
        t.interrupt();
    }
}
public class T08_Interrupt_and_wait {

    private static Object o = new Object();

    public static void main(String[] args) {
        Thread t = new Thread(() -> {
            synchronized (o) {
                try {
                    o.wait();
                } catch (InterruptedException e) {
                    //线程被打断异常, 抛异常后, JAVA默认会将该线程的标志位重置
                    System.out.println("Thread is interrupted!");
                    System.out.println(Thread.currentThread().isInterrupted());
                }
            }
        });
        t.start();

        SleepHelper.sleepSeconds(5);
        //5秒后, 给线程设置标志位
        //在线程sleep(),wait(),join()操作中, 设置标志位会导致抛InterruptedException异常,
        // 此时需手动捕获异常,并进行处理, 可以继续运行, 也可以结束线程
        t.interrupt();
    }
}

2.2.3 interrupt 是否能中断正在竞争锁的线程

synchronized锁
/**
 * @Description: 设标志位能不能把争抢锁的线程打断?
 * 结论: synchronized锁竞争的过程是不会被interrup()打断的
 * @version 1.0
 * @author cyc
 * @date 2021/7/29 11:24
 */
public class T09_Interrupt_and_sync {

    private static Object o = new Object();

    public static void main(String[] args) {
        Thread t1 = new Thread(() -> {
            synchronized (o) {
                SleepHelper.sleepSeconds(10);
            }
        });
        //t1 启动后, 锁定这把锁10秒钟
        t1.start();

        SleepHelper.sleepSeconds(1);

        Thread t2 = new Thread(() -> {
            synchronized (o) {
                System.out.println("t2 抢到了这把锁");
            }
            System.out.println("t2 end!");
        });
        //启动t2, t2去抢这个锁, 但是由于t1还在持有这把锁, 在9秒后才会释放, 此时t2处于阻塞(BLOCKED)状态
        t2.start();
        SleepHelper.sleepSeconds(1);
        System.out.println("t2的状态: "+t2.getState());
        //由于这里这是设置标志位, 并不是打断线程, 所以不会打断t2竞争锁的过程
        t2.interrupt();
    }
}

Lock锁
/**
 * @Description: 设标志位能不能把争抢锁的线程打断?
 * 结论: ReentrantLock锁竞争的过程是不会被interrup()打断的
 * @version 1.0
 * @author cyc
 * @date 2021/7/29 11:24
 */
public class T10_Interrupt_and_lock {

    private static ReentrantLock lock = new ReentrantLock();

    public static void main(String[] args) {
        Thread t1 = new Thread(() -> {
            lock.lock();
            try {
                Thread.sleep(10000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
            System.out.println("t1 end");
        });
        //t1 启动后, 锁定这把锁10秒钟
        t1.start();

        SleepHelper.sleepSeconds(1);

        Thread t2 = new Thread(() -> {
            lock.lock();
            try {

            } finally {
                lock.unlock();
            }
            System.out.println("t2 end!");
        });
        //启动t2, t2去抢这个锁, 但是由于t1还在持有这把锁, 在9秒后才会释放, 此时T2处于WAITING状态
        t2.start();
        SleepHelper.sleepSeconds(1);
        System.out.println("t2的状态: " + t2.getState());
        //由于这里这是设置标志位, 并不是打断线程, 所以不会打断t2竞争lock锁的过程
        t2.interrupt();
    }
}

如何打断锁竞争的过程

如果竞争锁过程中,不想被打断 ,使用Lock.lock()加锁, 如果想可以被打断, 则使用Lock.lockInterruptibly()

/**
 * @Description: 设标志位能不能把争抢锁的线程打断?
 * @version 1.0
 * @author cyc
 * @date 2021/7/29 11:50
 */
public class T11_Interrupt_and_lockInterruptibly {

    private static ReentrantLock lock = new ReentrantLock();
 
    public static void main(String[] args) {
        Thread t1 = new Thread(() -> {
            lock.lock();
            try {
                Thread.sleep(10000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
            System.out.println("t1 end");
        });
        //t1 启动后, 锁定这把锁10秒钟
        t1.start();

        SleepHelper.sleepSeconds(1);

        Thread t2 = new Thread(() -> {
            System.out.println("t2 start!");
            try {
                //lockInterruptibly(), 抢锁的过程可被打断
                lock.lockInterruptibly();
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
            System.out.println("t2 end!");
        });
        //启动t2, t2去抢这个锁
        t2.start();
        SleepHelper.sleepSeconds(1);
        //由于这里这是设置标志位, 并不是打断线程, 所以不会打断t2竞争lock锁的过程
        t2.interrupt();
    }
}

3. 优雅的结束线程

如何优雅的结束一个线程?

例如: 上传一个大文件,正在处理费时的计算,如何优雅的结束这个线程?

结束线程的方法:

  1. 自然结束(能自然结束的就尽量自然结束)
  2. stop() , suspend() , resume()
  3. volatie标志
    1. 不适合某些场景(比如还没有同步的时候,线程做了阻塞操作,没有办法循环回去)
    2. 打断时间也不是特别精确,比如一个阻塞容器,容量为5的时候结束生产者,但是,由于volatile同步线程标志位的时候的时间控制不是特别精确,有可能生产者还继续生产一段时间
  4. interrupt() and isInterrupt(比较优雅)
    public static void main(String[] args) {
        Thread t = new Thread(() -> {
            while (true) {
                //假设每隔一秒钟上传一个程序
                System.out.println("go on");
                SleepHelper.sleepSeconds(1);
            }
        });

        t.start();

        SleepHelper.sleepSeconds(5);
        //为什么不建议使用stop方法,
        //太粗暴了,他会释放所有锁, 容易造成数据不一致的问题
        t.stop();
    }
    public static void main(String[] args) {
        Thread t = new Thread(() -> {
            while (true) {
                //假设每隔一秒钟上传一个程序
                System.out.println("go on");
                SleepHelper.sleepSeconds(1);
            }
        });

        t.start();

        SleepHelper.sleepSeconds(5);
        //暂停, 不会释放锁,容易产生死锁问题, 如果没有resume , 那么这把锁就永远不会被释放,
        t.suspend();
        SleepHelper.sleepSeconds(3);
        //恢复执行
        t.resume();
    }
    private static volatile boolean running = true;

    public static void main(String[] args) {
        Thread t = new Thread(() -> {
            long i = 0L;
            while (running) {
                //wait recv accept
                i++;
            }
            System.out.println("end and i = " + i);
        });
        t.start();

        SleepHelper.sleepSeconds(1);
        running = false;
    }
    public static void main(String[] args) {
        Thread t = new Thread(() -> {
            while (!Thread.interrupted()) {
                //sleep wait
            }
            System.out.println("t end");
        });
        t.start();

        SleepHelper.sleepSeconds(1);
        //使用interrupt更加优雅, 但是依然不能精确控制
        t.interrupt();
    }

4. 线程三大特性

可见性

在这里插入图片描述

代码示例

public class T01_HelloVolatile {

    private static boolean running = true;

    private static void m() {
        System.out.println("m start");
        while (running){
//            System.out.println("hello");
        }
        System.out.println("m end");
    }

    public static void main(String[] args) {
        new Thread(T01_HelloVolatile::m,"t1").start();

        SleepHelper.sleepSeconds(1);

        running = false;
    }
}

这个程序 , 如果不手动停止, 循环就不会停止

解析:

running首先位于主内存中, 每个线程都会拿主内存中running的值,放到线程本地的缓存中一份,每次循环,并不是去读主内存的running, 而是去读自己缓存中running的值,所以这个值只要没有从新从主线程中取过, 那线程使用的running的值, 就一直是本地缓存中的。此时主线程将自己本地缓存中的running改为了false,但是t1线程中的running值并没有改变, 所以, 这里循环一直不会结束。

  • 添加 volatile修饰
// volatile所修饰的这块内存, 对于这块内存任何的修改, 都对其他线程可见, 即保持可见性
//其他线程每次读这个running的值, 都要去主内存中取, 因此主线程中修改的running值, 会立即刷新到主内存中,
//t1线程重新读取主内存中running的值
private static volatile boolean running = true;
  • 某些语句触发内存缓存同步刷新

    例如:system.out.print()

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

通过观察print源码, 发现里面有synchronized修饰,会在某些情况下, 触发更新running在主内存中的值。

注意: volatile 引用类型(包括数组) 只能保证引用本身的可见性, 不能保证内部字段的可见性

例如

/**
 * @Description: volatile 引用类型(包括数组) 只能保证引用本身的可见性, 不能保证内部字段的可见性
 * @version 1.0
 * @author cyc
 * @date 2021/7/29 15:02
 */
public class T02_VolatileReference {

    private static class A{
        boolean running = true;

        void m(){
            System.out.println("m start");
            while(running){
            }
        }
    }

    private volatile static A a = new A();

    public static void main(String[] args) {
        new Thread(a::m,"t1").start();
        SleepHelper.sleepSeconds(1);
        a.running = false;
    }
}

因为volatile修饰的是对象a,a引用指向了堆内存中的示例,但是a中的字段running修改后并不会被刷新到主内存中,所以这个循环永远不会结束。因此,volatile关键字一般用在字段上而不是对象上。

有序性

先看一个例子

/**
 * @author cyc
 * @date 2021/7/29 16:51
 */
public class T01_Disorder {

    private static int x = 0, y = 0;

    private static int a = 0, b = 0;

    public static void main(String[] args) throws InterruptedException {
        for (long i = 0; i < Long.MAX_VALUE; i++) {

            x = 0;
            y = 0;
            a = 0;
            b = 0;

            CountDownLatch latch = new CountDownLatch(2);

            Thread one = new Thread(() -> {
                a = 1;
                x = b;
                latch.countDown();
            });

            Thread other = new Thread(() -> {
                    b = 1;
                    y = a;
                    latch.countDown();
            });
            one.start();
            other.start();
            latch.await();
            String result = "第" + i + "次 (" + x + "," + y + ") ";
            if (x == 0 && y ==0) {
                //只有当x=b和y=a先执行后, 才会出现这种情况, 也就是乱序执行
                System.err.println(result);
                break;
            }
        }
    }
}

在这里插入图片描述

为什么会乱序?

简单来说, 是为了提高效率

CPU为了提高执行效率, 而做出的一种对执行顺序的优化。

在这里插入图片描述

乱序存在的条件

  • as-if-serial(看似顺序执行)
  • 不影响单线程的最终一致性
/**
 * @Description: 《Java并发编程实践》小程序
 * 有两大问题, 第一可见性问题,ready前应该加volatile修饰,第二,有序性问题
 * @version 1.0
 * @author cyc
 * @date 2021/7/29 17:26
 */
public class T02_NoVisbility {

    private static boolean ready = false;
    //number初始值为0
    private static int number;

    private static class ReaderThread extends Thread{

        @Override
        public void run() {
            while (!ready){
                Thread.yield();
            }
            System.out.println(number);
        }
    }

    public static void main(String[] args) throws InterruptedException {
        Thread t = new ReaderThread();
        t.start();
        //理论上来说number = 42和ready = true因为没有依赖关系
        //所以有可能会乱序执行, 即先执行ready=true,再执行number =42, 这种情况下, 上面的输出就会是0
        number = 42;
        ready = true;
        t.join();
    }
}
/**
 * @version 1.0
 * @author cyc
 * @date 2021/7/29 17:43
 */
public class T03_ThisEscape {

    private int num = 8;

    public T03_ThisEscape(){
        new Thread(()->{
            System.out.println(num);
        }).start();
    }

    public static void main(String[] args) throws IOException {
        //这里有可能会输出中间状态0,
        new T03_ThisEscape();
        //确保以上线程执行完,再结束主程序
        System.in.read();
    }
}

原子性

/**
 * volatile并不能保证多个线程共同修改running变量时所带来的不一致问题,也就是说volatile不能替代synchronized
 * 运行下面的程序,并分析结果
 * @author cyc
 */
package com.cyc.juc.c_012_Volatile;

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

public class T04_VolatileNotSync {
   volatile int count = 0;
   void m() {
      for(int i=0; i<10000; i++) {
         count++;
      }
   }
   
   public static void main(String[] args) {
      T04_VolatileNotSync t = new T04_VolatileNotSync();
      
      List<Thread> threads = new ArrayList<Thread>();
      
      for(int i=0; i<10; i++) {
         threads.add(new Thread(t::m, "thread-"+i));
      }
      
      threads.forEach((o)->o.start());
      
      threads.forEach((o)->{
         try {
            o.join();
         } catch (InterruptedException e) {
            e.printStackTrace();
         }
      });
      
      System.out.println(t.count);
      
      
   }
   
}

优化:

/**
 * 对比上一个程序,可以用synchronized解决,synchronized可以保证可见性和原子性,volatile只能保证可见性
 * @author cyc
 */
package com.cyc.juc.c_012_Volatile;

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


public class T05_VolatileVsSync {
   /*volatile*/ int count = 0;

   synchronized void m() { 
      for (int i = 0; i < 10000; i++) {
         count++;
      }
   }

   public static void main(String[] args) {
      T05_VolatileVsSync t = new T05_VolatileVsSync();

      List<Thread> threads = new ArrayList<Thread>();

      for (int i = 0; i < 10; i++) {
         threads.add(new Thread(t::m, "thread-" + i));
      }

      threads.forEach((o) -> o.start());

      threads.forEach((o) -> {
         try {
            o.join();
         } catch (InterruptedException e) {
            e.printStackTrace();
         }
      });

      System.out.println(t.count);

   }

}
Hypermesh二次开发实战:从Tcl/Tk脚本到Ext API的进阶之路 本文系统介绍了Hypermesh二次开发的进阶路径,从自动化重复操作的Tcl/Tk脚本入门,到利用HMASCII接口深度读写模型数据,最终深入Ext API进行高性能计算与功能定制。通过实战案例,帮助工程师掌握从脚本录制到C++插件开发的完整技能,显著提升CAE前处理效率与自动化水平。 阅读详情

相关推荐

Microchip Studio 7.0 从下载到项目调试:AVR开发者的完整入门指南(附常见问题解决)

本文为AVR开发者提供了Microchip Studio 7.0从下载安装到项目调试的完整入门指南。详细介绍了如何获取安装包、创建第一个AVR工程项目,并配置编译与调试环境。同时,文章重点解决了开发过程中常见的编译错误和仿真问题,帮助开发者快速上手并高效使用这一官方集成开发环境。

joy55的博客 332

线程的状态

只有synchronized这样的代码——才会进入BLOCKED状态。因为synchronized是需要经过操作系统的调度的。

fuyuanduan的博客 247

Win10插网线显示未识别网络的三种解决方法

本文介绍了三种解决Win10插网线显示未识别网络问题的方法:更新或重启网络设备驱动程序、重置Winsock目录、启用Network Setup Service服务。这些方法都是基于常见的网络问题和解决方案而设计的,适用于大多数Win10用户。如果你遇到了这一问题,可以按照上述步骤进行操作,相信一定能够找到解决问题的方法。需要注意的是,在进行任何操作之前,请务必备份重要数据以防万一。此外,如果你对计算机硬件或软件不熟悉,建议寻求专业人士的帮助以避免造成不必要的损失。

nntxthml的博客 6770

多线程--04--线程的生命周期

线程的生命周期 public enum Stat{ } 内部内枚举类 线程生命周期,总共有五种状态: 新建状态(New):当线程对象对创建后,即进入了新建状态,如:Thread t = new MyThread(); 就绪状态(Runnable):当调用线程对象的start()方法(t.start();),线程即进入就绪状态。处于就绪状态的线程,只是说明此线程已经做好了准备,随时等待CPU调度执行,并不是说执行了t.start()此线程立即就会执行; 运行状态(Running):当CPU开始

时光里的博客 591

python画弗里德曼(Friedman) 检验和秩和检验(Nemenyi)检验图

python画弗里德曼(Friedman) 检验和秩和检验(Nemenyi)检验图 画好后效果图 实现代码如下 import matplotlib.pyplot as plt #算法平均排名 _alg_=[1.405,2.643,2.786,2.167] y=[1,2,3,4]#alg1,alg2,alg3,alg4 通过Nemenyi测试计算得到的临界值CD CD=1.024 h_CD=CD/2 plt.figure(figsize=(10,6)) plt.scatter(_alg_,y,s=10

大模型天花板 4464

ADS1299寄存器配置与应用实战指南

本文提供了ADS1299高精度生物电信号采集芯片的寄存器配置与应用实战指南。详细解析了采样率、增益、转换模式等核心寄存器的设置方法,并分享了数据读取、高级功能配置及常见问题的调试心得,帮助开发者快速掌握ADS1299的配置技巧,实现稳定的ECG/EEG信号采集。

weixin_28278377的博客 194

提升--02---线程的打断(interrupt)--线程的结束

线程的打断(interrupt) interrupt相关的三个方法: interrupt只是设置标志位,并不是真的断开线程 public void interrupt() //t.interrupt() 打断t线程(设置t线程某给标志位f=true,并不是打断线程的运行) public boolean isInterrupted() //t.isInterrupted() 查询打断标志位是否被设置(是不是曾经被打断过) public static boolean int

时光里的博客 994

使用Hyper-V使用下载下来的macOS安装引导安装macos

然后关闭工具返回安装界面选择第二个reinstall,选择119g那个硬盘。我的资源上传了:macOS Monterey on Hyper-V.7z。设置固件的启动顺序,引导安装盘放到第一位,mac安装110g放第二。选择disk utility格式化那个110g。下载解压的macos引导挂载到虚拟硬盘。其他设置安全禁用,检查禁用,集成都选中。新建一个虚拟硬盘装mac,110g。

viviliving的专栏 3023

一、线程基本知识

一、计算机底层层面是逻辑图,然后再深刻理解:程序、进程、线程、纤程 进程(重)->线程(普通)->纤程(轻) 进程:是操作系统资源分配的基本单位,比如内存、打开文件、网络IO,分配了独立的内存空间 线程:是操作系统资源调度的基本单位,cpu分配的基本单位 纤程:是用户态的线程,是线程中的线程,切换和调度不需要经过OS(操作系统)。;轻量级的线程 - 线程 // 进程:是操作系统资源分配的基本单位,比如内存、打开文件、网络IO,分配了独立的内存空间 public class..

Somous_的博客 560

Windows下Anaconda和PyTorch深度学习环境完整安装配置教程

本文详细介绍了在Windows系统下安装配置PyTorch深度学习环境的完整流程。主要内容包括:硬件环境检查(确认GPU支持)、Anaconda环境管理系统的安装与配置、虚拟环境的创建与管理方法、以及PyTorch及其相关组件的安装指南。特别强调了版本兼容性选择策略(如CUDA版本的确定)、显卡驱动更新、使用Anaconda创建隔离的PyTorch专用环境等关键步骤。无论是使用CPU还是GPU版本,读者都能按照教程快速搭建稳定的PyTorch开发环境,为深度学习项目打下坚实基础。

FJN110的博客 971

【乐企】有关乐企能力测试接口对接-基础版(详细)

乐企能力测试接口对接详细代码以及步骤

九七的博客 7655

2021_04_12_优雅结束线程

1.stop(已经废弃) 太粗暴,容易产生数据不一致,不建议使用 package com.mashibing.juc.c_001_00_thread_end; import com.mashibing.util.SleepHelper; public class T01_Stop { public static void main(String[] args) { Thread t = new Thread(() -> { while (tr.

java_study_的博客 184

ET2012新版免狗免安装绿色软件

ET2012新版免狗免安祖昂绿色软件。,新界面,新视觉,

多线程的基础知识以及应用

什么是多线程、创建线程的三种方式、守护线程、线程的生命周期、线程的安全问题、线程池的使用

m0_68201836的博客 756

jre9下载 jdk9

(Java SE Runtime Environment 9.0) 9.0u175 官方最新版 【x86|x64】

jdk-8u144-linux-x64.tar.gz.

该资源为搭建Linux系统Java环境用,资源的后缀为tar.gz格式的压缩包

Android多线程:你必须要了解的多线程基础知识汇总

前言 多线程的应用在开发中非常常见 今天,我将献上一份全面 & 详细的多线程基础知识汇总,希望你们喜欢。 目录 1. 线程 - 介绍 1.1 定义 一个基本的CPU执行单元 & 程序执行流的最小单元 比进程更小的可独立运行的基本单位,可理解为:轻量级进程 组成:线程ID + 程序计数器 + 寄存器集合 + 堆栈 注:线程自己不拥有系统资源,与其他线程共享进程所拥有的全...

Carson带你学Android 5256

Probability and Statistics - 4th edition

Probability and Statistics - 4th edition

Java多线程基础知识总结

<-----------------------------------------廖雪峰学Java-----------------------------------------> 目录1. 多线程基础知识2. 创建多线程3. 线程状态4. 中断线程5. 守护线程6. 线程同步7. 同步方法8. 死锁9. 使用wait和notify10. 使用ReentrantLock11. 使用Condition12. 使用ReadWriteLock13. 使用StampedLock14. 使用Concu

Pinkgranite86的博客 2721

Java多线程:多线程基础知识

Java多线程:多线程基础知识 转载:https://mp.weixin.qq.com/s/eL401F9aOUIbBaT6tOhWPg 转载理由:看看理论

Object的博客 142

多线程的一些基础知识

前言 本文中包括多线程的一些基础知识,参考了一些文档和书籍,自己总结出来的常见的问题,以及其他优秀博文的链接。 1.线程的6个状态 https://blog.youkuaiyun.com/xiamiflying/article/details/82903361 1.1 初始态( NEW) 创建一个 Thread对象,但还未调用start()启动线程时,线程处于初始态。运行态( RUNNABLE),在Java中...

qq_34408516的博客 279

Java多线程基础知识

这是我再次学习多线程知识的一个总结,对于刚刚接触的学习者是比较友好易懂的,便于快速的理解和掌握。

gldzzznb的博客 3544

多线程的基础知识(必看)

多线程的基础知识 目录 什么是多线程? 为什么要用多线程? 什么场景会用到多线程? 多线程的局限性 什么是多线程? 多线程指的是在一个进程中运行多个线程。如果一个程序允许运行多个线程,那么它就是多线程程序。 例子1: 房子:进程 房子里的人:线程 客厅:公共空间(共享内存) 独立房间:线程的独享空间(独享内容) 花园浇花:线程的合作 例子2: 大火锅一个人吃,单进程单线程。 大火锅...

qq_41998273的博客 572

多线程的基础知识

多线程编程基础知识 当前流行的Windows操作系统能同时运行几个程序(独立运行的程序又称之为进程),对于同一个程序,它又可以分成若干个独立的执行流,我们称之为线程,线程提供了多任务处理的能力。用进程和线程的观点来研究软件是当今普遍采用的方法,进程和线程的概念的出现,对提高软件的并行性有着重要的意义。现在的大型应用软件无一不是多线程多任务处理,单线程的软件是不可想象的。因此掌握多线...

anxiaotao5180的博客 151

多线程基础知识

2.锁的释放上, synchronized - 获取锁的线程执行完同步代码,则直接释放锁 - 线程执行同步代码时发生异常,则JVM会让线程释放锁 Lock锁 - 在finally中调用unlock()进行释放锁,确保加锁次数和释放次数一样,不然很容易造成线程死锁。3.锁的获取上, synchronized 获取锁,线程A持有对象锁,线程B在等待对象锁, 此时线程A执行任务出现sleep阻塞不释放对象锁,线程B则会一直进入等待状态;5.锁的类型上, synchronized 可重入锁、不可中断锁、非公平锁;

m0_74419145的博客 899

C#中的多线程 - 基础知识

来源: C#中的多线程 - 基础知识 简介及概念 C# 支持通过多线程并行执行代码,线程有其独立的执行路径,能够与其它线程同时执行。 一个 C# 客户端程序(Console 命令行、WPF 以及 Windows Forms)开始于一个单线程,这个线程(也称为“主线程”)是由 CLR 和操作系统自动创建的,并且也可以再创建其它线程。以下是一个简单的使用多线程的例子: 所有示例都假...

weixin_30515513的博客 128

Java多线程--基础知识

本文介绍Java多线程的基础知识。包括:多线程同步的实现方式,死锁产生的条件,如何避免死锁。 多线程同步的方案 方式 优点 缺点 使用场景 synchronized 使用简单;CPU占用低 响应缓慢(并发性能差);不灵活。 并发量低;大的代码块 同步方法/代码块 并发集合(List,Map) 例:ConcurrentHashMap等 并发性能高 ...

IT利刃出鞘的博客 3845

多线程编程基础知识

多线程编程基础知识 当前流行的Windows操作系统能同时运行几个程序(独立运行的程序又称之为进程),对于同一个程序,它又可以分成若干个独立的执行流,我们称之为线程,线程提供了多任务处理的能力。用进程和线程的观点来研究软件是当今普遍采用的方法,进程和线程的概念的出现,对提高软件的并行性有着重要的意义。现在的大型应用软件无一不是多线程多任务处理,单线程的软件是不可想象的。因此掌握多线程多任务设

qq1041256623的专栏 525
上一篇: JVM---垃圾回收器CMS 和G1解析
下一篇: 多线程---详解各种锁和AQS原理
意田天
博客等级 码龄7年 41粉丝 89原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

意田天

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值