java并发编程实战wwj----------------------第二阶段--------------04-05--06--07-8-9-10-11

代码:

   wait线程放弃了cpu的执行权进入wait状态。

   

Optional.of:https://www.cnblogs.com/baidawei/p/9443402.html

看下这个英文的解释。

这个是一个虚的概念,因为你在它里面拿不出任何的东西,但是它确实是去存放我们的线程的。

线程里面执行了wait之后会把自己放在锁住得对象的wait set里面去的。任何一个对象都有一个

wait set。

public class WaitSet {
    private static final Object LOCK = new Object();

    public static void main(String[] args) {
        IntStream.rangeClosed(1, 10).forEach(i -> {
            new Thread(String.valueOf(i)) {
                public void run() {
                    synchronized (LOCK){
                        try {
                            Optional.of(Thread.currentThread().getName()+" will be to wait set").ifPresent(System.out::println);
                            LOCK.wait();
                            Optional.of(Thread.currentThread().getName()+" will leave to wait set").ifPresent(System.out::println);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }.start();
        });

        try {
            Thread.sleep(3_000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        IntStream.rangeClosed(1, 10).forEach(i -> {
            synchronized (LOCK){
                LOCK.notify();
                try {
                    Thread.sleep(1_000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });

    }
}

wait和notify都是lock对象去完成的。和线程本身是没有关系的。

---

代码:

关键的总结:

我再次起来会拿到begin......吗?

public class WaitSet2 {
    private static final Object LOCK = new Object();

    public static void work() {
        synchronized (LOCK) {
            System.out.println("begin......");
            try {
                System.out.println("Thread will comming......");
                LOCK.wait();
            } catch (Exception e) {
                e.printStackTrace();
            }
            System.out.println("Thread will end......");
        }
    }


    public static void main(String[] args) throws InterruptedException {
        new Thread(){
            @Override
            public void run() {
                work();
            }
        }.start();
        Thread.sleep(1_000);
        synchronized (LOCK){
            LOCK.notify();
        }
    }
}

有地址恢复顺着执行的单元去执行。

再次抢到锁的话是执行的end

--------------------------------------------------04-----------------------------------------------------------------------

volatile:

代码:

public class VolatileTest {
//    private volatile static int INIT_VALUE = 0;
//    private static int INIT_VALUE = 0;
    private final static int MAX_LIMIT = 5;

    public static void main(String[] args) {
        new Thread(() -> {
            int localValue = INIT_VALUE;
            while (localValue < MAX_LIMIT) {
                if (localValue != INIT_VALUE) {
                    System.out.println("The value READER to" + INIT_VALUE);
                    localValue = INIT_VALUE;
                }
            }
        }, "READER").start();
        new Thread(() -> {
            int localValue = INIT_VALUE;
            while (localValue < MAX_LIMIT) {
                System.out.println("The value UPDATE to" + (++localValue));
                INIT_VALUE = localValue;
                try {
                    Thread.sleep(5_00);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "UPDATE").start();
    }
}

不加volatile打印:

线程的可见行:https://blog.csdn.net/dyllove98/article/details/8580734

这个就是不主动去主存里面去刷新缓存的。看前面的链接有讲的。

-----------------------------------------05------------------------------------------------

线程的基本特性:原子性 有序性 可见行。

volatile不能保证原子性的。

volatile:内存可见性(读一定再写完之后的) 有序性。

内存RAM,计算是放在CPU的寄存器的。

 cpu--------cpu的cache-------ROM

cpu快,但是RAM慢,所以则加个缓冲区就是内存条。

cpu1和cpu2指的是不同的cpu的核。

JMM模型:https://blog.csdn.net/lxm55913153/article/details/79208126

每个线程都是修改自己本地的缓存的数据再更新到主存。

public class VolatileTest2 {
    //private volatile static int INIT_VALUE = 0;
    private static int INIT_VALUE = 0;
    private final static int MAX_LIMIT = 50;
    public static void main(String[] args) {
        new Thread(() -> {
            while (INIT_VALUE < MAX_LIMIT) {
                System.out.println("T1---" + (++INIT_VALUE));
                try {
                    Thread.sleep(1_00);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "ADDER-1").start();

        new Thread(() -> {
            while (INIT_VALUE < MAX_LIMIT) {
                System.out.println("T2---" + (++INIT_VALUE));
                try {
                    Thread.sleep(1_00);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "ADDER-2").start();
    }
}

打印:

注意和第一个的区别。

++会触发去主内存拿去刷新,但是刷新到主存可能是不及时的。

 new Thread(() -> {
            int localValue = INIT_VALUE;
            while (localValue < MAX_LIMIT) {
                //System.out.println(INIT_VALUE+" "+localValue);
                if (localValue != INIT_VALUE) {
                    System.out.println(INIT_VALUE+" "+localValue);
                    System.out.println("The value1 READER to" + INIT_VALUE);
                    localValue = INIT_VALUE;
                }
            }
        }, "READER").start();

因为在这个线程里每次都是读,没有感知到写就不去主内存拿值。

当前线程有写才会感知。在线程没有写的操作。不需要再主内存去拿。

----------------------------------------------06---------------------------------------------------

内存cache出现会加快访问的速度。

加锁的解决效率是很底的。

1.给数据总线加锁。总线(数据总线 地址总线 控制总线),加锁就是不能访问总线的加锁地址。

2.cpu高速缓存一致性协议,Internet,MESI。保证缓存的副本是一样的。

核心思想:cpu写入数据发现该变量被内存共享,就发出一个信号通知其他的cpu该变量的缓存是无效的(这个过程是要加锁的)。

当其他cpu访问该变量重新到主存获取。

-----------------------------------------------07---------------------------------------------------------

并发的三个比较重要的概念:

原子性:https://www.cnblogs.com/deepalley/p/10795389.html

有序性:

不一定是什么顺序的。

1和2可能重排序的。

中间不一定是怎么执行的,保证的是最终的一致性。

重排序的概念:重排序是单线程只要求最终的一致性。

可见性:多个线程访问这个变量一个线程访问,其他的线程可以看见。

 

线程1改变了还没刷新到主存 线程2就读取了。

---

补充一个特性|一致性:https://www.cnblogs.com/stone94/p/10409669.html

--------------------------------------------------08------------------------------------------------------

1.原子性:对基本数据类型的读取和赋值事保证了原子性的,要么成功,要么失败。不可被中断。

2.可见性:使用volatile保证可见性。

3.有序性:重排序满足最终一致性,再在单线程是没问题的。

happends before关系:

  • 在一个线程里面比如在run方法里面,代码的执行顺序,编写在前面的发生在编写在后面的,这个指的是逻辑前后。
  • 锁unlock必须发生在lock之后。
  • volatile修饰的变量,对一个变量的写操作必须发生在对变量的读操作之前
  • 传递规则。
  • -----------------------------------以下事废话
  • 线程的启动规则,start方法肯定先于run方法。
  • 线程的中断规则,中断方法先于发生捕获动作之前。
  • 对象的销毁规则,对象的初始化必须发生在finalize之前。
  • 所有的操作发生在线程的死亡之前。

-----------------------------------------------09------------------------------------------------------

代码:

就是volatile之前的代码一定发生在volatie代码之后的代码。同时volatile之前的代码不能拿到volatile之后。

volatile修饰的变量在读之前已经写完了。

原子性举例:

可能会有问题,可能没有问题。sleep时间小一点就有问题了。

---

这样拆是错的

有序性举例:这里第二个步骤必须放在第一个步骤之后禁止重排序的。

volatile在汇编加lock前缀相当于内存屏障。

------------------------------------------10------------------------------------------------------

使用场景:

1.

2.线程屏障前后的一致性

 

总结:

   

8.使用场景。

---------------------------------------11------------------------------------------------------

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值