俗话说的好,学好数理化,走遍天下都不怕。也就是说总有一些基础中的基础,学好了这些就可以起到事半功倍的效果
CPU时间片是操作系统中一个重要概念,它指的是操作系统分配给每个进程在CPU上执行的时间长度。
在多任务处理系统中,CPU时间片用于实现多进程之间的轮转调度,确保每个进程都有机会执行并且不会长时间占用CPU。
CPU分配给各个线程/进程的时间片非常短,CPU通过不停地切换线程/进程执行(而这种切换如果太多会严重影响多线程程序的执行效率),让我们感觉多个线程是同时执行的,时间片一般是几十毫秒(ms)
既然多线程上下文切换,会消耗导致程序执行效率降低,那么如何减少切换呢? 减少上下文切换的方法有无锁并发编程、CAS算法、使用最少线程和使用协程。
1. 无锁并发编程 多线程竞争锁时,会引起上下文切换,所以多线程处理数据时,可以用一些办法来避免使用锁,如将数据的D按照Hsh算法取模分段,不同的线程处理不同段的数据。
2. CAS算法 Java的Atomic包使用CAS算法来更新数据,而不需要加锁。
在x86架构下,底层通常使用CMPXCHG指令来实现CAS操作。以下是在x86汇编中简单示例:
lock cmpxchg [destination], registerlock: 这个前缀指示CMPXCHG操作是原子的,即在执行期间不会被中断。cmpxchg: 比较目标内存位置的值和寄存器中的值,如果相等则将寄存器中的值写入目标内存位置,否则不执行任何操作。[destination]: 是要操作的目标内存位置。register: 是一个寄存器,用来存放要写入目标内存位置的值。在实际执行时,CPU会比较目标内存地址中的值和寄存器的值,如果相等,则将寄存器的值写入目标内存地址,并返回成功。否则不进行写入操作,返回失败。整个过程是原子的,不会被其他线程的操作打断。
3. 使用最少线程 避免创建不需要的线程,比如任务很少,但是创建了很多线程来处理,这样会造成大量线程都处于等待状态。
4. 协程 在单线程里实现多任务的调度,并在单线程里维持多个任务间的切换。
死锁是指两个或多个进程(或线程)相互等待对方持有的资源,导致它们都无法继续执行的情况。在死锁状态下,没有任何一个进程能够继续执行,系统变得无响应。
public class DeadlockExample {
private static final Object lock1 = new Object();
private static final Object lock2 = new Object();
public static void main(String[] args) {
Thread thread1 = new Thread(() -> {
synchronized (lock1) {
System.out.println("Thread 1: Holding lock 1...");
try {
Thread.sleep(100);
} catch (InterruptedException e) {}
System.out.println("Thread 1: Waiting for lock 2...");
synchronized (lock2) {
System.out.println("Thread 1: Holding lock 1 and lock 2...");
}
}
});
Thread thread2 = new Thread(() -> {
synchronized (lock2) {
System.out.println("Thread 2: Holding lock 2...");
try {
Thread.sleep(100);
} catch (InterruptedException e) {}
System.out.println("Thread 2: Waiting for lock 1...");
synchronized (lock1) {
System.out.println("Thread 2: Holding lock 1 and lock 2...");
}
}
});
thread1.start();
thread2.start();
}
}如何避免死锁
1. 避免一个线程同时获取多个锁。
2. 避免一个线程在锁内同时占用多个资源,尽量保证每个锁只占用一个资源。
3. 尝试使用定时锁,使用lock.tryLock(timeout)来替代使用内部锁机制。
4. 对于数据库锁,加锁和解锁必须在一个数据库连接里,否则会出现解锁失败的情况。
这里的资源限制包括硬件资源限制和软件资源限制。在进行多线程编程时,要考虑资源限制。
举个例子:某机器的宽带只有 1Mb/s,某资源下载速度为 500KB/s,你用 10个线程,下载速度也不会变成 5000KB/s,最多为 1Mb/s。
另外,如果将代码改成多线程,但由于资源限制,导致其仍然以单线程方式执行,那这个程序会更慢,因为相比于单线程,增加了上下文切换和资源调度的时间。这一点需要额外注意!
虽然线程或进程切换会消耗一定的时间和资源,但使用CPU时间片的方式有一些重要的原因和优势,以确保系统能够高效地运行和满足多任务处理的需求:
多线程编程在提高系统性能和并发性方面具有重要作用,但也面临着一些挑战,如上下文切换的开销、死锁的风险和资源限制等。通过合理使用CPU时间片、减少上下文切换、避免死锁和考虑资源限制,可以提高多线程编程的效率和稳定性。