Java并发编程(上)

并发:多个线程(进程)竞争一个资源

并行:多个线程(进程)同时运行不同资源

线程和进程的关系简单地说,进程是一个容器,一个进程中可以容纳若干个线程,一个进程里面,至少有一个线程

跨系统的并发实现底层原理需要更好的机器来实现,不是跨系统的就是调用**操作系统的CAH**来实现,如下图所示

线程的状态

- 新建--就绪--运行--阻塞--等待---死亡
- 就绪队列、阻塞队列、等待队列

- 一个线程申请资源时,无法让其马上执行,而是先进入队列变为就绪态,然后等待资源分配,若马上就让其直接执行,会导致之前正在执行的进程中断、丢失

- 和操作人员交互多的指令,在运行时优先级越高、响应也越迅速

- 每个进程在CPU中分配的时间片是不同的,当该进程的时间片用完且该进程还没完成时就会中心进入就绪态,等待下一次执行,直到完成为止,同时,若一个进程在分配的时间还没用完就已经完成,此时CPU会马上切换任务,不会休息(比如进程A共需要5ms,但是cpu分配了10,此时在执行到5ms时,CPU便不再处理A,转而执行B),另外,只有没有任何进程时,CPU才会休息

- 不同的系统,每次为进程分配的时间片方式不同,一般是随机的,也有等长的,比如linux

- 在操作系统中,每个进程是否可以进入就绪、运行是靠优先级决定的,具体如何计算优先级每个系统的方法可能不一样,主要方法有优先级队列、等待响应比等

- 当一个进程的时间片用完,操作系统会记录此时的状态(即执行到哪里,也就是执行到的地址)和下一次执行时间,以便下一轮时间片到的时候可以继续执行

- 操作系统选中哪个线程去执行是不确定的,是随机的

- 进程执行完后进入死亡状态,时间片用完重新回到就绪态

- 阻塞队列:竞争枷锁资源失败的时候进程A就会进入阻塞队列,若某一刻被竞争的资源被正在占用的进程B释放,此时A又可以进去就绪队列竞争资源

- 等待队列:一个处于等待队列的进程A是不会自己接触等待状态的,只有当别人唤醒时,他才能进入就绪态去竞争资源,同时一个进程可以自己进入等待队列,正在执行的进程也可以自己进入等待队列

- 先进入就绪态的被先执行的概率越大,但不是百分之百,后进入就绪态的可能比先进入的更早执行

- 在线程完定义的变量,若需要出现在线程内,则该线程会默认的将该变量认为是final修饰的,此时若该变量是基本数据类型,则不能重新赋值,若是引用变量,则指向不能改变,但是内容可以改变

int a=10;
int[] arr = {0};
Thread x1 = new Thread(){
    a=100;	//错误
    @Override
    public void run(){
        arr[0] = 100; 	//不报错,因为指向没有变
    }
};

在有多个线程时,main线程会最先执行,然后是剩下的线程平等的竞争CPU,每个线程之间都是互不等待的,例如:

- 在上述代码中,arr[0]大概率是0,因为main先执行,当x1与x2进入就绪态时,分配给main的时间片可能还未用完,便会直接执行输出,但这是大概率,也有可能在main线程未执行到输出时时间片用完,此时arr[0]就会改变
- 若此时,我们加上join,就会取消掉互不等待,

此时,x1执行完之后,x2才会执行,最后是输出,注意,x1.join()执行,只是让其之后的等待,但x2是在x1.join()之前进入的就绪态,所以此时并不会让x2线程中的内容停止执行,这时候就是竞争CPU了

- 从严格的物理上讲,同一时刻只能有一个线程对变量进行操作,但是当我们有了告诉缓存后,只有在第一次对其执行的时候需要占用总线,剩下的只需要在缓存中操作即可,大大加快了速度与并发效率,但是,特也产生了并发问题
  - 比如上述的两个线程对arr[0]进行+1操作,可能会有多种情况,但是最后arr[0]的结果只能是小于等于20000的,最小的结果是1
- 那么如何解决并发问题呢?
  - 锁机制
    - synchronized    重量级重入锁,
      - 当synchronized锁静态方法时,锁的是该方法,如果别人想要调用,就要先加锁,且每次调用都要加锁,想调用,就加锁

上下文切换

**多线程一定比单线程快吗?**

- 不一定,因为有上下文切换
- 在产生CPU浪费的情况下,多线程合适,无浪费的情况下,单线程合适
- 在执行的操作次数越多,多线程就越快,次数越少,单线程相比更快,如下图:

原因:因为线程有创建和上下文切换的开销

当执行的操作次数很少时,CPU的使用率与多线程时相差不多,但是多线程会有一个创建和上下文切换的开销,所以总的时间就会变长;当执行操作次数很多时,单线程就会因为IO的时间而导致CPU在这期间会“发呆”(没有操作来使用CPU),导致CPU的使用率大大降低,而多线程在出现IO时并不会等待IO的结束,而是会直接执行其他操作,只要该操作的CPU使用时间完成,就直接让下一个操作来占用CPU,此时CPU就会不间断的工作起来,不再休息

- 线程性能测量工具
  - 使用Lmbench3来测量上下文切换的时长
  - 使用vmstat可以测量上下文切换的次数
- 如何减少上下文切换的次数
  - 无锁并发编程:多线程竞争锁时,会引起上下文切换,所以多线程处理数据时,可以用一 些办法来避免使用锁,如将数据的ID按照Hash算法取模分段,不同的线程处理不同段的数据
  - CAS算法:Java的Atomic包使用CAS算法来更新数据,而不需要加锁
  - 使用最少线程数:避免创建不必要的线程。比如任务很少但是创建的线程很多
  - 协程
- 减少上下文切换的实战
  - 通过减少线上大量WAITING的线程,来减少上下文切换次数
    - 第一步:用jstack命令dump线程信息(jsatck指令)
    - 第二步:统计所有线程分别处于什么状态(grep指令)
    - 第三步:打开dump文件查看处于WAITING(onobjectmonitor)的线程在做什么
    - 第四步:减少JBOSS的工作线程数,找到JBOSS的线程池配置信息,将maxThreads降到 100
    - 第五步:重启JBOSS,再dump线程信息,然后统计WAITING(onobjectmonitor)的线程(WAITING的线程少了,系统上下文切换的次数就会少,因为每一次从 WAITTING到RUNNABLE都会进行一次上下文的切换)

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值