java必学必会之线程(2)

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();
    }
}

以上线程池中的任务将交给线程池中的工作线程执行,当全部任务执行完毕后,线程池会自动关闭。

阅读剩余 73%

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:java必学必会之线程(2) - Python技术站

(0)
上一篇 2023年5月30日
下一篇 2023年5月30日

相关文章

  • maven多个仓库查询的优先级顺序案例讲解

    针对“maven多个仓库查询的优先级顺序案例讲解”这个主题,我将以以下方式进行讲解: 一、背景介绍 在使用maven进行依赖管理时,我们常常需要配置多个仓库。而当我们进行依赖查询时,maven也会按照一定的优先级顺序去依次查询这些仓库中是否存在对应的依赖。那么,maven多个仓库查询的优先级顺序是怎样的呢?本文将针对这一问题进行详细解析。 二、查询顺序 ma…

    Java 2023年5月20日
    00
  • jsp+servlet+jdbc实现对数据库的增删改查

    首先,我们需要明确一下技术栈的概念: JSP (Java Server Pages):基于Java语言的服务器端网页开发技术; Servlet:是Java Web应用程序的核心,位于服务端,负责处理浏览器发送过来的HTTP请求并返回响应结果; JDBC (Java Database Connectivity):Java数据库连接,Java语言操作关系型数据库…

    Java 2023年5月20日
    00
  • java 非对称加密算法RSA实现详解

    Java 非对称加密算法RSA实现详解 什么是非对称加密算法? 非对称加密算法指的是在加密和解密过程中分别使用两个不同的密钥,即公钥和私钥。公钥可以公开,任何人都可以使用公钥对信息进行加密,但只有私钥持有者才能解密被加密的信息。非对称加密算法具有安全性高、密钥分配方便等优点,因此被广泛应用于数据传输、数字证书等场景。 RSA算法简介 RSA算法是一种典型的非…

    Java 2023年5月19日
    00
  • 初学者,Spring快速入门

    以下是“初学者,Spring快速入门”的完整攻略: 目录 简介 准备工作 Spring快速入门 示例说明 总结 简介 Spring是一款流行的Java开发框架,它可以帮助开发者更加轻松地构建传统的Java应用程序和企业级应用程序。本攻略将帮助初学者快速入门Spring框架。 准备工作 在开始学习Spring框架之前,有一些基本的前置条件需要准备: JDK(J…

    Java 2023年5月19日
    00
  • Java8如何基于flatMap处理异常函数

    Java 8中的flatMap函数提供了一种优雅的处理异常函数的方法,使得我们可以更容易地在代码中处理异常。下面是一些基于flatMap的处理异常函数的可行方法和示例: 1. 使用Optional和flatMap Optional是Java 8中的一个类,它可以处理可能为空的对象。我们可以在函数中返回一个Optional对象,然后使用flatMap来处理异常…

    Java 2023年5月27日
    00
  • jquery动态加载select下拉框示例代码

    为了让回答更加清晰明了,我将对话分为几个步骤进行讲解,如下: 确认需求 首先,在动手敲代码之前,我们需要清晰地明确自己的需求是什么。在这个场景中,我们需要实现一个“动态加载select下拉框”的功能。具体来说,我们希望在页面刚加载完成时,下拉框中的选项列表是空的,当用户触发某个事件(比如点击某个按钮)后,网页通过调用ajax请求获取数据,并动态地将这些数据填…

    Java 2023年6月15日
    00
  • ASP开发中存储过程应用全接触

    ASP开发中存储过程应用全接触 什么是存储过程? 在数据库中,存储过程是一组为了完成特定任务的SQL语句集合。存储过程可以接收数据、处理数据并返回数据,它可以调用其他存储过程、控制逻辑、计划任务和其他编程构造。存储过程可以显著提高数据库的性能,同时也具备一些安全性和封装性方面的优势。在ASP开发中使用存储过程,可以使代码结构更清晰,可维护性更高,同时也能提高…

    Java 2023年6月16日
    00
  • SpringBoot利用jackson格式化时间的三种方法

    下面是SpringBoot利用jackson格式化时间的三种方法完整攻略: 1. 使用@JsonFormat注解 使用@JsonFormat注解是SpringBoot格式化时间最简单的方法之一,只需要在实体类的时间字段上加上该注解,指定格式即可。 示例: 假设我们有一个用户实体类,其中有一个创建时间字段: public class User { privat…

    Java 2023年5月20日
    00
合作推广
合作推广
分享本页
返回顶部