并发编程(一)

1、上下文切换

CPU通过时间片分配算法来循环执行任务,当前任务执行一个时间片后会切换到下一个任务。但是,在切换前会保存上一个任务的状态,以便下次切换回这个任务时,可以再加载这个任务的状态。所以任务从保存到再加载的过程就是一次上下文切换。有性能损耗,线程不是越多越好,毫秒级

2、多线程一定会快吗?

不一定,如果是单核CPU,需要CPU切换,切换会有性能损耗,需要时间

CPU产生计算浪费时(请求时间远远大于CPU处理时间),使用多线程更合适

让CPU刚好满,多线程的个数取决于CPU浪费比例(请求时间/处理时间),只能判断多线程个数的上限,无法判断下限

3、测线程的工具

使用Lmbench3 可以测量上下文切换的时长。

·使用vmstat可以测量上下文切换的次数。

  1. JMeter:一个开源的、基于Java的性能测试工具,可以用于测试Web应用程序、数据库、网络等的性能。
  2. LoadRunner:一个商业性能测试工具,可以用于测试各种应用程序的性能,包括Web、数据库、网络等。
  3. Gatling:一个开源的、基于Scala的高性能测试工具,主要用于测试Web应用程序的性能。
  4. Locust:一个开源的、基于Python的性能测试工具,可以用于编写可扩展的性能测试用例。
  5. Tsung:一个开源的、基于Erlang的性能测试工具,可以用于测试Web、数据库、实时通讯等应用程序的性能。

4、减少上下文切换的方法:减少线程数量

使用线程池:线程池可以重用已经存在的线程,避免创建和销毁线程的开销,从而减少上下文切换的次数。

使用锁机制:使用锁机制可以避免多个线程同时访问共享资源,从而减少上下文切换的次数。

使用并发集合:Java提供了一些并发集合,如ConcurrentHashMap、CopyOnWriteArrayList等,这些集合在多线程环境下具有较好的性能,可以减少上下文切换的次数。

避免频繁的I/O操作:频繁的I/O操作会导致线程频繁的阻塞和唤醒,从而增加上下文切换的次数。因此,应该尽量避免频繁的I/O操作。

合理使用synchronized和volatile:synchronized和volatile是Java中常用的并发控制工具,但是使用不当会导致上下文切换的次数增加。因此,应该根据实际情况合理使用synchronized和volatile。

避免使用Thread.sleep()和Thread.yield():Thread.sleep()和Thread.yield()会导致线程阻塞,从而增加上下文切换的次数。因此,应该尽量避免使用这些方法。

使用Java 8的流式API:Java 8引入了流式API,可以减少对原始数据结构的访问次数,从而减少上下文切换的次数。

5、dump: 将当前信息执行命令信息导出一个文件

6、在Java中,synchronized是一个关键字,用于控制多个线程对共享资源的访问,以避免出现线程安全问题。

synchronized可以用于方法或代码块。当它用于方法时,整个方法都是同步的,这意味着在任何时刻只有一个线程可以执行该方法。当它用于代码块时,它只同步该代码块,而不是整个方法。等代码块/方法执行完后释放锁。

以下是使用synchronized的示例:

public class SynchronizedExample {  
    private int count = 0;  
  
    public synchronized void incrementCount() {  
        count++;  
    }  
  
    public void worker1() {  
        for (int i = 0; i < 1000; i++) {  
            incrementCount();  
        }  
    }  
  
    public void worker2() {  
        for (int i = 0; i < 1000; i++) {  
            incrementCount();  
        }  
    }  
  
    public static void main(String[] args) throws InterruptedException {  
        SynchronizedExample example = new SynchronizedExample();  
        Thread t1 = new Thread(new Runnable() {  
            @Override  
            public void run() {  
                example.worker1();  
            }  
        });  
        Thread t2 = new Thread(new Runnable() {  
            @Override  
            public void run() {  
                example.worker2();  
            }  
        });  
        t1.start();  
        t2.start();  
        t1.join();  
        t2.join();  
        System.out.println("Final count: " + example.count); // Should be 2000  
    }  
}

在这个示例中,incrementCount()方法是同步的,这意味着在任何时刻只有一个线程可以调用它。尽管有两个线程同时执行worker1()和worker2()方法,但它们都尝试调用incrementCount()方法时,每次只有一个线程能够执行该方法。因此,最终的计数应该是2000。

synchronized 锁代码块时只能锁引用类型,不能是基本类型;不能修饰变量,只能修饰方法

不允许有两个线程对同一资源同时加锁;一个线程对一个资源加锁,其它线程可以访问该资源

使用synchronized关键字时,需要注意以下几点:

对象锁:synchronized关键字用于修饰方法或代码块,称为对象锁。同一时间只有一个线程可以访问被synchronized修饰的方法或代码块,其他线程需要等待当前线程执行完毕后才能继续执行。

锁的粒度:当存在多个线程共同操作共享数据时,需要保证同一时刻有且只有一个线程在操作共享数据。使用synchronized关键字可以控制多个线程对共享资源的访问,但需要注意锁的粒度。如果锁的粒度太大,可能会导致多个线程同时竞争同一个锁,从而降低程序的性能。

锁的释放:当一个线程持有锁时,其他线程必须等待该线程释放锁后才能继续执行。因此,在使用synchronized关键字时,需要注意锁的释放。如果一个线程在持有锁的过程中出现了异常,那么该锁可能不会被释放,从而导致其他线程一直等待下去,造成死锁。

锁的公平性:Java中的synchronized关键字默认情况下是公平的,即按照线程请求锁的顺序来分配锁。但是,如果多个线程同时竞争同一个锁,那么可能会出现不公平的情况。如果需要保证锁的公平性,可以使用java.util.concurrent.locks.ReentrantLock类来实现。

避免嵌套锁:在使用synchronized关键字时,需要注意避免嵌套锁。如果一个线程已经持有了一个锁,然后又试图获取另一个锁,那么可能会导致死锁。因此,在使用synchronized关键字时,应该尽量避免嵌套锁的情况。

避免在持有锁的情况下进行I/O操作:当一个线程持有锁时,其他线程必须等待该线程释放锁后才能继续执行。因此,如果在持有锁的情况下进行I/O操作(如打印输出、网络通信等),那么可能会导致其他线程一直等待下去,造成死锁。因此,在使用synchronized关键字时,应该尽量避免在持有锁的情况下进行I/O操作。

7、死锁

阻塞状态:不会被操作系统选中执行,能节省CPU开销

public class DeadLockDemo {
privat static String A = "A";
private static String B = "B";
    public static void main(String[] args) {
        new DeadLockDemo().deadLock();
    }
    private void deadLock() {
        Thread t1 = new Thread(new Runnable() {
            @Override
            publicvoid run() {
                synchronized (A) {
                    try { 
                        Thread.currentThread().sleep(2000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                synchronized (B) {
                    System.out.println("1");
                }
            }
        }
    });
    Thread t2 = new Thread(new Runnable() {
        @Override
        publicvoid run() {
            synchronized (B) {
                synchronized (A) {
                System.out.println("2");
                }
            }
        }
    });
    t1.start();
    t2.start();
    }
}

上述是一段死锁的代码。首先t1线程对A代码块进行加锁,加锁之后t1线程随即进入休眠2000毫秒,立即让出CPU;然后执行t2线程,t2线程对B进行了加锁,之后t2线程尝试对A加锁,由于t1线程已经对A加过锁,两个线程不能同时对同一资源进行加锁,所以此时t2进入阻塞状态;等到t1线程休眠2000毫秒后,继续执行t1线程,然后t1线程尝试对B进行加锁,但是B已被t2线程加过锁,所以t1进入阻塞状态,此时t1和t2线程形成死锁。

①什么是死锁

死锁是指两个或两个以上的进程在执行过程中,因争夺资源而造成的一种相互等待的现象,若无外力作用,它们都将无法推进下去。

②避免死锁的方法

  1. 避免多次锁定:尽量避免同一个线程对多个 Lock 进行锁定。例如,主线程要对 A、B 两个对象的 Lock 进行锁定,副线程也要对 A、B 两个对象的 Lock 进行锁定,这就埋下了导致死锁的隐患。
  2. 保持相同的加锁顺序:如果多个线程需要对多个 Lock 进行锁定,则应该保证它们以相同的顺序请求加锁。比如,主线程先对 A 对象的 Lock 加锁,再对 B 对象的 Lock 加锁;而副线程则先对 B 对象的 Lock 加锁,再对 A 对象的 Lock 加锁。这种加锁顺序很容易形成嵌套锁定,进而导致死锁。如果让主线程、副线程按照相同的顺序加锁,就可以避免这个问题。
  3. 使用定时锁:程序在调用 acquire() 方法加锁时可指定 timeout 参数,该参数指定超过 timeout 秒后会自动释放对 Lock 的锁定,这样就可以解开死锁了。
  4. 死锁检测:死锁检测是一种依靠算法机制来实现的死锁预防机制,它主要是针对那些不可能实现按序加锁,也不能使用定时锁的场景的。

此外,还有一些其他策略可以避免死锁:

  1. 尽量避免并发地只执行更新数据的语句。
  2. 要求每个事务一次就将所有要使用的数据全部加锁,否则就不予执行。
  3. 预先规定一个封锁顺序,所有的事务都必须按这个顺序对数据执行封锁。例如,不同的过程在事务内部对对象的更新执行顺序尽量保持一致。
  4. 每个事务的执行时间不可太长,在业务允许的情况下可以考虑将事务分割成为几个小事务来执行。
  5. 将逻辑上在一个表中的数据尽量按行或列分解为若干小表,以便改善对表的访问性能。一般来讲,如果数据不是经常被访问,那么死锁就不会经常发生。
  6. 将经常更新的数据库和查询数据库分开。

相关推荐

  1. 并发编程

    2024-01-11 17:20:05       34 阅读
  2. 、Go入门到进阶:并发编程

    2024-01-11 17:20:05       8 阅读
  3. C++ 并发编程 | 并发世界

    2024-01-11 17:20:05       42 阅读
  4. Python并发编程

    2024-01-11 17:20:05       38 阅读
  5. epoll并发编程

    2024-01-11 17:20:05       32 阅读
  6. python并发编程

    2024-01-11 17:20:05       36 阅读
  7. Golang 并发编程详解

    2024-01-11 17:20:05       33 阅读

最近更新

  1. TCP协议是安全的吗?

    2024-01-11 17:20:05       18 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-01-11 17:20:05       19 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-01-11 17:20:05       18 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-01-11 17:20:05       20 阅读

热门阅读

  1. What does rpm do?

    2024-01-11 17:20:05       38 阅读
  2. Linux 之间通过 SSH 传输文件

    2024-01-11 17:20:05       31 阅读
  3. linux踢掉远程登录用户*

    2024-01-11 17:20:05       36 阅读
  4. 【Unity】优化 if else 和 Switch Case

    2024-01-11 17:20:05       33 阅读
  5. web安全之XSS攻击原理及防范

    2024-01-11 17:20:05       36 阅读
  6. angular-tree-component组件中实现特定节点自动展开

    2024-01-11 17:20:05       39 阅读
  7. Python爬虫快速入门

    2024-01-11 17:20:05       38 阅读
  8. TypeScript使用技巧内置工具类型详解

    2024-01-11 17:20:05       39 阅读
  9. git branch 用法汇总和实际使用用例

    2024-01-11 17:20:05       37 阅读