第九章 避免活跃性威胁 Java并发编程实战 阅读总结

        在安全性与活跃性之间通常存在着某种制衡。我们使用加锁机制来确保线程安全, 但如果过度地使用加锁, 则可能导致锁顺序死锁(Lock-Ordering Deadlock) 。同样, 我们使用线程池和信号量来限制对资源的使用, 但这些被限制的行为可能会导致资源死锁(ResourceDeadlock)。Java应用程序无法从死锁中恢复过来, 因此在设计时一定要排除那些可能导致死锁出现的条件。本章将介绍一些导致活跃性故障的原因, 以及如何避免它们。

死锁

        经典的“ 哲学家进餐” 问题很好地描述了死锁状况。5个哲学家去吃中餐, 坐在一张圆桌旁。他们有5根筷子(而不是5双), 并且每两个人中间放一根筷子。哲学家们时而思考, 时而进餐。每个人都需要一双筷子才能吃到东西, 并在吃完后将筷子放回原处继续思考。有些筷子管理算法能够使每个入都能相对及时地吃到东西(例如一个饥饿的哲学家会尝试获得两根邻近的筷子, 但如果其中一根正在被另一个哲学家使用, 那么他将放弃已经得到的那根筷子, 并等待儿分钟之后再次尝试), 但有些算法却可能导致一些或者所有哲学家都“饿死” (每个人都立即抓住自己左边的筷子, 然后等待自己右边的筷子空出来, 但同时又不放下已经拿到的筷子)。后一种情况将产生死锁:每个人都拥有其他人需要的资源, 同时又等待其他人已经拥有的资源, 并且每个人在获得所有需要的资源之前都不会放弃已经拥有的资源

        当一个线程永远地持有一个锁, 并且其他线程都尝试获得这个锁时, 那么它们将永远被阻塞。在线程A持有锁L并想获得锁M的同时, 线程B持有锁M并尝试获得锁L, 那么这两个线程将永远地等待下去。这种情况就是最简单的死锁形式 (或者称为 “ 抱死[Deadly Embrace]"), 其中多个线程由于存在环路的锁依赖关系而永远地等待下去。(把每个线程假想 为有向图中的一个节点,图中每条边表示的关系是: “线程A等待线程B所占有的资源于”。如果在图中形成了一条环路,那么就存在一个死锁。)

        在数据库系统的设计中考虑了监测死锁以及从死锁中恢复。在执行一个事务(Transaction)时可能需要获取多个锁,并一直持有这些锁直到事务提交。因此在两个事务之间很可能发生死锁,但事实上这种情况并不多见。如果没有外部干涉,那么这些事务将永远等待下去(在某个事物中持有的锁可能在其他事务中也需要)。但数据库服务器不会让这种情况发生。当它检测到一组事务发生了死锁时(通过在表示等待关系的有向图中搜索循环),将选择一个牺牲者并放弃这个事务。作为牺牲者的事务会释放它所持有的资源,从而使其他事务继续进行。应用程序可以重新执行被强行中止的事务,而这个事务现在可以成功完成,因为所有跟它竞争资源的事务都已经完成了。

        JVM在解决死锁问题方面并没有数据库服务那样强大。当一组Java线程发生死锁时,“游戏” 将到此结束-这些线程永远不能再使用了。根据线程完成工作的不同,可能造成应用程序完全停止,或者某个特定的子系统停止,或者是性能降低。恢复应用程序的唯一方式就是中止并重启它,并希望不要再发生同样的事情。

        与许多其他的并发危险一样,死锁造成的影响很少会立即显现出来。如果一个类可能发生死锁,那么并不意味着每次都会发生死锁,而只是表示有可能。当死锁出现时,往往是在最糟糕的时候——在高负载情况下

锁顺序死锁

        程序清单10-1中的LeftRightDeadlock存在死锁风险。leftRight和rightLeft这两个方法分别获得left锁和right锁。如果一个线程调用了leftRight, 而另一个线程调用了rightLeft, 并且这两个线程的操作是交错执行,如图10-l所示,那么它们会发生死锁。



        在LeftRightDeadlock中发生死锁的原因是:两个线程试图以不同的顺序来获得相同的锁。如果按照相同的顺序来请求锁,那么就不会出现循环的加锁依赖性,因此也就不会产生死锁。如果每个需要锁L和锁M的线程都以相同的顺序来获取L和M, 那么就不会发生死锁了。

        如果所有线程以固定的顺序来获得锁,那么在程序中就不会出现锁顺序死锁问题。

        要想验证锁顺序的一致性, 需要对程序中的加锁行为进行全局分析。 如果只是单独地分析 每条获取多个锁的代码路径, 那是不够的; leftRight和rightLeft都采用了 “合理的” 方式来获 得锁,它们只是不能相互兼容。 当需要加锁时, 它们需要知道彼此正在执行什么操作。


动态的锁顺序死锁

        有时候, 并不能清楚地知道是否在锁顺序上有足够的控制权来避免死锁的发生。 考虑程序清单10-2中看似无害的代码, 它将资金从一个账户转入另一个账户。 在开始转账之前, 首先要 获得这两个 Account 对象的锁, 以确保通过原子方式来更新两个账户中的余额, 同时又不破坏一些不变性条件, 例如 “账户的余额不能为负数”。


        在transferMoney中如何发生死锁?所有的线程似乎都是按照相同的顺序来获得锁, 但事实上锁的顺序取决于传递给transferMoney的参数顺序, 而这些参数顺序又取决于外部输入。 如果两个线程同时调用transferMoney, 其中一个线程从X向Y转账, 另一个线程从Y向X转账, 那么就会发生死锁:

        如果执行时序不当, 那么A可能获得 myAccount的锁并等待yourAccount的锁, 然而B此时持有yourAccount的锁, 并正在等待myAccount的锁。

        这种死锁可以采用程序清单10-1中的方法来检查——查看是否存在嵌套的锁获取操作。 由于我们无法控制参数的顺序, 因此要解决这个问题, 必须定义锁的顺序, 并在整个应用程序中都按照这个顺序来获取锁。

        在制定锁的顺序时, 可以使用System.identityHashCode方法, 该方法将返回由Object. hashCode返回的值。 程序清单1 0-3给出了另一个版本的transferMoney, 在该版本中使用了System.identityHashCode来定义锁的顺序。虽然增加了 些新的代码, 但却消除了发生死锁的可能性。


        在极少数情况下, 两个对象可能拥有相同的散列值, 此时必须通过某种任意的方法来决定锁的顺序, 而这可能又会重新引入死锁。 为了避免这种情况, 可以使用 “加时赛(Tie­Breaking)"锁。在获得两个Account锁之前, 首先获得这个 “加时赛” 锁, 从而保证每次只有 一个线程以未知的顺序获得这两个锁, 从而消除了死锁发生的可能性(只要一致地使用这种机制)。 如果经常会出现散列冲突的情况, 那么这种技术可能会成为并发性的一个瓶颈(这类似千在整个程序中只有一个锁的情况), 但由于System.identityHashCode中出现散列冲突的频率非常低, 因此这项技术以最小的代价,换来了最大的安全性。

        如果在Account中包含一个唯一的、不可变的, 并且具备可比性的键值, 例如账号, 那么 要制定锁的顺序就更加容易了:通过键值对对象进行排序, 因而不需要使用 “加时赛” 锁。

        你或许认为我有些夸大了死锁的风险, 因为锁被持有的时间通常很短暂, 然而在真实统中, 死锁往往都是很严重的问题。 作为商业产品的应用程序每天可能要执行数十亿次获取 锁-释放锁的操作。 只要在这数十亿次操作中有一次发生了错误, 就可能导致程序发生死锁, 并且即使应用程序通过了压力测试也不可能找出所有潜在的死锁。

在协作对象之间发生的死锁

        某些获取多个锁的操作并不像在 LeftRightDeadlock 或 transferMoney 中那么明显, 这两个锁并不一定必须在同一个方法中被获取。 考虑程序清单 10-5 中两个相互协作的类, 在出租车调度系统中可能会用到它们。 Taxi 代表一个出租车对象, 包含位置和目的地两个属性, Dispatcher代表一个出租车车队。


        尽管没有任何方法会显式地获取两个锁,但 setLocation 和 getlmage 等方法的调用者都会 获得两个锁。如果一个线程在收到 GPS 接收器的更新事件时调用 setLocation, 那么它将首先 更新出租车的位置,然后判断它是否到达了目的地。如果已经到达,它会通知 Dispatcher : 它需要一个新的目的地。因为setLocation 和notifyAvailable 都是同步方法,因此调用setLocation的线程将首先获取Taxi 的锁,然后获取Dispatcher 的锁。同样,调用getlmage 的线程将首先获取Dispatcher 锁,然后再获取每一个Taxi 的锁(每次获取一个)。这与LeftRightDeadlock 中的情况相同,两个线程按照不同的顺序来获取两个锁,因此就可能产生死锁。

        在LeftRightDeadlock 或transferMoney 中,要查找死锁是比较简单的,只需要找出那些需要获取两个锁的方法。然而要在Taxi 和Dispatcher 中查找死锁则比较困难:如果在持有锁的情况下调用某个外部方法,那么就需要警惕死锁。

        如果在持有锁时调用某个外部方法,那么将出现活跃性问题。在这个外部方法中可能会获取其他锁(这可能会产生死锁),或者阻塞时间过长,导致其他线程无法及时获得当前被持有的锁。

开放调用

        当然,Taxi 和Dispatcher 并不知道它们将要陷入死锁,况且它们本来就不应该知道。方法调用相当于一种抽象屏障,因而你无须了解在被调用方法中所执行的操作。但也正是由于不知道在被调用方法中执行的操作,因此在持有锁的时候对调用某个外部方法将难以进行分析,从而可能出现死锁。

        如果在调用某个方法时不需要持有锁,那么这种调用被称为开放调用(Open Call) 。依赖于开放调用的类通常能表现出更好的行为,井且与那些在调用方法时需要持有锁的类相比,也更易于编写。这种通过开放调用来避免死锁的方法,类似于采用封装机制来提供线程安全的方法:虽然在没有封装的情况下也能确保构建线程安全的程序,但对一个使用了封装的程序进行线程安全分析,要比分析没有使用封装的程序容易得多。同理,分析一个完全依赖于开放调用的程序的活跃性,要比分析那些不依赖开放调用的程序的活跃性简单。通过尽可能地使用开放调用,将更易千找出那些需要获取多个锁的代码路径,因此也就更容易确保采用致的顺序来获得锁。

        可以很容易地将程序清单 10-5 中的 Taxi 和 Dispatcher 修改为使用开放调用, 从而消除发生死锁的风险。 这需要使同步代码块仅被用于保护那些涉及共享状态的操作, 如程序清单 10-6 所示。 通常, 如果只是为了语法紧凑或简单性(而不是因为整个方法必须通过一个锁 来保护)而使用同步方法(而不是同步代码块), 那么就会导致程序清单 10-5 中的问题。(此外, 收缩同步代码块的保护范围还可以提高可伸缩性, 在 ll.4.1 节中给出了如何确定同步代码块大小的方法。)



        在程序中应尽量使用开放调用。与那些在持有锁时调用外部方法的程序相比,更易于对依赖于开放调用的程序进行死锁分析。

        有时候,在重新编写同步代码块以使用开放调用时会产生意想不到的结果,因为这会使得某个原子操作变为非原子操作。在许多情况下,使某个操作失去原子性是可以接受的。例如, 对于两个操作:更新出租车位置以及通知调度程序这辆出租车已准备好出发去一个新的目的地, 这两个操作并不需要实现为一个原子操作。在其他情况中,虽然去掉原子性可能会出现一些值得注意的结果,但这种语义变化仍然是可以接受的。在容易产生死锁的版本中,getlmage会生 成某个时刻下的整个车队位置的完整快照,而在重新改写的版本中,getlmage将获得每辆出租 车不同时刻的位置。

        然而,在某些情况下,丢失原子性会引发错误,此时需要通过另一种技术来实现原子性。 例如,在构造一个并发对象时,使得每次只有单个线程执行使用了开放调用的代码路径。例 如,在关闭某个服务时,你可能希望所有正在运行的操作执行完成以后,再释放这些服务占用 的资源。如果在等待操作完成的同时持有该服务的锁,那么将很容易导致死锁,但如果在服务关闭之前就释放服务的锁,则可能导致其他线程开始新的操作。这个问题的解决方法是,在将 服务的状态更新为 “关闭 ” 之前一直持有锁,这样其他想要开始新操作的线程, 包括想关闭该服务的其他线程,会发现服务已经不可用,因此也就不会试图开始新的操作。然后, 你可以等待关闭操作结束, 并且知道当开放调用完成后, 只有执行关闭操作的线程才能访问服务的状态。因此,这项技术依赖于构造一些协议(而不是通过加锁 )来防止其他线程进入代码 的临界区。

资源死锁

        正如当多个线程相互持有彼此正在等待的锁而又不释放自己已持有的锁时会发生死锁, 当它们在相同的资源集合上等待时,也会发生死锁。

        假设有两个资源池,例如两个不同数据库的连接池。资源池通常采用信号量来实现当资源池为空时的阻塞行为。如果一个任务需要连接两个数据库,并且在请求这 两个资源时不会始终遵循相同的顺序, 那么线程A可能持有与数据库D1 的连接,并等待与数据库D2的连接,而线程B则持有与D2的连接并等待与D1的连接。(资源池越大,出现这种情况的可能性就越小。如果每个资源池都有N个连接,那么在发生死锁时不仅需要N个循环等待的线程,而且还需要大量不恰当的执行时序。)

        另一种基于资源的死锁形式就是线程饥饿死锁(Thread-Starva tionDeadlock): 一个任务提交另一个任务,并等待被提交任务在单线程的Execut执行完成。这种情况下,第一个任务将永远等待下去,并使得另一个任务以及在这个Executor中执行的所有其他任务都停止执行。如果某些任务需要等待其他任务的结果,那么这些任务往往是产生线程饥饿死锁的主要来源, 有界线程池/资源池与相互依赖的任务不能一起使用。



死锁的避免与诊断

        如果一个程序每次至多只能获得一个锁, 那么就不会产生锁顺序死锁当然, 这种情况通常并不现实, 但如果能够避免这种情况, 那么就能省去很多工作。如果必须获取多个锁, 那么在设计时必须考虑锁的顺序:尽量减少潜在的加锁交互数最, 将获取锁时需要遵循的协议写入正式文档并始终遵循这些协议。

        在使用细粒度锁的程序中, 可以通过使用一种两阶段策略(Two-Part Strategy) 来检查代码中的死锁:首先, 找出在什么地方将获取多个锁(使这个集合尽量小), 然后对所有这些实例进行全局分析, 从而确保它们在整个程序中获取锁的顺序都保持一致。尽可能地使用开放调用, 这能极大地简化分析过程。如果所有的调用都是开放调用, 那么要发现获取多个锁的实例是非常简单的, 可以通过代码审查, 或者借助自动化的源代码分析工具。

支持定时的锁

        还有一项技术可以检测死锁和从死锁中恢复过来, 即显式使用Lock 类中的定时tryLock 功能来代替内置锁机制。当使用内置锁时, 只要没有获得锁, 就会永远等待下去, 而显式锁则可以指定一个超时时限( Timeout), 在等待超过该时间后tryLock 会返回一个失败信息。如果超时时限比获取锁的时间要长很多, 那么就可以在发生某个意外情况后重新获得控制权。(在程序清单13-3 中给出了transferMoney的另一种实现, 其中使用了一种轮询的tryLock 消除了死锁发生的可能性。)

        当定时锁失败时, 你并不需要知道失败的原因。或许是因为发生了死锁, 或许某个线程在持有锁时错误地进入了无限循环, 还可能是某个操作的执行时间远远超过了你的预期。然而,至少你能记录所发生的失败, 以及关于这次操作的其他有用信息, 并通过一种更平缓的方式来重新启动计算, 而不是关闭整个进程。

        即使在整个系统中没有始终使用定时锁, 使用定时锁来获取多个锁也能有效地应对死锁问题。如果在获取锁时超时, 那么可以释放这个锁, 然后后退并在一段时间后再次尝试, 从而消除了死锁发生的条件, 使程序恢复过来。(这项技术只有在同时获取两个锁时才有效, 如果在嵌套的方法调用中请求多个锁, 那么即使你知道已经持有了外层的锁, 也无法释放它。

通过线程转储信息来分析死锁

        虽然防止死锁的主要责任在于你自己, 但JVM 仍然通过线程转储(Thread Dump) 来帮助识别死锁的发生线程转储包括各个运行中的线程的栈追踪信息, 这类似于发生异常时的栈追踪信息。线程转储还包含加锁信息, 例如每个线程持有了哪些锁, 在哪些栈帧中获得这些锁, 以及被阻塞的线程正在等待获取哪一个锁。在生成线程转储之前, JVM 将在等待关系图中通过搜索循环来找出死锁。如果发现了一个死锁, 则获取相应的死锁信息, 例如在死锁中涉及哪些 锁和线程,以及这个锁的获取操作位于程序的哪些位置。

        要在 UNIX 平台上触发线程转储操作, 可以通过向JVM的进程发送 SIGQUIT信号(kiII -3),或者在 UNIX平台中按下Ctrl-\键, 在Windows平台中按下CtrI-Break键。在许多 IDE (集成开发环境)中都可以请求线程转储。

        如果使用显式的Lock类而不是内部锁, 那么Java 5.0并不支持与Lock相关的转储信息, 在线程转储中不会出现显式的Lock。 虽然Java 6 中包含对显式Lock的线程转储和死锁检测等的支持,但在这些锁上获得的信息比在内置锁上获得的信息精确度低。内置锁与获得它们所在 的线程栈帧是相关联的, 而显式的Lock只与获得它的线程相关联。

        诊断死锁时,JVM可以帮我们做许多工作一—哪些锁导致了这个问题, 涉及哪些线程,它们持有哪些其他的锁,以及是否间接地给其他线程带来了不利影响。其中一个线程持有MumbleDBConnection上的锁, 并等待获得 MumbleDBCallableStatement上的锁, 而另一个线程则持有MumbleDBCallableStatement上的锁, 并等待MumbleDBConnection上的锁。


其他活跃性危险

        尽管死锁是最常见的活跃性危险, 但在并发程序中还存在一些其他的活跃性危险, 包括: 饥饿、 丢失信号和活锁等。

饥饿

        当线程由于无法访问它所需要的资源而不能继续执行时, 就发生了“ 饥饿(Starvation)"。引发饥饿的最常见资源就是CPU时钟周期。如果在Java应用程序中对线程的优先级使用不当,或者在持有锁时执行一些无法结束的结构(例如无限循环, 或者无限制地等待某个资源), 那么也可能导致饥饿, 因为其他需要这个锁的线程将无法得到它。

        在Thread API中定义的线程优先级只是作为线程调度的参考。在Thread API中定义了10个优先级,JVM根据需要将它们映射到操作系统的调度优先级。这种映射是与特定平台相关的, 因此在某个操作系统中两个不同的Java优先级可能被映射到同一个优先级, 而在另一个操作系统中则可能被映射到另一个不同的优先级。在某些操作系统中, 如果优先圾的数量少于10个, 那么有多个Java优先级会被映射到同一个优先级。

        操作系统的线程调度器会尽力提供公平的、活跃性良好的调度, 甚至超出Java语言规范的需求范围。在大多数Java应用程序中, 所有线程都具有相同的优先级Thread.NORM_PRIORITY。线程优先级并不是一种直观的机制, 而通过修改线程优先级所带来的效果通常也不明显。当提高某个线程的优先级时, 可能不会起到任何作用, 或者也可能使得某个线程的调度优先级高于其他线程, 从而导致饥饿。

        通常, 我们尽量不要改变线程的优先级。只要改变了线程的优先级, 程序的行为就将与平台相关, 并且会导致发生饥饿问题的风险。你经常能发现某个程序会在一些奇怪的地方调用Thread.sleep或Thread.yield, 这是因为该程序试图克服优先级调整问题或响应性问题, 并试图让低优先级的线程执行更多的时间。

        要避免使用线程优先级,因为这会增加平台依赖性,并可能导致活跃性问题。在大多数并发应用程序中,都可以使用默认的线程优先级

糟糕的响应性

        除饥饿以外的另一个问题是糟糕的响应性,如果在GUI应用程序中使用了后台线程,那么这种问题是很常见的。在第9章中开发了一个框架, 并把运行时间较长的任务放到后台线程中 运行, 从而不会使用户界面失去响应。 但CPU密集型的后台任务仍然可能对响应性造成影响,因为它们会与事件线程共同竞争CPU的时钟周期。 在这种情况下就可以发挥线程优先级的作用, 此时计算密集型的后台任务将对响应性造成影响。 如果由其他线程完成的工作都是后台任务,那么应该降低它们的优先级,从而提高前台程序的响应性。

        不良的锁管理也可能导致糟糕的响应性。 如果某个线程长时间占有一个锁(或许正在对一 个大容器进行迭代, 并且对每个元素进行计算密集的处理),而其他想要访问这个容器的线程 就必须等待很长时间。

活锁

        活锁(Livelock)是另 一种形式的活跃性问题, 该问题尽管不会阻塞线程, 但也不能继续执行,因为线程将不断重复执行相同的操作, 而且总会失败。 活锁通常发生在处理事务消息的应 用程序中:如果不能成功地处理某个消息, 那么消息处理机制将回滚整个事务, 并将它重新放 到队列的开头。 如果消息处理器在处理某种特定类型的消息时存在错误并导致它失败, 那么每 当这个消息从队列中取出井传递到存在错误的处理器时, 都会发生事务回滚。 由于这条消息又被放回到队列开头, 因此处理器将被反复调用, 并返回相同的结果。(有时候也被称为毒药消 息,Poison Message。)虽然处理消息的线程并没有阴塞, 但也无法继续执行下去。 这种形式 的活锁通常是由过度的错误恢复代码造成的, 因为它错误地将不可修复的错误作为可修复的 错误。

        当多个相互协作的线程都对彼此进行响应从而修改各自的状态, 并使得任何一个线程都无法继续执行时, 就发生了活锁。 这就像两个过于礼貌的人在半路上面对面地相遇:他们彼此都让出对方的路,然而又在另一条路上相遇了。 因此他们就这样反复地避让下去。

        要解决这种活锁问题, 需要在重试机制中引入随机性。 例如, 在网络上, 如果两台机器 尝试使用相同的载波来发送数据包, 那么这些数据包就会发生冲突。 这两台机器都检查到了冲 突,并都在稍后再次重发。 如果二者都选择了在1秒钟后重试,那么它们又会发生冲突, 并且不断地冲突下去,因而即使有大量闲置的带宽,也无法使数据包发送出去。 为了避免这种情况 发生,需要让它们分别等待一段随机的时间。(以太协议定义了在重复发生冲突时采用指数方 式回退机制, 从而降低在多台存在冲突的机器之间发生拥塞和反复失败的风险。) 在并发应用 程序中, 通过等待随机长度的时间和回退可以有效地避免活锁的发生。

小结

        活跃性故障是一个非常严重的问题, 因为当出现活跃性故障时, 除了中止应用程序之外没有其他任何机制可以帮助从这种故障时恢复过来。 最常见的活跃性故障就是锁顺序死锁。 设计时应该避免产生锁顺序死锁: 确保线程在获取多个锁时采用一致的顺序。 最好的解决方法是在程序中始终使用开放调用。这将大大减少需要同时持有多个锁的地方,也更容易发现这些地方。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在现有省、市港口信息化系统进行有效整合基础上,借鉴新 一代的感知-传输-应用技术体系,实现对码头、船舶、货物、重 大危险源、危险货物装卸过程、航管航运等管理要素的全面感知、 有效传输和按需定制服务,为行政管理人员和相关单位及人员提 供高效的管理辅助,并为公众提供便捷、实时的水运信息服务。 建立信息整合、交换和共享机制,建立健全信息化管理支撑 体系,以及相关标准规范和安全保障体系;按照“绿色循环低碳” 交通的要求,搭建高效、弹性、高可扩展性的基于虚拟技术的信 息基础设施,支撑信息平台低成本运行,实现电子政务建设和服务模式的转变。 实现以感知港口、感知船舶、感知货物为手段,以港航智能 分析、科学决策、高效服务为目的和核心理念,构建“智慧港口”的发展体系。 结合“智慧港口”相关业务工作特点及信息化现状的实际情况,本项目具体建设目标为: 一张图(即GIS 地理信息服务平台) 在建设岸线、港口、港区、码头、泊位等港口主要基础资源图层上,建设GIS 地理信息服务平台,在此基础上依次接入和叠加规划建设、经营、安全、航管等相关业务应用专题数据,并叠 加动态数据,如 AIS/GPS/移动平台数据,逐步建成航运管理处 "一张图"。系统支持扩展框架,方便未来更多应用资源的逐步整合。 现场执法监管系统 基于港口(航管)执法基地建设规划,依托统一的执法区域 管理和数字化监控平台,通过加强对辖区内的监控,结合移动平 台,形成完整的多维路径和信息追踪,真正做到问题能发现、事态能控制、突发问题能解决。 运行监测和辅助决策系统 对区域港口与航运业务日常所需填报及监测的数据经过科 学归纳及分析,采用统一平台,消除重复的填报数据,进行企业 输入和自动录入,并进行系统智能判断,避免填入错误的数据, 输入的数据经过智能组合,自动生成各业务部门所需的数据报 表,包括字段、格式,都可以根据需要进行定制,同时满足扩展 性需要,当有新的业务监测数据表需要产生时,系统将分析新的 需求,将所需字段融合进入日常监测和决策辅助平台的统一平台中,并生成新的所需业务数据监测及决策表。 综合指挥调度系统 建设以港航应急指挥中心为枢纽,以各级管理部门和经营港 口企业为节点,快速调度、信息共享的通信网络,满足应急处置中所需要的信息采集、指挥调度和过程监控等通信保障任务。 设计思路 根据项目的建设目标和“智慧港口”信息化平台的总体框架、 设计思路、建设内容及保障措施,围绕业务协同、信息共享,充 分考虑各航运(港政)管理处内部管理的需求,平台采用“全面 整合、重点补充、突出共享、逐步完善”策略,加强重点区域或 运输通道交通基础设施、运载装备、运行环境的监测监控,完善 运行协调、应急处置通信手段,促进跨区域、跨部门信息共享和业务协同。 以“统筹协调、综合监管”为目标,以提供综合、动态、实 时、准确、实用的安全畅通和应急数据共享为核心,围绕“保畅通、抓安全、促应急"等实际需求来建设智慧港口信息化平台。 系统充分整合和利用航运管理处现有相关信息资源,以地理 信息技术、网络视频技术、互联网技术、移动通信技术、云计算 技术为支撑,结合航运管理处专网与行业数据交换平台,构建航 运管理处与各部门之间智慧、畅通、安全、高效、绿色低碳的智 慧港口信息化平台。 系统充分考虑航运管理处安全法规及安全职责今后的变化 与发展趋势,应用目前主流的、成熟的应用技术,内联外引,优势互补,使系统建设具备良好的开放性、扩展性、可维护性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值