进程,线程,抢占式调度,synchronized锁,AtomicInteger解决原子类问题的具体过程

多线程的定义:

Java中的多线程是指同一个java进程下的多个线程交叉执行。

是不是听起来很晕?晕就对了,我也在想世界上怎么会有这么有逻辑的话术,简练到不符合中文的逻辑,有种初学英语的晦涩感。实际上,多线程的概念没有想象中的那么复杂,是有方法的。只要真正的明白理解它了,那么后面的原子性和不可见性就简单了,所以接下来我将说一下个人的理解思路:我个人是通过拆分理解它的,首先是知道进程,线程是什么东西。

进程:程序运行的一次过程,ye'jiu'shi说系统运行一次就相当于一个进程从新建,运行,死亡的过程。一个程序由多个进程组成。现在的电脑是多进程的,可以同时浏览网站和听歌。

就像我现在运行的浏览器,是一个程序,我运行它的时候,他里面有22个进程。因为进程已经是程序一次执行的最小单元了,所以单个进程之间不会互相影响的。

线程:线程是进程的一个执行单元,负责进程的执行,一个进程最少是单条线程的,如果是多条线程,那么就是多线程。

Java进程:Java程序中由java命令启动JVM虚拟机,这就相当于创建了一个进程,接着该进程去调用main方法也就是我们的主线程。Java程序本身是多线程,因为它还有一个垃圾回收机制。但是它只支持单线程操作,也就是说,当java程序运行时,如果没有其余的线程,那么垃圾回收机制和主线程是独立的。如果有其余的线程,那么主线程和其余的线程之间的线程运行是独立,也就是说,它要不就是在运行main方法 要不就是在运行其余的线程。具体运行规则要看具体的代码了。  

抢占式调度:线程只有获取到系统CPU的时间片才能执行,因为时间片一般都很短,所以线程之间的切换都是高并发的,也就是说java本身就是一个高并发的。有些人看到这里可能会有疑问,既然java中是单线程操作,那么为什么当用 synchronized锁锁住线程时,进程的执行效率会变低,执行所有的线程结束花的时间一般会更长? 实际上是这样的,因为java中虽然是单线程的,单看起来先执行哪个线程,用不用锁没区别。实际上当我们用锁的时候,因为锁里面的代码变成了一个整体,当里面有一些耗时代码sleep或者当从后台加载别的文件需要等待而不会失去锁芯时。如果没有锁,进程在那段耗时操作时,别的线程就会抢占时间片去执行程序。

什么是原子性问题:

Java中把原子当作最小的单元,所谓的原子性问题就相当于对于线程来说,时间片不够执行一次完整的线程。比如说线程A要执行对A自加10000 0000,线程B也要对同一个A进行自加。但是因为A和B每次抢占到的时间片都不够执行一次完整的线程,结果可能出现A自加时获取到的值还没来得及使用或者使用后还没来得及修改主内存的值时,又重新被B线程获取了。这样可能就会重复,具体怎样看计算机性能,代码越复杂,步骤越多就越大的可能性发生原子类问题。

public class RunableDemo {
        //定义共享变量a
    static int a = 0;

        //创建主方法(主线程),java程序只会从主方法开始运行
    public static void main(String[] args) {


        //利用匿名内部类创建线程A
        new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 10000000; i++) {
        //里面有三步  1.从主内存获取a的值   2,进行a+1运算   3,将a+1赋值给a
                    a++;
                }
            }
        }, "线程A").start();
        new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 10000000; i++) {
       //里面有三步  1.从主内存获取a的值   2,进行a+1运算   3,将a+1赋值给a
                    a++;
                }
            }
        }, "线程B").start();
      //因为加上main线程和垃圾回收是4条线程,为了保证A,B都运行完毕,让主线程休眠2秒
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        //输出a的值   19803346
        //int最大值是:2147483647
        System.out.println(a);

    }
}

可以看到:

预期的结果是:2000 0000,结果输出的结果是:1980 3346  比预期结果小。

原因是:因为A和B每次抢占到的时间片都不够执行一次完整的线程,结果可能出现A自加时获取到的值还没来得及使用或者使用后还没来得及修改主内存的值时,又重新被B线程获取了。中间的计算可能出现重复,但是计算机运行太快了,如果性能好的话,发生原子性的问题几率会变小,那么可以用long类型的数据测试。归根到底原子性问题就是因为线程不能完整的执行一个过程而导致的。如果可以让线程里面的多个步骤变成一个整体 ,就不会出现这种问题了。

后面lock锁和synchronized关键字也就是利用这种思想。而AtomicInteger是采用getAndIncrement()采用原子类的形式将自加变成一个整体,这样赋值自加过程就不会出现问题了。也就不会出现还没自加就被别的线程抢到了时间片了,而别的线程得到数字要不就是用原子类方法自加前的要不就是用原子类方法自加后的,就不会出现重复的问题。

synchronized锁;将多行代码当成是一个完整的整体,一个线程如果进入到这个代码块中,会全部执行完毕,执行结束后,其它线程才会执行

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值