iis服务器助手广告广告
返回顶部
首页 > 资讯 > 后端开发 > Python >Java多线程编程详细解释
  • 163
分享到

Java多线程编程详细解释

2024-04-02 19:04:59 163人浏览 安东尼

Python 官方文档:入门教程 => 点击学习

摘要

目录一、多线程的优缺点多线程的优点:多线程的代价:二、创建java多线程1、创建Thread的子类2、实现Runnable接口三、线程安全四、java同步块五、java线程通信六、j

一、多线程的优缺点

多线程的优点:

1)资源利用率更好

2)程序设计在某些情况下更简单

3)程序响应更快

多线程的代价:

1)设计更复杂

虽然有一些多线程应用程序比单线程的应用程序要简单,但其他的一般都更复杂。在多线程访问共享数据的时候,这部分代码需要特别的注意。线程之间的交互往往非常复杂。不正确的线程同步产生的错误非常难以被发现,并且重现以修复。

2)上下文切换的开销

当CPU从执行一个线程切换到执行另外一个线程的时候,它需要先存储当前线程的本地的数据,程序指针等,然后载入另一个线程的本地数据,程序指针等,最后才开始执行。这种切换称为“上下文切换”(“context switch”)。CPU会在一个上下文中执行一个线程,然后切换到另外一个上下文中执行另外一个线程。上下文切换并不廉价。如果没有必要,应该减少上下文切换的发生。

二、创建java多线程

1、创建Thread的子类

创建Thread子类的一个实例并重写run方法,run方法会在调用start()方法之后被执行。例子如下:


public class MyThread extends Thread {
   public void run(){
     System.out.println("MyThread running");
   }
}
MyThread myThread = new MyThread();
myTread.start();

也可以如下创建一个Thread的匿名子类:


Thread thread = new Thread(){
   public void run(){
     System.out.println("Thread Running");
   }
};
thread.start();

2、实现Runnable接口

第二种编写线程执行代码的方式是新建一个实现了java.lang.Runnable接口的类的实例,实例中的方法可以被线程调用。下面给出例子:


public class MyRunnable implements Runnable {
   public void run(){
    System.out.println("MyRunnable running");
   }
}
Thread thread = new Thread(new MyRunnable());
thread.start();

同样,也可以创建一个实现了Runnable接口的匿名类,如下所示:


Runnable myRunnable = new Runnable(){
   public void run(){
     System.out.println("Runnable running");
   }
}
Thread thread = new Thread(myRunnable);
thread.start();

三、线程安全

在同一程序中运行多个线程本身不会导致问题,问题在于多个线程访问了相同的资源。如同一内存区(变量,数组,或对象)、系统(数据库WEB services等)或文件。实际上,这些问题只有在一或多个线程向这些资源做了写操作时才有可能发生,只要资源没有发生变化,多个线程读取相同的资源就是安全的。

当两个线程竞争同一资源时,如果对资源的访问顺序敏感,就称存在竞态条件。导致竞态条件发生的代码区称作临界区。

如果一个资源的创建,使用,销毁都在同一个线程内完成,且永远不会脱离该线程的控制,则该资源的使用就是线程安全的。

四、java同步块

Java中的同步块用synchronized标记。同步块在Java中是同步在某个对象上。所有同步在一个对象上的同步块在同时只能被一个线程进入并执行操作。所有其他等待进入该同步块的线程将被阻塞,直到执行该同步块中的线程退出。

有四种不同的同步块:

实例方法静态方法实例方法中的同步块静态方法中的同步块

实例方法同步:


 public synchronized void add(int value){
this.count += value;
 }

Java实例方法同步是同步在拥有该方法的对象上。这样,每个实例其方法同步都同步在不同的对象上,即该方法所属的实例。只有一个线程能够在实例方法同步块中运行。如果有多个实例存在,那么一个线程一次可以在一个实例同步块中执行操作。一个实例一个线程。

静态方法同步:


public static synchronized void add(int value){
 count += value;
 }

静态方法的同步是指同步在该方法所在的类对象上。因为在Java虚拟机中一个类只能对应一个类对象,所以同时只允许一个线程执行同一个类中的静态同步方法。

实例方法中的同步块:


public void add(int value){
    synchronized(this){
       this.count += value;
    }
  }

注意Java同步块构造器用括号将对象括起来。在上例中,使用了“this”,即为调用add方法的实例本身。在同步构造器中用括号括起来的对象叫做监视器对象。上述代码使用监视器对象同步,同步实例方法使用调用方法本身的实例作为监视器对象。一次只有一个线程能够在同步于同一个监视器对象的Java方法内执行。

下面两个例子都同步他们所调用的实例对象上,因此他们在同步的执行效果上是等效的。


public class MyClass {
   public synchronized void log1(String msg1, String msg2){
      log.writeln(msg1);
      log.writeln(msg2);
   }
   public void log2(String msg1, String msg2){
      synchronized(this){
         log.writeln(msg1);
         log.writeln(msg2);
      }
   }
 }

静态方法中的同步块:


public class MyClass {
    public static synchronized void log1(String msg1, String msg2){
       log.writeln(msg1);
       log.writeln(msg2);
    }
    public static void log2(String msg1, String msg2){
       synchronized(MyClass.class){
          log.writeln(msg1);
          log.writeln(msg2);
       }
    }
  }

这两个方法不允许同时被线程访问。如果第二个同步块不是同步在MyClass.class这个对象上。那么这两个方法可以同时被线程访问。

五、java线程通信

线程通信的目标是使线程间能够互相发送信号。另一方面,线程通信使线程能够等待其他线程的信号。

Java有一个内建的等待机制来允许线程在等待信号的时候变为非运行状态。java.lang.Object 类定义了三个方法,wait()、notify()和notifyAll()来实现这个等待机制。

一个线程一旦调用了任意对象的wait()方法,就会变为非运行状态,直到另一个线程调用了同一个对象的notify()方法。为了调用wait()或者notify(),线程必须先获得那个对象的锁。也就是说,线程必须在同步块里调用wait()或者notify()。

以下为一个使用了wait()和notify()实现的线程间通信的共享对象:


public class MyWaitNotify{
  MonitorObject myMonitorObject = new MonitorObject();
  boolean wasSignalled = false;
  public void doWait(){
    synchronized(myMonitorObject){
      while(!wasSignalled){
        try{
          myMonitorObject.wait();
         } catch(InterruptedException e){...}
      }
      //clear signal and continue running.
      wasSignalled = false;
    }
  }
  public void doNotify(){
    synchronized(myMonitorObject){
      wasSignalled = true;
      myMonitorObject.notify();
    }
  }
}

注意以下几点:

1、不管是等待线程还是唤醒线程都在同步块里调用wait()和notify()。这是强制性的!一个线程如果没有持有对象锁,将不能调用wait(),notify()或者notifyAll()。否则,会抛出IllegalMonitorStateException异常。

2、一旦线程调用了wait()方法,它就释放了所持有的监视器对象上的锁。这将允许其他线程也可以调用wait()或者notify()。

3、为了避免丢失信号,必须把它们保存在信号类里。如上面的wasSignalled变量。

4、假唤醒:由于莫名其妙的原因,线程有可能在没有调用过notify()和notifyAll()的情况下醒来。这就是所谓的假唤醒(spurious wakeups)。为了防止假唤醒,保存信号的成员变量将在一个while循环里接受检查,而不是在if表达式里。这样的一个while循环叫做自旋锁。

5、不要在字符串常量或全局对象中调用wait()。即上面MonitorObject不能是字符串常量或是全局对象。每一个MyWaitNotify的实例都拥有一个属于自己的监视器对象,而不是在空字符串上调用wait()/notify()。

六、java中的锁

自Java 5开始,java.util.concurrent.locks包中包含了一些锁的实现,因此你不用去实现自己的锁了。

常用的一些锁:

java.util.concurrent.locks.Lock;

java.util.concurrent.locks.ReentrantLock;

java.util.concurrent.locks.ReadWriteLock;

java.util.concurrent.locks.ReentrantReadWriteLock;

一个可重入锁(reentrant lock)的简单实现:


public class Lock {
    boolean isLocked = false;
    Thread  lockedBy = null;
    int lockedCount = 0;
    public synchronized void lock() throws InterruptedException{
        Thread callingThread = Thread.currentThread();
        while(isLocked && lockedBy != callingThread){
            wait();
        }
        isLocked = true;
        lockedCount++;
        lockedBy = callingThread;
    }
    public synchronized void unlock(){
        if(Thread.currentThread() == this.lockedBy){
            lockedCount--;
            if(lockedCount == 0){
                isLocked = false;
                notify();
            }
        }
    }
}

注意的一点:在finally语句中调用unlock()


lock.lock();
try{
    //do critical section code, which may throw exception
} finally {
    lock.unlock();
}

七、java中其他同步方法

信号量(Semaphore):java.util.concurrent.Semaphore

阻塞队列(Blocking Queue):java.util.concurrent.BlockingQueue


public class BlockingQueue {
    private List queue = new LinkedList();
    private int limit = 10;
    public BlockingQueue(int limit) {
        this.limit = limit;
    }
    public synchronized void enqueue(Object item) throws InterruptedException {
        while (this.queue.size() == this.limit) {
            wait();
        }
        if (this.queue.size() == 0) {
            notifyAll();
        }
        this.queue.add(item);
    }
    public synchronized Object dequeue() throws InterruptedException {
        while (this.queue.size() == 0) {
            wait();
        }
        if (this.queue.size() == this.limit) {
            notifyAll();
        }
        return this.queue.remove(0);
    }
}

八、java中的线程池

Java通过Executors提供四种线程池,分别为:

newCachedThreadPool

创建一个可缓存的线程池。如果线程池的大小超过了处理任务所需要的线程,那么就会回收部分空闲(60秒不执行任务)的线程,当任务数增加时,此线程池又可以智能的添加新线程来处理任务。此线程池不会对线程池大小做限制,线程池大小完全依赖于操作系统(或者说JVM)能够创建的最大线程大小。

newFixedThreadPool

创建固定大小的线程池。每次提交一个任务就创建一个线程,直到线程达到线程池的最大大小。线程池的大小一旦达到最大值就会保持不变,如果某个线程因为执行异常而结束,那么线程池会补充一个新线程。

newScheduledThreadPool

创建一个大小无限制的线程池。此线程池支持定时以及周期性执行任务。

newSingleThreadExecutor

创建一个单线程的线程池。此线程池支持定时以及周期性执行任务。这个线程池只有一个线程在工作,也就是相当于单线程串行执行所有任务。如果这个唯一的线程因为异常结束,那么会有一个新的线程来替代它。此线程池保证所有任务的执行顺序按照任务的提交顺序执行。

线程池简单用法:


import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Main {
    public static void main(String[] args) {
        ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
        for (int i = 0; i < 10; i++) {
            final int index = i;
            cachedThreadPool.execute(new Runnable() {
                public void run() {
                    System.out.println(index);
                }
            });
        }
    }
}

参考:

Http://tutorials.jenkov.com/java-concurrency/index.html

https://www.jb51.net/article/214633.htm

Java线程池的分析和使用://www.jb51.net/article/227397.htm

总结

本篇文章就到这里了,希望能够给你带来帮助,也希望您能够多多关注编程网的更多内容!

--结束END--

本文标题: Java多线程编程详细解释

本文链接: https://www.lsjlt.com/news/156511.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • Java多线程编程详细解释
    目录一、多线程的优缺点多线程的优点:多线程的代价:二、创建java多线程1、创建Thread的子类2、实现Runnable接口三、线程安全四、java同步块五、java线程通信六、j...
    99+
    2024-04-02
  • Kotlinserver多线程编程详细讲解
    目录service 是什么多线程编程Service 基本用法service 是什么 Service是实现程序后台运行的解决方案,适合执行非交互,后台预先的任务,即使用户打开其他应用,...
    99+
    2023-01-30
    Kotlin server多线程编程 Kotlin server Kotlin多线程编程
  • JAVA多线程详解(超详细)
    目录 一、线程简介1、进程、线程2、并发、并行、串行3、进程的三态 二、线程实现1、继承Thread类2、实现Runnable接口3、实现Callable接口(不常用) 三、线程常用方法1、线程的状态2、线程常用方法 四...
    99+
    2023-08-19
    java jvm 开发语言
  • java多线程(超详细)
    1 - 线程 1.1 - 进程 进程就是正在运行中的程序(进程是驻留在内存中的) 是系统执行资源分配和调度的独立单位 每一进程都有属于自己的存储空间和系统资源 注意:进程A和进程B的内存独立不共享。 1.2 - 线程 线程...
    99+
    2023-08-31
    jvm java intellij idea 开发语言
  • C++多线程编程详解
    目录C++多线程1. 概念1.1 概念2. 常用API1.thread2.互斥锁mutex3. 挂起和唤醒3. 应用场景3.1 call_once执行一次的函数3.2 conditi...
    99+
    2024-04-02
  • Java多线程编程综合案例详解
    目录Java多线程综合案例数字加减生产电脑竞争抢答Java多线程综合案例 数字加减 设计4个线程对象,两个线程执行减操作,两个线程执行加操作 public class ThreadD...
    99+
    2024-04-02
  • python多线程超详细详解
    python中的多线程是一个非常重要的知识点,今天为大家对多线程进行详细的说明,代码中的注释有多线程的知识点还有测试用的实例。 import threading from thr...
    99+
    2024-04-02
  • C# 多线程详细讲解
    多线程是指在一个程序中同时执行多个线程,每个线程可以独立执行不同的任务。在 C# 中,可以使用 System.Threading 命...
    99+
    2023-09-09
    C#
  • Java 并发(多线程)超详细
    Java 并发 此文章已收录至项目 Developer-Knowledge-Base 信息来源 https://www.cnblogs.com/snow-flower/p/6114765.html j...
    99+
    2023-09-06
    java 开发语言
  • java多线程编程之管道通信详解
    上一章节讲了wait/notify通信,这一节我们来探讨使用管道进行通信。 java中提供了IO流使我们很方便的对数据进行操作,pipeStream是一种特殊的流,用于不同线程间直接传送数据。一个线程将数据发送到输出管道,另一个线程从输入管...
    99+
    2023-05-30
    java 多线程 管道通信
  • C++多线程编程超详解
    目录C++多线程1. 概念2. 常用API1.thread2.互斥锁mutex3. 挂起和唤醒3. 应用场景3.1 call_once执行一次的函数3.2 condition_var...
    99+
    2024-04-02
  • Java多线程编程基石ThreadPoolExecutor示例详解
    目录前言为什么用线程池参数介绍核心线程数和最大线程数设置使用示例线程池执行任务的流程线程池执行流程图源码解读基础属性和变量execute(Runnable command)addWo...
    99+
    2023-05-16
    Java多线程ThreadPoolExecutor Java ThreadPoolExecutor
  • Java 多线程编程
    Java 多线程编程   目录 Java 多线程编程 一个线程的生命周 线程的优先级 创建一个线程 通过实现Runnable接口来创建线程 实例 通过继承Thread来创建线程 实例 Thread 方法 实例   线程的几个主要概念: 多线...
    99+
    2023-10-20
    python 开发语言
  • Java多线程与优先级详细解读
    目录1、多线程1.1多线程的基本概念1.2多线程的实现1.3继承Thread类实现多线程1.4Runnable接口实现多线程1.5Thread类和Runnable接口实现多线程的区别...
    99+
    2024-04-02
  • Java线程池详细解读
    目录1、线程池1.1 线程池概念1.2 线程池的实现2、StringBuffer类面试题:请解释String、StringBuffer、StringBuilder的区别?3、...
    99+
    2024-04-02
  • Java多线程wait()和notify()方法详细图解
    目录一、线程间等待与唤醒机制二、等待方法wait()三、唤醒方法notify()四、关于wait和notify内部等待问题(重要)五、完整代码(仅供测试用)总结一、线程间等待与唤醒机...
    99+
    2022-11-13
    多线程wait notify 线程的wait和notify 线程notify和wait区别
  • Python多线程编程入门详解
    目录一、任务、进程和线程任务进程线程进程和线程的关系二、Python既支持多进程,又支持多线程Python实现多进程Process进程类的说明Python实现多线程线程类Thread...
    99+
    2024-04-02
  • Java多线程之线程状态详解
    目录 线程状态停止线程线程休眠模拟网络延迟(放大问题的发生性)模拟计时线程礼让插队(线程强制执行)线程状态观测线程优先级守护线程总结 线程状态 五个状态:新生、就...
    99+
    2024-04-02
  • Java多线程 原子操作类详细
    目录1、What and Why2、原子更新基本类型类3、实现原理4、原子更新数组5、原子更新引用类型6、原子更新字段类1、What and Why 原子的本意是不能被分割的粒子,而...
    99+
    2024-04-02
  • Java多线程之Interrupt中断线程详解
    目录一、测试代码二、测试三、执行过程描述四、输出日志五、结论六、主要方法释义七、DEMO八、拓展程序九、实战一、测试代码 https://gitee.com/zture/sprin...
    99+
    2024-04-02
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作