Java必学必会之线程(2)攻略
线程同步
在多线程编程中,线程同步是一个非常重要的问题。如果不加以控制,在多线程同时访问共享资源的情况下,可能会导致数据不一致、死锁等问题。
同步的两种方式
Java 中实现同步的两种方式分别是 synchronized 和 ReentrantLock。
synchronized 关键字是 Java 提供的默认的语言级别的同步机制,可以用于实现方法或者块的同步。当一个线程调用 synchronized 方法时,其他线程可以调用对象的非 synchronized 方法,但是会阻塞,直到第一个线程执行完毕。
public synchronized void deposit(int amount) {
balance = balance + amount;
}
ReentrantLock 则是 Java 提供的高级同步机制,可以更加灵活地对锁进行控制。与 synchronized 相比,ReentrantLock 支持更加灵活的同步方式,如公平锁、多个 Condition 等。
Lock lock = new ReentrantLock();
...
lock.lock();
try {
// 内部业务逻辑
} finally {
lock.unlock();
}
死锁的产生
在多线程编程中,死锁是常见的难题。死锁是指两个或多个线程在执行过程中,因竞争资源或互相等待而造成的一种互相等待的现象。
死锁原因通常有以下几种:
- 竞争资源
- 线程锁定顺序死锁
- 动态的锁顺序死锁
以下示例说明了死锁的问题:
class DeliveryService{
public synchronized void takeOrder(Order order){
// 待实现的代码
}
public synchronized void deliverOrder(Order order){
// 待实现的代码
}
}
在上述示例代码中,当一个派送员在派送订单时,需要先占用 deliverOrder 方法的锁。而这个派送员在派送订单前,需要先接受顾客的订单,也就是占用 takeOrder 方法的锁。如果此时多个派送员同时接收订单,那么就会发生死锁。
避免死锁
避免死锁问题,一种方法是通过共享锁来实现同步。另外,还需要注意避免锁竞争、锁嵌套等问题。
以下是一些避免死锁的方法:
- 破坏请求资源的环路
- 使用尝试锁定机制
- 按顺序加锁
- 应用程序拥有锁的时长尽可能短
线程的池化
线程池是 Java 并发包提供的用于管理线程的工具。线程池由线程池管理器和一组工作线程组成。
如何使用线程池
int corePoolSize = 5;// 池中所保存的线程数,包括空闲线程。
int maximumPoolSize = 10;// 池中允许的最大线程数。
long keepAliveTime = 60000;// 当线程数大于核心线程数时,此为终止多余的空闲线程等待新任务的最长时间。
BlockingQueue workQueue = new LinkedBlockingQueue();// 当线程池的任务队列满时,提交的任务将在此队列中等待。
ThreadPoolExecutor threadPool = new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime,TimeUnit.MILLISECONDS, workQueue);
threadPool.execute(new MyTask());// 调用execute方法添加一个要执行的任务
threadPool.shutdown();// 当添加任务后关闭线程池
线程池任务队列的选择
Java 中线程池任务队列的选择有以下几种:
- LinkedBlockingQueue:基于链表的阻塞队列,按照先进先出的原则对任务进行排序。适用于任务比较频繁且任务处理时间比较短的情况。
- ArrayBlockingQueue:基于数组的有界队列,适用于任务比较稳定的情况。
- PriorityBlockingQueue:具有优先级的无界队列。
示例
同步示例
以下示例代码演示了使用 synchronize 关键字实现同步。
public class Counter {
private int count = 0;
public synchronized void increment() {
count++;
}
public synchronized void decrement() {
count--;
}
public synchronized int getCount() {
return count;
}
}
public class Worker extends Thread {
private Counter counter;
public Worker(Counter counter) {
this.counter = counter;
}
public void run() {
for (int i = 0; i < 1000000; i++) {
counter.increment();
counter.decrement();
}
}
}
public class Test {
public static void main(String[] args) {
Counter counter = new Counter();
Worker[] workers = new Worker[10];
for (int i = 0; i < 10; i++) {
workers[i] = new Worker(counter);
workers[i].start();
}
for (int i = 0; i < 10; i++) {
try {
workers[i].join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(counter.getCount());
}
}
线程池示例
以下示例代码演示了使用线程池的方式执行任务。
public class TestThreadPool {
public static void main(String[] args) {
ExecutorService threadPool = Executors.newFixedThreadPool(5);
for (int i = 0; i < 10; i++) {
threadPool.execute(new Runnable() {
public void run() {
System.out.println(Thread.currentThread().getName() + " is running");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
}
threadPool.shutdown();
}
}
以上线程池中的任务将交给线程池中的工作线程执行,当全部任务执行完毕后,线程池会自动关闭。
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:java必学必会之线程(2) - Python技术站