面试题答案
一键面试常见线程竞争场景
- 读写冲突:一个线程正在读取数据,另一个线程同时尝试写入数据。写入操作可能会修改数据结构,导致读取线程获取到不一致的数据。
- 写 - 写冲突:多个线程同时尝试写入数据库。这可能会导致数据的部分更新丢失,因为后一个写入操作可能会覆盖前一个写入操作的部分结果。
避免线程竞争的方法
- 锁机制:
- 互斥锁(Mutex):在SQLite中,可以使用互斥锁来确保同一时间只有一个线程能够访问数据库。当一个线程想要执行数据库操作时,它首先获取互斥锁。如果锁已被其他线程持有,该线程将等待,直到锁可用。例如,在C/C++中,可以使用POSIX线程库的
pthread_mutex_t
来实现互斥锁。
#include <pthread.h> #include <sqlite3.h> pthread_mutex_t db_mutex = PTHREAD_MUTEX_INITIALIZER; void* thread_function(void* arg) { sqlite3* db; // 打开数据库前获取互斥锁 pthread_mutex_lock(&db_mutex); sqlite3_open("test.db", &db); // 执行数据库操作 sqlite3_close(db); // 操作完成后释放互斥锁 pthread_mutex_unlock(&db_mutex); return NULL; }
- 读写锁(Read - Write Lock):适用于读多写少的场景。读写锁允许多个线程同时进行读操作,但只允许一个线程进行写操作。当有线程进行写操作时,其他读写线程都需要等待。在POSIX线程库中,可以使用
pthread_rwlock_t
来实现读写锁。
#include <pthread.h> #include <sqlite3.h> pthread_rwlock_t db_rwlock = PTHREAD_RWLOCK_INITIALIZER; void* read_thread_function(void* arg) { sqlite3* db; // 读操作前获取读锁 pthread_rwlock_rdlock(&db_rwlock); sqlite3_open("test.db", &db); // 执行读操作 sqlite3_close(db); // 操作完成后释放读锁 pthread_rwlock_unlock(&db_rwlock); return NULL; } void* write_thread_function(void* arg) { sqlite3* db; // 写操作前获取写锁 pthread_rwlock_wrlock(&db_rwlock); sqlite3_open("test.db", &db); // 执行写操作 sqlite3_close(db); // 操作完成后释放写锁 pthread_rwlock_unlock(&db_rwlock); return NULL; }
- 互斥锁(Mutex):在SQLite中,可以使用互斥锁来确保同一时间只有一个线程能够访问数据库。当一个线程想要执行数据库操作时,它首先获取互斥锁。如果锁已被其他线程持有,该线程将等待,直到锁可用。例如,在C/C++中,可以使用POSIX线程库的
- 线程调度策略:
- 队列化操作:可以将所有数据库操作放入一个队列中,由一个专门的线程负责从队列中取出操作并执行。这样,所有线程对数据库的访问就被串行化了,避免了线程竞争。例如,在Python中可以使用
Queue
模块来实现。
import sqlite3 from queue import Queue from threading import Thread db_queue = Queue() def db_worker(): conn = sqlite3.connect('test.db') while True: task = db_queue.get() if task is None: break cursor = conn.cursor() cursor.execute(task) conn.commit() db_queue.task_done() conn.close() db_thread = Thread(target=db_worker) db_thread.start() def perform_db_operation(sql): db_queue.put(sql)
- 线程池:使用线程池来管理对数据库的访问。线程池中的线程数量是有限的,当一个线程需要访问数据库时,从线程池中获取一个线程来执行操作。这样可以控制并发访问的数量,减少线程竞争。例如,在Java中可以使用
ExecutorService
和ThreadPoolExecutor
来实现线程池。
import java.sql.Connection; import java.sql.DriverManager; import java.sql.Statement; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class DatabaseAccess { private static final ExecutorService executor = Executors.newFixedThreadPool(5); public static void performDbOperation(String sql) { executor.submit(() -> { try (Connection conn = DriverManager.getConnection("jdbc:sqlite:test.db"); Statement stmt = conn.createStatement()) { stmt.executeUpdate(sql); } catch (Exception e) { e.printStackTrace(); } }); } }
- 队列化操作:可以将所有数据库操作放入一个队列中,由一个专门的线程负责从队列中取出操作并执行。这样,所有线程对数据库的访问就被串行化了,避免了线程竞争。例如,在Python中可以使用