zoukankan      html  css  js  c++  java
  • java(9)并发编程

    整理自《java 并发编程的艺术》

    1. 上下文切换

        即使是单核处理器也支持多线程执行代码,CPU通过给每个线程分配CPU时间片来实现这个机制。时间片是CPU分配给各个线程的时间,因为时间片非常短,所以CPU通过不停地切换线程执行,让我们感觉多个线程是同时执行的,时间片一般是几十毫秒(ms)。
        CPU通过时间片分配算法来循环执行任务,当前任务执行一个时间片后会切换到下一个任务。但是,在切换前会保存上一个任务的状态,以便下次切换回这个任务时,可以再加载这个任务的状态。所以任务从保存到再加载的过程就是一次上下文切换

    2. 多线程一定快吗?

        public class ConcurrencyTest {
            
            private static final long count = 1000000000l;
            public static void main(String[] args) throws InterruptedException {
                    concurrency();
                    serial();
            }
            
            
            private static void concurrency() throws InterruptedException {
                    long start = System.currentTimeMillis();
                    Thread thread = new Thread(new Runnable() {
                            @Override
                            public void run() {
                                    int a = 0;
                                    for (long i = 0; i < count; i++) {
                                            a += 5;
                                    }
                            }
                    });           
                    //开启线程循环
                    thread.start();
                    
                    //在主线程中执行的循环count次
                    int b = 0;
                    for (long i = 0; i < count; i++) {
                            b--;
                    }
                    long time = System.currentTimeMillis() - start;
                    thread.join();
                    System.out.println("concurrency :" + time+"ms,b="+b);
            }
            
            
            private static void serial() {
                    long start = System.currentTimeMillis();
                    int a = 0;
                    for (long i = 0; i < count; i++) {
                            a += 5;
                    }
                    int b = 0;
                    for (long i = 0; i < count; i++) {
                            b--;
                    }
                    long time = System.currentTimeMillis() - start;
                    System.out.println("     serial :" + time+"ms,b="+b+",a="+a);
            }
        }

    3. 测试上下文切换次数和时长

    * 使用Lmbench3可以测量上下文切换的时长
    * 使用vmstat可以测量上下文切换的次数        
    procs -----------memory---------- ---swap-- -----io---- --system-- -----cpu-----
     r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st
     0  0 239444 190120 201124 13106568    0    0   276    20    1    1  1  0 99  0  0
     0  0 239444 190120 201124 13106596    0    0  1000     0 1246 2407  1  0 99  0  0
     1  0 239444 190128 201124 13106596    0    0  1000     0 1261 2421  1  1 98  0  0
     0  0 239444 190128 201124 13106604    0    0  1000    40 1220 2410  1  1 99  0  0
     1  0 239444 190128 201124 13106604    0    0  1000    60 1252 2426  1  1 98  1  0
     0  0 239444 190208 201124 13106604    0    0  1000    24 1239 2455  1  1 98  1  0
    
    注:CS(Content Switch)表示上下文切换的次数,从上面的测试结果中我们可以看到,上下文每1秒切换2000多次。    

    4. 如何减少上下文切换

      减少上下文切换的方法有无锁并发编程、CAS算法、使用最少线程和使用协程

    5. 减少上下文切换实战

    * 本节将通过减少线上大量WAITING的线程,来减少上下文切换次数
    
    第一步:用jstack命令dump线程信息,看看pid为3117的进程里的线程都在做什么。    
    sudo -u admin /opt/ifeve/java/bin/jstack 31177 > /home/tengfei.fangtf/dump17
    
    第二步:统计所有线程分别处于什么状态,发现300多个线程处于WAITING(onobject-monitor)状态。    
    [tengfei.fangtf@ifeve ~]$ grep java.lang.Thread.State dump17 | awk '{print $2$3$4$5}'| sort | uniq -c
    39 RUNNABLE
    21 TIMED_WAITING(onobjectmonitor)
    6 TIMED_WAITING(parking)
    51 TIMED_WAITING(sleeping)
    305 WAITING(onobjectmonitor)
    3 WAITING(parking)
    
    第三步:打开dump文件查看处于WAITING(onobjectmonitor)的线程在做什么。发现这些线程基本全是JBOSS的工作线程,在await。说明JBOSS线程池里线程接收到的任务太少,大量线程都闲着。      
    "http-0.0.0.0-7001-97" daemon prio=10 tid=0x000000004f6a8000 nid=0x555e in
        Object.wait() [0x0000000052423000]
     java.lang.Thread.State: WAITING (on object monitor)
     at java.lang.Object.wait(Native Method)
     - waiting on <0x00000007969b2280> (a org.apache.tomcat.util.net.AprEndpoint$Worker)
     at java.lang.Object.wait(Object.java:485)
     at org.apache.tomcat.util.net.AprEndpoint$Worker.await(AprEndpoint.java:1464)
     - locked <0x00000007969b2280> (a org.apache.tomcat.util.net.AprEndpoint$Worker)
     at org.apache.tomcat.util.net.AprEndpoint$Worker.run(AprEndpoint.java:1489)
     at java.lang.Thread.run(Thread.java:662)    
    
    第四步:减少JBOSS的工作线程数,找到JBOSS的线程池配置信息,将maxThreads降到100。
    
    
    第五步:重启JBOSS,再dump线程信息,然后统计WAITING(on object monitor)的线程,发现减少了175个。
    WAITING的线程少了,系统上下文切换的次数就会少,因为每一次从WAITTING到RUNNABLE都会进行一次上下文的切换。读者也可以使用vmstat命令测试一下        

    6. 定位死锁  

    * 一旦出现死锁,业务是可感知的,因为不能继续提供服务了,那么只能通过dump线程查看到底是哪个线程出现了问题,以下线程信息告诉我们是DeadLockDemo类的第42行和第31行引起的死锁
    "Thread-2" prio=5 tid=7fc0458d1000 nid=0x116c1c000 waiting for monitor entry [116c1b000]
        java.lang.Thread.State: BLOCKED (on object monitor)
            at com.ifeve.book.forkjoin.DeadLockDemo$2.run(DeadLockDemo.java:42)
            - waiting to lock <7fb2f3ec0> (a java.lang.String)
            - locked <7fb2f3ef8> (a java.lang.String)
            at java.lang.Thread.run(Thread.java:695)
    "Thread-1" prio=5 tid=7fc0430f6800 nid=0x116b19000 waiting for monitor entry [116b18000]
        java.lang.Thread.State: BLOCKED (on object monitor)
            at com.ifeve.book.forkjoin.DeadLockDemo$1.run(DeadLockDemo.java:31)
            - waiting to lock <7fb2f3ef8> (a java.lang.String)
            - locked <7fb2f3ec0> (a java.lang.String)
            at java.lang.Thread.run(Thread.java:695)    
  • 相关阅读:
    51nod乘积之和
    Dell服务器安装OpenManage(OMSA)
    Nginx反向代理PHP
    搭建haproxy
    108. Convert Sorted Array to Binary Search Tree
    60. Permutation Sequence
    142. Linked List Cycle II
    129. Sum Root to Leaf Numbers
    118. Pascal's Triangle
    26. Remove Duplicates from Sorted Array
  • 原文地址:https://www.cnblogs.com/polestar/p/7161109.html
Copyright © 2011-2022 走看看