Java并发编程

1.进程与线程/并行与并发

进程:就是一个正在运行的程序
线程:就是进程内的多条执行路径,一个进程内有多个线程。
并行:多核cpu下,每个核心都可以运行线程。同一时间动手做多件事情的能力。
并发:线程轮流使用cpu,同一时间应对多件事情的能力。

  • 同步:需要等待结果返回才能继续向下运行

  • 异步:不需要等待结果返回,就能继续向下运行。

2.创建线程

//继承Thread类,匿名内部类的写法
Thread t = new Thread("t1") {
   @Override
    public void run() {
        log.info("666");
    }
};
t.start();
//实现Runnable接口,lambda表达式的写法
new Thread(()->{
	System.out.println("666");
},"t2").start();
//FutureTask异步任务,lambda表达式的写法,Callable函数式接口,有返回值和异常抛出。
FutureTask<Integer> task = new FutureTask<Integer>(()->{
  	return 100;
});
new Thread(task,"t3").start();

3、栈与栈帧

每个线程启动,虚拟机会为其分配线程工作栈内存。每个工作栈内存由多个栈帧组成,对应着每次方法调用时所占用的内存。每个线程只有一个活动栈帧,对应着当前正在执行的方法。
在这里插入图片描述

4、sleep

  • 调用sleep方法会让当前线程从RUNNABLE运行状态变为TIMED_WAITING阻塞状态。
  • 其他线程可以调用interrupt()方法,打断正在睡眠的线程,这时sleep方法会抛出InterruptedException。睡眠的线程将被唤醒。
  • 但是睡眠结束后的线程未必会立即得到执行,而是进入RUNNABLE就绪状态,等待CPU的调度执行。
  • TimeUnit替代sleep得到更好的可读性。
TimeUnit.MILLISECONDS.sleep(500);
  • sleep防止cpu占用100%,让出cpu的执行权,不让死循环占用太多资源。
public class CpuSleepTest {
    public static void main(String[] args) throws InterruptedException {
        while (true){
            Thread.sleep(1);
            System.out.println(new Date());
        }
    }
}

5、yield

  • 礼让,会让当前线程从运行状态变为就绪状态,等待CPU的调度执行,又有可能获得CPU的执行权。
  • 具体的实现依赖于操作系统的任务调度器。

6、PRIORITY线程优先级

  • 优先级会提示调度器优先执行该线程,但仅仅只是一个提示,调度器可能忽略这个提示。
  • 如果CPU比较忙,优先级较高的线程会获得更多的时间片,CPU空闲时,线程的优先级几乎没有作用。
    /**
     * The minimum priority that a thread can have.
     */
    public final static int MIN_PRIORITY = 1;

   /**
     * The default priority that is assigned to a thread.
     */
    public final static int NORM_PRIORITY = 5;

    /**
     * The maximum priority that a thread can have.
     */
    public final static int MAX_PRIORITY = 10;
public class YieldPriorotyTest {
    public static void main(String[] args) {

        Thread t1 = new Thread(() -> {
            int count = 0;
            for (; ; ) {
                System.out.println("t1--"+count++);
            }
        });

        Thread t2 = new Thread(() -> {
            int count = 0;
            for (; ; ) {
                //Thread.yield();//礼让
                System.out.println("        t2--"+count++);
            }
        });

        t2.start();
        t1.start();

        //设置线程优先级
        t2.setPriority(Thread.MAX_PRIORITY);
        t1.setPriority(Thread.MIN_PRIORITY);

    }
}

7、join插队

等待调用join()方法的线程结束

package new2023.juc;

import java.util.concurrent.TimeUnit;

/**
 * @Author zhangxuhui
 * @Date 2023/3/17
 * @email zxh_1633@163.com
 */
public class JoinTest {
    static int s = 0;
    public static void main(String[] args) throws InterruptedException {

        Thread t = new Thread(()->{
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            s = 10;
            System.out.println("t is over");
        },"t");

        t.start();
        t.join();//插队,等待t线程结束。
        System.out.println(s);
    }
}

8、inturrupt打断线程

  • 打断阻塞状态的线程(sleep,wait,join),会重置打断标记。
package new2023.juc;

/**
 * @Author zhangxuhui
 * @Date 2023/3/17
 * @email zxh_1633@163.com
 */
public class InterruptTest {
    public static void main(String[] args) throws InterruptedException {
        Thread t = new Thread(()->{
            System.out.println("sleep....");
            try {
                Thread.sleep(4000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("thread is over");
        });

        t.start();
        Thread.sleep(1000);
        System.out.println("interrupt...");
        t.interrupt();
        System.out.println("打断标记:"+t.isInterrupted());//false

    }
}

  • 打断正常运行的线程,判断打断标识可以优雅的停止线程。
package new2023.juc;

import java.util.concurrent.TimeUnit;

/**
 * @Author zhangxuhui
 * @Date 2023/3/17
 * @email zxh_1633@163.com
 */
public class InterruptRunningThread {
    public static void main(String[] args) throws InterruptedException {
        Thread t = new Thread(()->{
           while(true){
               boolean interrupted = Thread.currentThread().isInterrupted();
               System.out.println(interrupted);
               if(interrupted){
                   System.out.println("thread is inturrupted : stop");
                   break;
               }
           }
        });

        t.start();

        TimeUnit.SECONDS.sleep(1);

        t.interrupt();

        System.out.println("main thread is stop");
    }
}

  • 两阶段终止模式(two phase termination):在一个线程t1中如何优雅的终止另一个线程t2,优雅是指给t2一个料理后事的机会。
    在这里插入图片描述
package new2023.juc;

import java.util.concurrent.TimeUnit;

/**
 * @Author zhangxuhui
 * @Date 2023/3/17
 * @email zxh_1633@163.com
 */
public class TwoPhaseTermination {
    public static void main(String[] args) throws InterruptedException {
        Phase p = new Phase();
        p.start();

        TimeUnit.SECONDS.sleep(3);
        p.stop();
        System.out.println("等待重启...");
        TimeUnit.SECONDS.sleep(5);
        p.restart();
    }
}

class Phase{

    private Thread monitor;

    public void start(){
        monitor =  new Thread(()->{
            while (true){
                Thread current = Thread.currentThread();
                //current.isInterrupted();不会清除打断标记
                //Thread.interrupted();会清除打断标记
                if(current.isInterrupted()){
                    System.out.println("线程被打断,料理后事");
                    break;
                }

                try {
                    Thread.sleep(1000);
                    System.out.println("系统监控");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                    current.interrupt();
                }

            }
        });
        monitor.start();
    }

    public void stop(){
        monitor.interrupt();
    }

    public void restart(){
        start();
    }
}

  • 打断park线程,使其继续执行,在打断标记为true时,park将失效。
package new2023.juc;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.LockSupport;

/**
 * @Author zhangxuhui
 * @Date 2023/3/18
 * @email zxh_1633@163.com
 */
public class InterruptPark {
    public static void main(String[] args) throws InterruptedException {

        Thread t = new Thread(()->{
            System.out.println("park");
            LockSupport.park();
            System.out.println("unpark");
//            System.out.println(Thread.currentThread().isInterrupted());
            System.out.println(Thread.interrupted());//获取打断标记并清除
            System.out.println("park two");
            LockSupport.park();//打断标记为true时失效

        },"t");

        t.start();
        TimeUnit.SECONDS.sleep(2);
        t.interrupt();
    }
}

9、守护线程

默认情况下,Java进程需要等待所有的线程结束才会结束。有一种特殊的线程叫守护线程,只要其他非守护线程运行结束了,即使守护线程代码没有运行结束也会强行结束。垃圾回收器线程就是守护线程。

package new2023.juc;

import java.util.concurrent.TimeUnit;

/**
 * @Author zhangxuhui
 * @Date 2023/3/18
 * @email zxh_1633@163.com
 */
public class SetThreadDeamon {
    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(() -> {
            while (true){
                System.out.println("running...");
            }
        });
        thread.setDaemon(true);
        thread.start();
        TimeUnit.MILLISECONDS.sleep(10);
        System.out.println("thread main over");
    }
}

10、线程的状态

操作系统层面的五种状态:

在这里插入图片描述

  • 初始状态(新建状态):即线程被创建new Thread()。
  • 可运行状态(就绪状态):线程被启动,但是未得到CPU的时间片,没有执行。
  • 运行状态:线程得到了CPU的时间片,线程正在执行。
  • 阻塞状态:调用阻塞api例如:sleep,wait,线程进入阻塞状态。
  • 终止状态:线程运行结束,生命周期结束。

java层面的六种状态:

在这里插入图片描述

11、线程应用统筹规划

在这里插入图片描述

  • 解法一:join插队
package new2023.juc;

/**
 * @Author zhangxuhui
 * @Date 2023/3/18
 * @email zxh_1633@163.com
 */
public class TeaThread {
    public static void main(String[] args) {
        Thread t1 = new Thread(()->{
            try {
                Thread.sleep(1000);
                System.out.println("小王 洗水壶");

                Thread.sleep(5000);
                System.out.println("小王 烧开水");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        },"小王");


        Thread t2 = new Thread(()->{
            try {
                Thread.sleep(1000);
                System.out.println("大王 洗茶壶");

                Thread.sleep(1000);
                System.out.println("大王 洗茶杯");

                Thread.sleep(1000);
                System.out.println("大王 拿茶叶");

                t1.join();
                System.out.println("大王 泡茶");

            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        },"大王");

        t1.start();
        t2.start();
    }
}

  • 解法二:谁泡茶都可以?怎么实现?
  • 解法三:小王工作完将开水交给大王,怎么实现?

12 、多线程问题

临界区:代码块内存在对共享资源的多线程读写。

  • 多线程读共享资源,其实没有问题。
  • 多线程对共享资源的读写时发生指令交错,就会出现问题。

竞态条件:多线程在临界区内执行,由于代码的执行序列不同而导致结果无法预测,称之为发生了竞态条件。

13、 同步解决方案

  • 阻塞式的解决方案:synchronized/lock
  • 非阻塞式的解决方案:原子变量

synchronized

采用的互斥的方式让同一时刻,至多只有一个线程能持有对象锁,其他线程再想获取这个对象锁时就会阻塞住,这样就能保证拥有锁的线程可以安全的执行临界区内的代码,而不用担心线程的上下文切换导致线程同步问题的出现。

package new2023.juc;

/**
 * @Author zhangxuhui
 * @Date 2023/3/19
 * @email zxh_1633@163.com
 */
public class synchronizedTest {

     static int count=0;
    public static void main(String[] args)throws Exception {
        Object lock = new Object();

        Thread t1 = new Thread(() -> {
            for (int i = 0 ; i < 5000 ;i++){
                synchronized(lock){
                    count++;
                }
            }
        });

        Thread t2 = new Thread(() -> {
            for (int i = 0 ; i < 5000 ;i++){
                synchronized (lock) {
                    count--;
                }
            }
        });

        t1.start();
        t2.start();

        t1.join();
        t2.join();
        System.out.println(count);
    }
}

  • 面向对象改造
package new2023.juc;

/**
 * @Author zhangxuhui
 * @Date 2023/3/19
 * @email zxh_1633@163.com
 */
public class OOMSynchronized {
    public static void main(String[] args) throws  Exception{
        Room room = new Room();

        Thread t1 = new Thread(() -> {
            for (int i = 0 ; i < 5000 ;i++){
                room.incr();
            }
        });

        Thread t2 = new Thread(() -> {
            for (int i = 0 ; i < 5000 ;i++){
                room.decr();
            }
        });

        t1.start();
        t2.start();

        t1.join();
        t2.join();
        System.out.println(room.getCount());
    }
}

class Room{
    private int count = 0;

    public void incr(){
        synchronized(this){
            count++;
        }
    }

    public void decr(){
        synchronized (this){
            count--;
        }
    }

    public int getCount(){
        synchronized (this){
            return count;
        }
    }
}

  • 对象锁
class test{
    public void t1(){
        synchronized (this){
            
        }
    }
    //等价
    public synchronized  void t2(){
        
    }
}
  • 类锁
class test2{
    public synchronized static void t(){

    }
	//等价
    public void t2(){
        synchronized (test2.class){
            
        }
    }
}
  • 成员变量多线程读写存在安全问题,局部变量为引用类型时,也可能存在线程安全问题。
  • 父类为了不让子类重写线程安全的方法,而造成线程安全问题,将方法声明为priavet或final类型,不让子类重写。
  • 常见线程安全的类的方法是线程安全的,但是这些方法组合起来并不一定是线程安全的。
  • 没有成员变量的类是线程安全的
  • 转账练习
package new2023.juc;

/**
 * @Author zhangxuhui
 * @Date 2023/3/21
 * @email zxh_1633@163.com
 */
public class AcountTest {
    public static void main(String[] args) throws InterruptedException {
        Counter a = new Counter(1000);
        Counter b = new Counter(1000);

        Thread t1 = new Thread(()->{
            for (int i = 0; i < 1000; i++) {
                a.transfer(b,1);
            }
        });

        Thread t2 = new Thread(()->{
            for (int i = 0; i < 1000; i++) {
                b.transfer(a,1);
            }
        });

        t1.start();
        t2.start();

        t1.join();
        t2.join();

        System.out.println(a.getCount()+ b.getCount());
    }
}

class Counter{
    private int count;

    Counter(int count){
        this.count=count;
    }

    public int getCount(){
        return this.count;
    }

    public void setCount(int count){
        this.count = count;
    }

    public void transfer(Counter target,int money){
        //类锁,因为this和target为不同的对象,不能用对象锁。
        //他们共同的对象是Counter.class类对象。
        synchronized (Counter.class) {
            if (this.count >= money) {
                this.setCount(this.count - money);
                target.setCount(target.count + money);
            }
        }
    }
}

14 、Monitor监视器/管程

  • 对象头
    在这里插入图片描述
    在这里插入图片描述
  • 每个Java对象都可以关联一个Monitor对象(由操作系统提供),如果使用synchronized(重量级锁)给对象上锁后,该对象的对象头中的mark word就被设置为执行Monitor对象的指针。
    在这里插入图片描述
  • 刚开始monitor中的owner为null
  • 当thread-2执行零界区代码时,将obj对象的对象头的mark word设置为执行Monitor对象的指针。monitor的owner设置指向thread-2。
  • 在thread-2上锁期间,其他线程执行零界区代码,将进入entry list阻塞队列。
  • thread-2执行完同步代码后,不再指向owner,并且唤醒阻塞的线程,重新竞争锁执行,这种锁竞争是非公平的。
  • wait set中的线程是之前获得过锁,但条件不满足进入waitting状态的线程。
    在这里插入图片描述
    注意:只要在同步代码块内,都会正常的加锁及解锁,不会出现死锁的现象。当同步代码块中出现异常时将自动解锁。
    在这里插入图片描述

15、synchronized原理

  • 轻量级锁(lock record)
    如果一个对象虽然有多线程访问,但多线程访问的时间的错开的(没有竞争),那么可以使用轻量级锁进行优化。轻量级锁对使用者是透明的,语法仍然是synchronized。
  • 锁膨胀
    如果在尝试加轻量级锁的过程中,CAS操作无法成功,这是一种情况就是有其他线程为此对象加上了轻量级锁(锁竞争),这时需要进行锁膨胀,将轻量级锁升级为重量级锁。
  • 自旋优化
    重量级锁竞争的时候,如果当前线程自旋成功(即持锁线程已经退出了同步块,释放了锁。),这时当前线程就可以避免阻塞。
  • 偏向锁
    轻量级锁在没有竞争时(就自己单个线程),每次重入都需执行CAS操作。1.6加入偏向锁进行优化,只有第一次加锁时进行CAS,后续重入锁无需CAS操作。将线程id存放在对象头中,加锁时先判断对象头中的线程是不是自己。

16、wait-notify

在这里插入图片描述

  • wait 让线程等待
  • notify 唤醒wait set中的其中一个
  • notifyAll 唤醒wait set 中的全部
    都是object对象中的方法,必须获得对象的锁才能调用这些方法。即必须在同步方法或代码块中才能调用。
    在这里插入图片描述
    线程内不能使用if判断一次,应该使用while进行循环判断。
    在这里插入图片描述

17、保护性暂停

有一个结果需要从一个线程传递到另一个线程,让他们关联同一个guardedobject。jdk中join,future的实现就是采用此模式。
在这里插入图片描述

package new2023.保护性暂停;

/**
 * @Author zhangxuhui
 * @Date 2023/4/5
 * @email zxh_1633@163.com
 * 线程传递结果的中间对象
 */
public class GuardeObject {

    private Object response;

    //get response by timeOut
    public Object get(long timeOut){
        synchronized (this){
            long start = System.currentTimeMillis();
            long con = 0;
            while (response == null){
                long waitTime = timeOut - con;
                if (waitTime <= 0){
                    break;
                }
                try {
                    this.wait(waitTime);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                con = System.currentTimeMillis() - start;
            }
            return response;
        }
    }

    //get response
    public Object get(){
        synchronized (this){
            while (response == null){
                try {
                    this.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            return response;
        }
    }


    //set response
    public void complete(Object o){
        synchronized (this){
            this.response = o;
            this.notifyAll();
        }
    }

}

注意:使用第三个类futures进行消息的生产者和消息的消费者之间的解耦,futures类包含多个消息处理的类,使用id进行一一对应,使用后注意将消息处理的类进行销毁,防止内存溢出。

在这里插入图片描述

18、生产者消费者模式

在这里插入图片描述

  • 消费队列可以平衡消费和生产的线程资源
  • 生产者只产生消息,消费者只消费消息,二者解耦互不干扰。
  • 消息队列的容量时有限的,满时不会再添加数据,空时不会再消费数据。
  • JDK中的阻塞队列就是依据此模式实现
package new2023.生产者消费者模式;

import java.util.LinkedList;

/**
 * @Author zhangxuhui
 * @Date 2023/4/5
 * @email zxh_1633@163.com
 * 消息队列
 */
public class MessageQuene {

    private LinkedList<Message> list = new LinkedList<>();
    private int cap;

    public MessageQuene(int cap) {
        this.cap = cap;
    }

    public Message take(){
        synchronized (list){
            while (list.isEmpty()){
                try {
                    System.out.println(Thread.currentThread().getName()+"队列为空,没有消息。");
                    list.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            Message last = list.removeLast();
            System.out.println(Thread.currentThread().getName()+"消费消息:"+last);
            list.notifyAll();
            return last;
        }
    }


    public void put(Message msg){
        synchronized (list){
            while (list.size() == cap){
                try {
                    System.out.println(Thread.currentThread().getName()+"队列已满");
                    list.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            list.addFirst(msg);
            System.out.println(Thread.currentThread().getName()+"生产消息:"+msg);
            list.notifyAll();
        }
    }
}


package new2023.生产者消费者模式;

/**
 * @Author zhangxuhui
 * @Date 2023/4/5
 * @email zxh_1633@163.com
 */
public class TestMessage {
    public static void main(String[] args) {
        MessageQuene quene = new MessageQuene(2);

        for (int i = 0; i < 3; i++) {
            int id = i;
            new Thread(()->{
                quene.put(new Message(id,"msg"+id));
            }).start();
        }

        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        new Thread(()->{
            for (;;){
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                quene.take();
            }
        }).start();
    }
}

19、park/unpark

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

原理

  • 每个线程都关联一个Parker对象,由三部分组成_counter,_cond,_mutex.
  • park检查通行证,unpark发放一张通行证。
  • 先park后unpark
    在这里插入图片描述
    在这里插入图片描述
  • 先unpark后park
    在这里插入图片描述

20、多把锁活跃性

  • 死锁:一个线程需要同时获取多把锁,这时就容易发生死锁。使用jstack或jconsole工具进行死锁检测。
  • 活锁:两个线程相互改变对方的结束条件,导致两个线程都无法结束。
  • 饥饿:一个线程由于优先级太低,始终得不到cpu调度执行,也不能结束。
package new2023.juc;

/**
 * @Author zhangxuhui
 * @Date 2023/4/6
 * @email zxh_1633@163.com
 * 实际开发中应避免死锁的产生
 */
public class ThreadDeadLock {
    public static void main(String[] args) {
        Object locka = new Object();
        Object lockb = new Object();

        new Thread(()->{
            synchronized (locka){
                System.out.println("locka--" + Thread.currentThread().getName());
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (lockb){
                    System.out.println("lockb--"+Thread.currentThread().getName());
                }
            }
        },"t1").start();

        new Thread(()->{
            synchronized (lockb){
                System.out.println("lockb--" + Thread.currentThread().getName());
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (locka){
                    System.out.println("locka--"+Thread.currentThread().getName());
                }
            }
        },"t2").start();
    }
}

21、可重入锁ReentrantLock

相对于synchronized对比具有以下特点:

  • 可中断 lockInterruptibly()
  • 可是设置超时时间 tryLock()
  • 可以设置为公平锁,默认为不公平锁。
  • 支持多个条件变量
  • 同synchronized一样支持可重入

可重入:同一个线程如果首次获得了这把锁,就有权利再次获得这把锁,如果不可重入则自己也会被锁住,导致程序无法继续执行。

package new2023.juc;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

/**
 * @Author zhangxuhui
 * @Date 2023/4/7
 * @email zxh_1633@163.com
 * await前先获得锁
 * await执行后释放锁,进入condition对象等待。
 * await被唤醒/打断/超时后会重新竞争锁
 * 竞争锁成功后将继续执行
 */
public class ReentantLockCondationTest {
    static ReentrantLock lock = new ReentrantLock();
    static Condition room = lock.newCondition();

    public static void main(String[] args) {
        Thread t1 = new Thread(() -> {
            lock.lock();
            try {
                System.out.println("go into room");
                try {
                    room.await();
                    System.out.println("run..");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }finally {
                lock.unlock();
            }

        }, "t1");

        t1.start();

        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        lock.lock();
        try {
            System.out.println("唤醒。。。");
            room.signal();
        }finally {
            lock.unlock();
        }

    }
}

22、线程顺序控制

package new2023.juc;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

/**
 * @Author zhangxuhui
 * @Date 2023/4/8
 * @email zxh_1633@163.com
 */
public class ControlPrintNumReentrantLock {
    public static void main(String[] args) throws InterruptedException {
        awaitSinglePrint asp = new awaitSinglePrint();
        Condition a = asp.newCondition();
        Condition b = asp.newCondition();
        Condition c = asp.newCondition();
        new Thread(()->{
            asp.print("a",a,b);
        }).start();
        new Thread(()->{
            asp.print("b",b,c);
        }).start();
        new Thread(()->{
            asp.print("c",c,a);
        }).start();

        Thread.sleep(1000);
        asp.lock();
        try {
            a.signal();
        }finally {
            asp.unlock();
        }
    }
}

class awaitSinglePrint extends ReentrantLock{
    private int loopNum = 5;

    public void print(String msg,Condition current,Condition next){
        lock();
        try {
            for (int i = 0; i < loopNum; i++) {
                try {
                    current.await();
                    System.out.print(msg);
                    next.signal();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }finally {
            unlock();
        }
    }
}

22、Java内存模型

在这里插入图片描述

  • volatile易变关键字:可以用来修饰成员变量,避免线程在自己的工作内存中获取变量的值,直接操作主内存中的变量。synchronized代码块即可以保证原子性又可以保证可见性,但是性能较低。
  • balking犹豫模式,一个线程发现另一个线程或本线程已经做了某件事情,将不再继续直接结束线程返回。
  • JVM会在不影响执行结果的前提下,优化代码的执行顺序。
package new2023.内存模型;

/**
 * @Author zhangxuhui
 * @Date 2023/4/8
 * @email zxh_1633@163.com
 */
public class BlakingTest {

    static volatile boolean flag = false;
    public static void main(String[] args) {
        Thread thread = new Thread(() -> {
            synchronized (BlakingTest.class) {
                if (flag) {
                    System.out.println("stop");
                    return;
                }
                flag = true;
                System.out.println("start");
            }
        });


        flag = true;
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        thread.start();
    }
}

volatile原理:不能保证原子性,可见性及有序性只在单个线程内,多个线程中共享变量的读写由cpu的调度决定,不能保证读到的数据就是最新的数据。

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

23、DCL(双检锁)

package new2023.内存模型;

/**
 * @Author zhangxuhui
 * @Date 2023/4/8
 * @email zxh_1633@163.com
 */
public class DoubleCheckingLock {
    public static void main(String[] args) {
        for (int x = 0 ; x < 1000; x++){
            new Thread(()->{
                System.out.println(DCL.getIns());
            }).start();
        }
    }
}

class DCL{
    private static volatile DCL ins = null;
    private DCL(){}
    public static DCL getIns(){
        if(ins == null){
            synchronized (DCL.class){
                if(ins == null){
                    ins = new DCL();
                }
            }
        }
        return ins;
    }

    @Override
    public String toString() {
        return "DCL " + this.hashCode();
    }
}

24、happens-before规则

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

25、CAS比较并交换

package juc;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * @Author zhangxuhui
 * @Date 2023/4/9
 * @email zxh_1633@163.com
 */
public class AtomicIntergerTest {
    public static void main(String[] args) {
        AtomicInteger ai = new AtomicInteger(100);
        for (int x = 0;x < 10;x++){
            new Thread(()->{
                while (true){
                    int i = ai.get();
                    int update = i - 10;
                    if(ai.compareAndSet(i,update)){
                        System.out.println(Thread.currentThread().getName()+"将:"+i+"更新为:"+update);
                        break;
                    }
                }
            }).start();
        }
        try {
            TimeUnit.SECONDS.sleep(1);
            System.out.println("最后结果为:"+ai.get());
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

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

  • 原子整型:AtomicInteger、AtomicLong、AtomicBoolean
  • 原子引用:AtomicReference、AtomicMarkableReference、AtomicReferenceFieldUpdater、AtomicStampedReference(存在aba问题)
  • 原子数组:AtomicIntegerArray\AtomicLongArray\AtomicReferenceArray
  • 字段更新器:AtomicReferenceFieldUpdater\AtomicIntegerFieldUpdater\AtomicLongFieldUpdater
  • 原子累加器:LongAdder\LongAccumulator\DoubleAdder\DoubleAccumulator,性能比原子整型好,因为会在竞争时设置多个累加单元,最后将结果汇总,减少CAS重试失败,从而提高性能。
package juc;

import java.math.BigDecimal;
import java.util.concurrent.atomic.AtomicReference;

/**
 * @Author zhangxuhui
 * @Date 2023/4/9
 * @email zxh_1633@163.com
 */
public class AtomicReferenceTest {
    public static void main(String[] args) {
        BigDecimal bigDecimal = new BigDecimal(100);
        AtomicReference<BigDecimal> arf = new AtomicReference(bigDecimal);
        System.out.println(arf.get());
        if(arf.compareAndSet(bigDecimal,BigDecimal.valueOf(90))){
            System.out.println(arf.get());
        }
    }
}

package juc;

import java.util.concurrent.atomic.AtomicMarkableReference;

/**
 * @Author zhangxuhui
 * @Date 2022/8/1
 * @email zxh_1633@163.com
 */
public class AtomicMarkableReferenceDemo {
    public static void main(String[] args) {
        //原子类+标记 默认为false,修改后设置为true。
        AtomicMarkableReference<Integer> ar = new AtomicMarkableReference<>(100,false);
        boolean marked = ar.isMarked();
        System.out.println(marked);
        ar.compareAndSet(100,200,marked,!marked);
        System.out.println(ar.isMarked());
        System.out.println(ar.getReference());
    }
}

package new2023.juc;

import java.util.Arrays;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicIntegerArray;

/**
 * @Author zhangxuhui
 * @Date 2023/4/9
 * @email zxh_1633@163.com
 */
public class AtomicArrayTest {
    public static void main(String[] args) {
//        AtomicReferenceArray
//        AtomicLongArray
//        AtomicIntegerArray
        int [] arr = new int[10];
        for (int x = 0 ; x < arr.length;x++){
            new Thread(()->{
                for (int y = 0 ; y < 10000;y++){
                    arr[y%arr.length] = arr[y%arr.length]+1;
                }
            }).start();

        }

        try {
            TimeUnit.SECONDS.sleep(5);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println(Arrays.toString(arr));

        AtomicIntegerArray ata = new AtomicIntegerArray(10);
        for (int x = 0 ; x < ata.length();x++){
            new Thread(()->{
                for (int y = 0 ; y < 10000;y++){
                    ata.getAndIncrement(y%ata.length());
                }
            }).start();

        }

        try {
            TimeUnit.SECONDS.sleep(5);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println(ata);
    }
}

package new2023.juc;

import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;

/**
 * @Author zhangxuhui
 * @Date 2023/4/9
 * @email zxh_1633@163.com
 * 原子字段更新器
 */
public class AtomicFileUpdateTest {
    public static void main(String[] args) {
//        AtomicReferenceFieldUpdater
//        AtomicIntegerFieldUpdater
//        AtomicLongFieldUpdater
        Student s = new Student();
        AtomicReferenceFieldUpdater arfu = AtomicReferenceFieldUpdater.newUpdater(Student.class,String.class,"name");
        arfu.compareAndSet(s,null,"zhangsan");
        System.out.println(s);
    }
}

class Student{
    volatile String name;

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                '}';
    }
}

package new2023.juc;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.LongAdder;

/**
 * @Author zhangxuhui
 * @Date 2023/4/9
 * @email zxh_1633@163.com
 */
public class AtomicNumberAdder {
    public static void main(String[] args) {
        LongAdder la = new LongAdder();

        CountDownLatch cdl = new CountDownLatch(10);

        for (int x = 0 ; x < 10;x++){
            new Thread(()->{
                for(int y = 0 ; y < 20000000;y++){
                   la.increment();
                }
                cdl.countDown();
            }).start();
        }
        try {
            cdl.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(la.intValue());
    }
}

package new2023.juc;

import sun.misc.Unsafe;
import java.lang.reflect.Field;
/**
 * @Author zhangxuhui
 * @Date 2023/4/9
 * @email zxh_1633@163.com
 */
public class UnsafeTest {
    public static void main(String[] args)throws Exception {
        Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
        theUnsafe.setAccessible(true);
        Unsafe unsafe = (Unsafe) theUnsafe.get(null);
        Person p = new Person();
        Field id = p.getClass().getDeclaredField("id");
        Field name = p.getClass().getDeclaredField("name");
        long idoffset = unsafe.objectFieldOffset(id);
        long nameoffest = unsafe.objectFieldOffset(name);
        unsafe.compareAndSwapInt(p,idoffset,0,1);
        unsafe.compareAndSwapObject(p,nameoffest,null,"bob");
        System.out.println(p);
    }
}

class Person{
    private volatile String name;
    private volatile int id;

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", id=" + id +
                '}';
    }
}

26、不可变对象

  • final的使用:修饰属性保证了该属性只能被读取,不能被修改;修饰类保证了该类不能被继承,方法不能被重写,防止子类无意间破坏不可变性。
  • 保护性拷贝:创建副本对象来避免共享的手段。
  • 享元模式:需要重用数量有限的同一类对像时。
  • 无状态:因为成员变量保存的数据也可以称为状态信息,因此没有成员变量称为无状态,没有成员变量的类线程安全。
//读取缓存-128 - 127
public static Integer valueOf(int i) {
       if (i >= IntegerCache.low && i <= IntegerCache.high)
           return IntegerCache.cache[i + (-IntegerCache.low)];
       return new Integer(i);
}
//String类,不可变类。
public final class String
    implements java.io.Serializable, Comparable<String>, CharSequence {
    /** The value is used for character storage. */
    private final char value[];

    /** Cache the hash code for the string */
    private int hash; // Default to 0
}
  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值