并发编程一(多线程基础)

1、多线程安全

一张票,还剩:99
窗口1卖了一张票,还剩:99
窗口2卖了一张票,还剩:97
窗口3卖了一张票,还剩:96
窗口1卖了一张票,还剩:97

package com.hsq.thread;

/**
 * 售票问题
 */
public class MyThead_安全 {
    static Integer piao = 100;
    static Object o = new Object();
    public static void main(String[] args) {

        new Thread(new MyTest(), "窗口1").start();
        new Thread(new MyTest(), "窗口2").start();
        new Thread(new MyTest(), "窗口3").start();

    }

    static class MyTest implements Runnable {

        @Override
        public void run() {
            while (true) {
                try {
                    Thread.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                
                    if (piao > 0) {
                        piao--;
                        System.out.println(Thread.currentThread().getName() + "卖了一张票,还剩:" + piao);
                    } else {
                        // 票没了
                        break;
                    }
                
            }
        }
    }
}

2、JMM内存模型

在这里插入图片描述

3、线程并发的三大特征

3.1、原子性

原子性就是指操作不可分割,所有操作要么全部执行完成,要么全部执行失败。

对于代码段,一次只允许一个线程执行

解决售票问题、加锁 synchronized

package com.hsq.thread;

/**
 * 售票问题
 */
public class MyThead_安全 {
    static Integer piao = 100;
    static Object o = new Object();
    public static void main(String[] args) {

        new Thread(new MyTest(), "窗口1").start();
        new Thread(new MyTest(), "窗口2").start();
        new Thread(new MyTest(), "窗口3").start();

    }

    static class MyTest implements Runnable {

        @Override
        public void run() {
            while (true) {
                try {
                    Thread.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (o){
                    if (piao > 0) {
                        piao--;
                        System.out.println(Thread.currentThread().getName() + "卖了一张票,还剩:" + piao);
                    } else {
                        // 票没了
                        break;
                    }
                }
            }
        }
    }
}

3.2、可见性

可见性:变量被修改后,对其他线程立马可见

package com.hsq.thread;

public class MyThread_可见性 {
    static  boolean flag = false;

    public static void main(String[] args) throws InterruptedException {
        new Thread(new test1(), "线程一").start();
        Thread.sleep(1000);
        new Thread(new test2(), "线程二").start();
        System.out.println(flag);
    }

    // 线程1
    static class test1 implements Runnable {

        @Override
        public void run() {
            System.out.println("线程一执行=======" + Thread.currentThread().getName());
            long sum = 0;
            while (!flag) {
                sum++;
            }
            System.out.println(sum);
        }
    }

    // 线程2
    static class test2 implements Runnable {

        @Override
        public void run() {
            System.out.println("线程2执行===========" + Thread.currentThread().getName());
            fun();
        }
    }

    public static void fun() {
        flag = true;
    }
}

出现线程一,一直执行

原因:参考JMM内存

3.3、有序性

有序性:代码禁止重排序

有序性是指代码在运行期间保证和代码的顺序一致,但实际上在执行过程中会进行指令重排序。

指令重排序:为了提高CPU的运行效率,JVM可能会对编译后的代码进行顺序调换等,但会保证最终结果与调换前的结果保持一致,指令的执行顺序与代码逻辑顺序不一致,这个过程就叫做指令的重排序。

  • 当然,在多线程的情况下重排序可不会保证我们代码最终结果的一致,可以通过sychronized或者volatile来确保有序性。

4、volatile

volatile关键字的作用是变量在多个线程之间可见。并且能够保证所修饰变量的有序性:
1、保证变量的可见性: 当一个被volatile关键字修饰的变量被一 个线程修改的时候,其他线程可以立刻得到修改之后的结果。当一个线程向被volatile关键字修饰的变量写入数据的时候,虚拟机会强制它被值刷新到主内存中。当-个线程用到被volatile关键字修饰的值的时候,虚拟机会强制要求它从主内存中读取。
2、屏蔽指令重排序: 指令重排序是编译器和处理器为了高效对程序进行优化的手段,它只能保证程序执行的结果时正确的,但是无法保证程序的操作顺序与代码顺序-致。这在单线程中不会构成问题,但是在多线程中就会出现问题。非常经典的例子是在单例方法中同时对字段加入volatile,就是为了防止指令重排序。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值