首页 文章 精选 留言 我的

精选列表

搜索[高并发],共10000篇文章
优秀的个人博客,低调大师

Java并发编程 -- 手动实现可重入Lock

Lock就像synchronized块一样是一个线程同步机制。 然而,Lock定比synchronized更灵活、更复杂。 Lock和synchronized块 的区别 同步块不保证等待输入它的线程被授予访问权限的顺序。 不能将任何参数传递给同步块的条目。 同步块必须完全包含在单个方法中。 一个Lock可以在不同的方法中调用lock()和unlock()。 简单例子 Lock lock = new ReentrantLock(); lock.lock(); //要保证线程安全的代码 lock.unlock(); 其中,你应该能够猜到,lock() 方法是加锁,unlock()方法是解锁。 Lock接口含有的方法 lock() lockInterruptibly() tryLock() tryLock(long timeout, TimeUnit timeUnit) unlock() lock()方法锁定Lock实例。 如果锁定实例已被锁定,则线程调用锁定()将被锁定,直到解锁锁定。 lockInterruptibly()方法锁定Lock,除非调用该方法的线程已被中断。如果一个线程被阻塞,等待通过此方法锁定Lock,该线程将被中断,并退出此方法调用。(获取锁的时候可以被中断) tryLock()方法立即尝试锁定Lock实例。 如果锁定成功则返回true;如果Lock已经被锁定,则返回false。 这个方法永远不会阻塞 tryLock(long timeout,TimeUnit timeUnit)的工作方式与tryLock()方法相似,只是它对超时时间有所规定。 unlock()方法解锁Lock实例。 通常,Lock实现将只允许已锁定Lock的线程调用此方法。 调用此方法的其他线程可能会导致未经检查的异常(RuntimeException)。 ReentrantLock实例 ReentrantLock 可重入锁,是Lock的一个子类。我们这里来使用它实现线程安全编程。 package com.lock; import com.thread.security.Task; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; /** * 重入锁 * Created by Fant.J. * 2018/3/6 20:09 */ public class ReentrantLockTest { public int value = 0; //实例化重入锁锁 Lock lock = new ReentrantLock(); public int getValue() { //加锁 lock.lock(); int a = value++; //消除锁 lock.unlock(); return a; } public static void main(String[] args) { ReentrantLockTest task = new ReentrantLockTest(); new Thread(){ @Override public void run() { while (true) { System.out.println(Thread.currentThread().getName() + " " + task.getValue()); try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } } } }.start(); new Thread(){ @Override public void run() { while (true) { System.out.println(Thread.currentThread().getName() + " " + task.getValue()); try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } } } }.start(); } } 手写自己的Lock实现类 如果有特殊业务需求,我们也可以重写Lock接口,来打造一个自己的lock锁。 package com.lock; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; /** * Created by Fant.J. * 2018/3/6 20:12 */ public class MyLock implements Lock { //声明一个判断锁的布尔值 private boolean isLocked = false; /** * 必须声明 synchronized 原自行操作,不然jvm不会识别是哪个线程的wait方法,notify也一样 */ @Override public synchronized void lock() { while (isLocked){ try { wait(); } catch (InterruptedException e) { e.printStackTrace(); } } isLocked = true; } @Override public synchronized void unlock() { isLocked = false; notify(); } @Override public void lockInterruptibly() throws InterruptedException { } @Override public boolean tryLock() { return false; } @Override public boolean tryLock(long time, TimeUnit unit) throws InterruptedException { return false; } @Override public Condition newCondition() { return null; } } 然后我们做测试 package com.lock; /** * Created by Fant.J. * 2018/3/6 20:24 */ public class MyLockTest { public int value = 0; MyLock myLock = new MyLock(); public int getValue(){ myLock.lock(); value++; myLock.unlock(); return value; } public static void main(String[] args) { MyLockTest task = new MyLockTest(); new Thread(){ @Override public void run() { while (true) { System.out.println(Thread.currentThread().getName() + " " + task.getValue()); try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } } } }.start(); new Thread(){ @Override public void run() { while (true) { System.out.println(Thread.currentThread().getName() + " " + task.getValue()); try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } } } }.start(); } } 结果没有出现线程安全问题,这里不做截图了,自己可以试试。但是我们写的方法还有一定的问题,就是MyLock这个类不支持 可重入锁,意思就是如果有两个锁嵌套,如果相同的线程先调用a方法,再调用带锁的b方法,则就会进入自旋锁。 测试方法源码 package com.lock; /** * Created by Fant.J. * 2018/3/6 20:24 */ public class MyLockTest2 { public int value = 0; MyLock myLock = new MyLock(); public void a(){ myLock.lock(); System.out.println("a"); b(); myLock.unlock(); } public void b(){ myLock.lock(); System.out.println("b"); myLock.unlock(); } public static void main(String[] args) { MyLockTest2 task = new MyLockTest2(); new Thread(){ @Override public void run() { task.a(); } }.start(); new Thread(){ @Override public void run() { task.a(); } }.start(); } } 执行该方法后,我们会发现,线程停止在打印出"a"后,一直在等待。这就是因为该锁不是可重入锁。 可重入锁的设计 我在这里只贴和上面代码不同的部分。 public class MyLock implements Lock { //声明一个判断锁的布尔值 private boolean isLocked = false; Thread lockBy = null; int lockCount = 0; @Override public synchronized void lock() { Thread currentThread = Thread.currentThread(); //获取到当前线程 while (isLocked && currentThread != lockBy){ try { wait(); } catch (InterruptedException e) { e.printStackTrace(); } } isLocked = true; lockBy = currentThread; //将currentThread线程指向 lockBy线程 lockCount++;//计数器自增 } @Override public synchronized void unlock() { if (lockBy == Thread.currentThread()){ lockCount--; if (lockCount ==0 ){ notify(); isLocked = false; } } } } 第一个线程执行a()方法,得到了锁,使lockedBy等于当前线程,也就是说,执行的这个方法的线程获得了这个锁,执行add()方法时,同样要先获得锁,因不满足while循环的条件,也就是不等待,继续进行,将此时的lockedCount变量,也就是当前获得锁的数量加一,当释放了所有的锁,才执行notify()。如果在执行这个方法时,有第二个线程想要执行这个方法,因为lockedBy不等于第二个线程,导致这个线程进入了循环,也就是等待,不断执行wait()方法。只有当第一个线程释放了所有的锁,执行了notify()方法,第二个线程才得以跳出循环,继续执行。

优秀的个人博客,低调大师

【总结】Spark优化(1)-多Job并发执行

Spark程序中一个Job的触发是通过一个Action算子,比如count(), saveAsTextFile()等 在这次Spark优化测试中,从Hive中读取数据,将其另外保存四份,其中两个Job采用串行方式,另外两个Job采用并行方式。将任务提交到Yarn中执行。能够明显看出串行与兵线处理的性能。 每个Job执行时间: JobID 开始时间 结束时间 耗时 Job 0 16:59:45 17:00:34 49s Job 1 17:00:34 17:01:13 39s Job 2 17:01:15 17:01:55 40s Job 3 17:01:16 17:02:12 56s 四个Job都是自执行相同操作,Job0,Job1一组采用串行方式,Job2,Job3采用并行方式。 Job0,Job1串行方式耗时等于两个Job耗时之和 49s+39s=88s Job2,Job3并行方式耗时等于最先开始和最后结束时间只差17:02:12-17:01:15=57s 代码: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 package com.cn.ctripotb; import org.apache.spark.SparkConf; import org.apache.spark.api.java.JavaSparkContext; import org.apache.spark.sql.DataFrame; import org.apache.spark.sql.hive.HiveContext; import java.util.*; import java.util.concurrent.Callable; import java.util.concurrent.Executors; /** *CreatedbyAdministratoron2016/9/12. */ public class HotelTest{ static ResourceBundlerb=ResourceBundle.getBundle( "filepath" ); public static void main(String[]args){ SparkConfconf= new SparkConf() .setAppName( "MultiJobWithThread" ) .set( "spark.serializer" , "org.apache.spark.serializer.KryoSerializer" ); JavaSparkContextsc= new JavaSparkContext(conf); HiveContexthiveContext= new HiveContext(sc.sc()); //测试真实数据时要把这里放开 final DataFramedf=getHotelInfo(hiveContext); //没有多线程处理的情况,连续执行两个Action操作,生成两个Job df.rdd().saveAsTextFile(rb.getString( "hdfspath" )+ "/file1" ,com.hadoop.compression.lzo.LzopCodec. class ); df.rdd().saveAsTextFile(rb.getString( "hdfspath" )+ "/file2" ,com.hadoop.compression.lzo.LzopCodec. class ); //用Executor实现多线程方式处理Job java.util.concurrent.ExecutorServiceexecutorService=Executors.newFixedThreadPool( 2 ); executorService.submit( new Callable<Void>(){ @Override public Voidcall(){ df.rdd().saveAsTextFile(rb.getString( "hdfspath" )+ "/file3" ,com.hadoop.compression.lzo.LzopCodec. class ); return null ; } }); executorService.submit( new Callable<Void>(){ @Override public Voidcall(){ df.rdd().saveAsTextFile(rb.getString( "hdfspath" )+ "/file4" ,com.hadoop.compression.lzo.LzopCodec. class ); return null ; } }); executorService.shutdown(); } public static DataFramegetHotelInfo(HiveContexthiveContext){ Stringsql= "select*fromcommon.dict_hotel_ol" ; return hiveContext.sql(sql); } } 本文转自巧克力黒 51CTO博客,原文链接:http://blog.51cto.com/10120275/1961130 ,如需转载请自行联系原作者

优秀的个人博客,低调大师

iOS: 并发编程的几个知识点

iOS 多线程问题 查阅的大部分资料都是英文的,整理完毕之后,想翻译成中文,却发现很多名字翻译成中文很难表述清楚。 所以直接把整理好的资料发出来,大家就当顺便学习学习英语。 1. Thread Safe Vs Main Thread Safe Main Thread Safe means only safe execute on main thread; Thread Safe means you can modify on any thread simultaneously; 2. ConditionLock Vs Condition NSCondition A condition variable whose semantics follow those used for POSIX-style conditions. A condition is another type of semaphore that allows threads to signal each other when a certain condition is true.Conditions are typically used to indicate the availability of a resource or to ensure that tasks are performed in a specific order.When a thread tests a condition, it blocks unless that condition is already true. It remains blocked until some other thread explicitly changes and signals the condition. The difference between acondition and a mutex lock is that multiple threads may be permitted access to the condition at the same time.The condition is more of a gatekeeper that lets different threads through the gate depending on some specified criteria. Due to the subtleties involved in implementing operating systems, condition locks are permitted to return with spurious success even if they were not actually signaled by your code.To avoid problems caused by these spurious signals, you should always use a predicate in conjunction with your condition lock. When a thread waits on a condition, the condition object unlocks its lock and blocks the thread. When the condition is signaled, the system wakes up the thread. The condition object then reacquires its lock before returning from thewaitorwaitUntilDate:method. Thus, from the point of view of the thread, it is as if it always held the lock. A boolean predicate is an important part of the semantics of using conditions because of the way signaling works. Signaling a condition does not guarantee that the condition itself is true.Using a predicate ensures that these spurious signals do not cause you to perform work before it is safe to do so. The predicate itself is simply a flag or other variable in your code that you test in order to acquire a Boolean result. Thesemanticsfor using anNSConditionobject is as follows: Lock the condition object. Test a boolean predicate. (This predicate is a boolean flag or other variable in your code that indicates whether it is safe to perform the task protected by the condition.) If the boolean predicate is false, call the condition object’swaitorwaitUntilDate:method to block the thread. Upon returning from these methods, go to step 2 to retest your boolean predicate. (Continue waiting and retesting the predicate until it is true.) If the boolean predicate is true, perform the task. Optionally update any predicates (or signal any conditions) affected by your task. When your task is done, unlock the condition object. lock the condition while (!(boolean_predicate)) { wait on condition } do protected work (optionally, signal or broadcast the condition again or change a predicate value) unlock the condition NSCondition的底层是通过pthread_mutex + pthread_cond_t来实现的。 NSConditionLock A lock that can be associated with specific, user-defined conditions. Using anNSConditionLockobject, you can ensure that a thread can acquire a lockonly if a certain condition is met. AnNSConditionLockobject defines a mutex lock that can be locked and unlocked with specific values. NSConditionLock just support condition with a int, if you want support a custom condition value, you should use NSCondition. 用互斥所能不能实现生产者,消费者模型???答案是: YES 参考资料: https://web.stanford.edu/class/cs140/cgi-bin/lecture.php?topic=locks http://blog.ibireme.com/2016/01/16/spinlock_is_unsafe_in_ios/ https://bestswifter.com/ios-lock/ 3. @synchronized Directive The object passed to the @synchronized directive is aunique identifierused to distinguish the protected block. If you execute the preceding method in two different threads, passing a different object for theanObjparameter on each thread, each would take its lock and continue processing without being blocked by the other. If you pass the same object in both cases, however, one of the threads would acquire the lock first and the other would block until the first thread completed the critical section. Several Common ways to use @synchronized wrong @synchronized(nil) @synchronized(][NSObject all] init]) Exceptions With @synchronized As a precautionary measure, the@synchronizedblock implicitly adds an exception handler to the protected code.This handler automatically releases the mutex in the event that an exception is thrown. This means that in order to use the@synchronizeddirective, you must also enable Objective-C exception handling in your code. If you do not want the additional overhead caused by the implicit exception handler, you should consider using the lock classes. 原理 OBJC_EXPORT int objc_sync_enter(id obj) OBJC_AVAILABLE(10.3, 2.0, 9.0, 1.0); OBJC_EXPORT int objc_sync_exit(id obj) OBJC_AVAILABLE(10.3, 2.0, 9.0, 1.0); @synchronized(obj) { // do work } 会被编译器转换为: @try { objc_sync_enter(obj); // do work } @finally { objc_sync_exit(obj); } Example 结论: 你调用sychronized的每个对象,Objective-C runtime 都会为其分配一个递归锁并存储在哈希表中。 如果在sychronized内部对象被释放或被设为nil看起来都 OK。 注意不要向你的sychronizedblock 传入nil!这将会从代码中移走线程安全。 参考资料 http://rykap.com/objective-c/2015/05/09/synchronized/ http://yulingtianxia.com/blog/2015/11/01/More-than-you-want-to-know-about-synchronized/ https://opensource.apple.com/source/objc4/objc4-646/runtime/objc-sync.mm 4. Runloop Perform selector on a thread 当目标线程runloop未启动时是没有效果的。 启动 Runloop If no input sources or timers are attached to the run loop, this method exits immediately; Manually removing all known input sources and timers from the run loop is not a guarantee that the run loop will exit.macOS can install and remove additional input sources as needed to process requests targeted at the receiver’s thread. Those sources could therefore prevent the run loop from exiting. The Run Loop Sequence of Events Each time you run it, your thread’s run loop processes pending events and generates notifications for any attached observers. The order in which it does this is very specific and is as follows: Notify observers that the run loop has been entered. Notify observers that any ready timers are about to fire. Notify observers that any input sources that are not port based are about to fire. Fire any non-port-based input sources that are ready to fire. If a port-based input source is ready and waiting to fire, process the event immediately. Go to step 9. Notify observers that the thread is about to sleep. Put the thread to sleep until one of the following events occurs: An event arrives for a port-based input source. A timer fires. The timeout value set for the run loop expires. The run loop is explicitly woken up. Notify observers that the thread just woke up. Process the pending event. If a user-defined timer fired, process the timer event and restart the loop. Go to step 2. If an input source fired, deliver the event. If the run loop was explicitly woken up but has not yet timed out, restart the loop. Go to step 2. Notify observers that the run loop has exited. Example: Detect Main Runloop lag with RunloopObserver 参考资料: https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/Multithreading/RunLoopManagement/RunLoopManagement.html http://www.tanhao.me/code/151113.html/ 6. Queue Vs Thread Thread != Queue A queue doesn't own a thread and a thread is not bound to a queue. There are threads and there are queues. Whenever a queue wants to run a block, it needs a thread but that won't always be the same thread. It just needs any thread for it (this may be a different one each time) and when it's done running blocks (for the moment), the same thread can now be used by a different queue. There's also no guarantee that a given serial queue will always use the same thread. The only exception is the main queue: dispatch_get_main_queue will must run on main thread. While, main thread may run task at any more than one queue. 7. Dispatch Sync Vs Dispatch Async dispatch_sync dispatch_sync └──dispatch_sync_f └──_dispatch_sync_f2 └──_dispatch_sync_f_slow static void _dispatch_sync_f_slow(dispatch_queue_t dq, void *ctxt, dispatch_function_t func) { _dispatch_thread_semaphore_t sema = _dispatch_get_thread_semaphore(); struct dispatch_sync_slow_s { DISPATCH_CONTINUATION_HEADER(sync_slow); } dss = { .do_vtable = (void*)DISPATCH_OBJ_SYNC_SLOW_BIT, .dc_ctxt = (void*)sema, }; _dispatch_queue_push(dq, (void *)&dss); _dispatch_thread_semaphore_wait(sema); _dispatch_put_thread_semaphore(sema); // ... } Submits a block to a dispatch queue for synchronous execution. Unlike dispatch_async,this function does not return until the block has finished. Calling this function and targeting the current queue results in deadlock. Unlike withdispatch_async,no retain is performed on the target queue.Because calls to this function are synchronous, it "borrows" the reference of the caller. Moreover, noBlock_copyis performed on the block. As an optimization, this function invokes the block on the current thread when possible. dispatch_syncdoes two things: queue a block blocks the current thread until the block has finished running dispatch_async dispatch_async(dispatch_queue_t queue, dispatch_block_t block) { dispatch_async_f(dq, _dispatch_Block_copy(work), _dispatch_call_block_and_release); } dispatch_async_f(dispatch_queue_t queue, void *context, dispatch_function_t work); Dead Locks dispatch_sync(queueA, ^{ dispatch_sync(queueB, ^{ dispatch_sync(queueA, ^{ // DEAD LOCK // some task }); }); }); Example: dispatch_async(QueueA, ^{ someFunctionA(...); dispatch_sync(QueueB, ^{ someFunctionB(...); }); }); WhenQueueAruns the block, it will temporarily own a thread, any thread.someFunctionA(...)will execute on that thread. Now while doing the synchronous dispatch,QueueAcannot do anything else, it has to wait for the dispatch to finish.QueueBon the other hand, will also need a thread to run its block and executesomeFunctionB(...). So eitherQueueAtemporarily suspends its thread andQueueBuses some other thread to run the block orQueueAhands its thread over toQueueB(after all it won't need it anyway until the synchronous dispatch has finished) andQueueBdirectly uses the current thread ofQueueA. Needless to say that the last option is much faster as no thread switch is required. Andthisis the optimization the sentence talks about. So adispatch_sync()to a different queue may not always cause a thread switch (different queue, maybe same thread). But adispatch_sync()still cannot happen to the same queue (same thread, yes, same queue, no). That's because a queue will execute block after block and when it currently executes a block, it won't execute another one until this one is done. So it executesBlockAandBlockAdoes adispatch_sync()ofBlockBon the same queue. The queue won't runBlockBas long as it still runsBlockA, but runningBlockAwon't continue untilBlockBhas run. Important:You should never call thedispatch_syncordispatch_sync_ffunction from a task that is executing in the same queue that you are planning to pass to the function. This is particularly important for serial queues, which are guaranteed to deadlock, butshould also be avoided for concurrent queues. 8. Dispatch set target The misunderstanding here is thatdispatch_get_specificdoesn't traverse thestackof nested queues, it traverses thequeue targeting lineage. Modifying the target queue of some objects changes their behavior: Dispatch queues: A dispatch queue's priority is inherited from its target queue. If you submit a block to a serial queue, and the serial queue’s target queue is a different serial queue, that block is not invoked concurrently with blocks submitted to the target queue or to any other queue with that same target queue. Dispatch sources: A dispatch source's target queue specifies where its event handler and cancellation handler blocks are submitted. Dispatch I/O channels: A dispatch I/O channel's target queue specifies where its I/O operations are executed. By default, a newly created queue forwards into the default priority global queue. 参考资料: https://bestswifter.com/deep-gcd/?spm=5176.100239.0.0.vCv2rL https://stackoverflow.com/questions/20860997/dispatch-queue-set-specific-vs-getting-the-current-queue https://stackoverflow.com/questions/23955948/why-did-apple-deprecate-dispatch-get-current-queue https://stackoverflow.com/questions/7346929/why-do-we-use-builtin-expect-when-a-straightforward-way-is-to-use-if-else https://www.objc.io/issues/2-concurrency/concurrency-apis-and-pitfalls/?spm=5176.100239.blogcont17709.5.71pknM libdispatch 源码:https://opensource.apple.com/tarballs/libdispatch/ 9. Read-write Lock in GCD Use dispatch_barrier_async(). When the barrier block reaches the front of a private concurrent queue, it is not executed immediately. Instead, the queue waits until its currently executing blocks finish executing. At that point, the barrier block executes by itself.Any blocks submitted after the barrier block are not executed until the barrier block completes. The queue you specify should be a concurrent queue that you create yourself using thedispatch_queue_create function.If the queue you pass to this function is a serial queue or one of the global concurrent queues, this function behaves like thedispatch_asyncfunction. 附录: 测试Demo:http://files.cnblogs.com/files/smileEvday/iOSMultiThreadSample.zip 部门招人: 高级iOS、Android、前端开发,有意私聊,博主请你喝️ 如果觉得本文帮到了你,记得点赞哦,当然也可以请博主喝一杯豆浆 微信二维码 QQ二维码

优秀的个人博客,低调大师

德地图之轨迹回放

最近项目中有需要做车辆的轨迹回放功能,在这里就给打分享一下还是挺简单的。 <!doctype html> <html> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="initial-scale=1.0, user-scalable=no, width=device-width"> <title>轨迹回放</title> <link href="styles/NewGlobal.css" rel="stylesheet" /> <link rel="stylesheet" href="css/gaode_main1119.css" /> <link rel="stylesheet" type="text/css" href="css/ui.css"> <!-- <link type="text/css" rel="stylesheet" href="css/sale-list.css" /> --> <link rel="stylesheet" href="css/demo-center.css"/> <script type="text/javascript" src="jquery/1.9.1/jquery.min.js"></script> <style> html, body, #container { height: 98%; width: 100%; } .input-card .btn{ margin-right: 1.2rem; width: 9rem; } .input-card .btn:last-child{ margin-right: 0; } </style> </head> <body> <div id="container"></div> <div class="input-card" style="margin-bottom:45px"> <div style="" align="center"><span id="tsid" style="font-size: 14px"></span></div> <h4>轨迹回放控制</h4> <div class="input-item"> <input type="button" class="btn" style="width: 25%" value="今天" id="start1" onclick="gettrack('1')"/> <input type="button" class="btn" style="width: 25%" value="昨天" id="start2" onclick="gettrack('2')"/> <input type="button" class="btn" style="width: 25%" value="前天" id="start3" onclick="gettrack('3')"/> <input type="button" class="btn" value="开始回放" id="start" onclick="startAnimation()"/> <input type="button" class="btn" value="暂停回放" id="pause" onclick="pauseAnimation()"/> </div> </div> <div class="aui-l-content" style="width:100%;margin: 0 auto; bottom: 0px; position: fixed;"> <div class="aui-menu-list aui-menu-list-clear"> <ul> <li class="b-line"> <a href="new_index.html"> <div class="aui-icon"><img src="images/icon-home/my-in2.png"></div> <h3>返回主页</h3> <div class="aui-time"><i class="aui-jump"></i></div> </a> </li> </ul> </div> </div> <script type="text/javascript" src="https://webapi.amap.com/maps?v=1.4.10&key="></script> <script> var marker, lineArr=new Array(),tishiArr=new Array(); $(function(){ var h = document.documentElement.clientHeight || document.body.clientHeight; $("#container").css("height", h-52-77-12-50); tomap(); }) //lineArr数组为历史回顾的GPS点 function tomap(){ var map = new AMap.Map("container", { resizeEnable: true, center: [104.04, 30.40], zoom: 14 }); map.clearMap(); // 清除地图覆盖物 marker = new AMap.Marker({ map: map, position: lineArr[0], icon: "images/car.png", offset: new AMap.Pixel(-26, -13), autoRotation: true, angle:-90, }); // 绘制轨迹 var polyline = new AMap.Polyline({ map: map, path: lineArr, showDir:true, strokeColor: "#28F", //线颜色 // strokeOpacity: 1, //线透明度 strokeWeight: 6, //线宽 // strokeStyle: "solid" //线样式 }); var passedPolyline = new AMap.Polyline({ map: map, // path: lineArr, strokeColor: "#AF5", //线颜色 // strokeOpacity: 1, //线透明度 strokeWeight: 6, //线宽 // strokeStyle: "solid" //线样式 }); marker.on('moving', function (e) { passedPolyline.setPath(e.passedPath); }); map.setFitView(); } var i=0; var t1; var k=0; function addinfo(){ $("#tsid").html(tishiArr[i]); i=i+1; if(i>tishiArr.length){ clearInterval(t1); i=0; } } //开始 function startAnimation () { if(lineArr.length>0){ if(k>0){ marker.resumeMove(); }else{ marker.moveAlong(lineArr, 350); } t1=window.setInterval(addinfo, 500); } } //继续 function pauseAnimation () { k=1; clearInterval(t1); if(lineArr.length>0){ marker.pauseMove(); } } // function resumeAnimation () { marker.resumeMove(); } //停止 function stopAnimation () { if(lineArr.length>0){ marker.stopMove(); } } </script> </body> </html> 上个图吧这里: ​​​ 是不是就是很简单的操作。 如有需要可以加我Q群【308742428】大家一起讨论技术。 后面会不定时为大家更新文章,敬请期待。 喜欢的朋友可以关注下。

优秀的个人博客,低调大师

可用Redis(三):Hash类型

1.哈希类型键值结构 哈希类型也是key-value结构,key是字符串类型,其value分为两个部分:field和value 其中field部分代表属性,value代表属性对应的值 上面的图里,user:1:info为key,name,age,Date为user这个key的一些属性,value是属性对应的值 在hash中,可以为key添加一个新的属性和新的值 比如使用下面的命令向user:1:info这个key添加一个新属性viewCounter,属性对应的值为100 hset user:1:info viewCounter 100 2.特点 key-value结构 key(field)不能相同,value可以相同 3.Redis哈希类型对应的命令 3.1 hget命令,hset命令和hdel命令 hget key field 获取hash key对应的field的value hset key field value 设置hash key对应的field的value hdel key field 删除hash key对应的field的value 例子: 127.0.0.1:6379> hset user:1:info age 23 (integer) 1 127.0.0.1:6379> hget user:1:info age "23" 127.0.0.1:6379> hset user:1:info name python (integer) 1 127.0.0.1:6379> hgetall user:1:info 1) "age" 2) "23" 3) "name" 4) "python" 127.0.0.1:6379> hdel user:1:info age (integer) 1 127.0.0.1:6379> hgetall user:1:info 1) "name" 2) "python" 127.0.0.1:6379> 注意事项: hget命令,hset命令和hdel命令的时间复杂度为O(1) 3.2 hexists命令和hlen命令 hexists key field 判断hash key是否有field hlen key 获取hash key field的数量 例子: 127.0.0.1:6379> hgetall user:1:info 1) "name" 2) "python" 3) "age" 4) "23" 127.0.0.1:6379> hexists user:1:info name (integer) 1 127.0.0.1:6379> hlen user:1:info (integer) 2 注意事项: hexists命令和hlen命令的时间复杂度为O(1) 3.3 hmget命令和hmset命令 hmget key field1 field2 ... fieldN 批量获取hash key的一批field对应的值 hmset key field1 value1 field2 value2 ... fieldN valueN 批量设置hash key的一批field value 例子: 127.0.0.1:6379> hmset user:2:info age 30 name mysql page 50 OK 127.0.0.1:6379> hlen user:2:info (integer) 3 127.0.0.1:6379> hmget user:2:info age name 1) "30" 2) "mysql" 127.0.0.1:6379> 注意事项: hmget命令和hmset命令的时间复杂度为O(1) 3.4 hgetall命令,hvals命令和hkeys命令 hgetall key 返回hash key对应所有的field和value hvals key 返回hash key对应所有field的value hkeys key 返回hash key对应所有field 例子: 127.0.0.1:6379> hgetall user:2:info 1) "age" 2) "30" 3) "name" 4) "mysql" 5) "page" 6) "50" 127.0.0.1:6379> hvals user:2:info 1) "30" 2) "mysql" 3) "50" 127.0.0.1:6379> hkeys user:2:info 1) "age" 2) "name" 3) "page" 注意事项: hgetall命令,hvals命令和hkeys命令的时间复杂度为O(1) 由于Redis的单线程的特点以及hgetall会返回所有的key和value,所以如果hash中存储的数据过多时,hgetall命令的执行速度会比较慢 3.5 hsetnx命令,hincrby命令和hincrbyfloat命令 hsetnx key field value 设置hash key对应field的value(如field已经存在,则失败) hincrby key field intCounter hash key对应的field的value自增intCounter hincrbyfloat key field floatCounter hincrby浮点数版 注意事项: hsetnx命令,hincrby命令和hincrbyfloat命令的时间复杂度为O(1) 4.实战 记录网站每个用户个人主页的访问量,也可以使用哈希类型 这样可以保证每个用户的相关数据是一个整体,而使用字符串类型保存的话,则每个用户个人主面的访问量与每个用户的其他数据都是一个分离的状态。 记录网站每个用户个人主页的访问量需要根据实际情况来进行设计使用 hincrby user:1:info pageview count 5.使用Redis保存每个用户相关的数据,可以使用三种方式 5.1 方式一 使用用户的id为key,把用户的相关数据进行序列化后并做为value 使用时,根据用户id获取对应的数据的字符串格式,进行反序列化后就可以得到用户相关的数据进行查询和更新操作 5.2 方式二 使用用户的id与用户对应的属性名进行拼接得到新的字符串,并做为key,用户对应属性的值做为value 这样,用户的所有信息都是分离开的,可以很方便的对用户的数据进行查询和更新,并且可以很方便的为用户添加新的属性,而不用对原来的属性有影响 5.3 方式三 使用用户id做为key,用户的其他数据都保存为hash格式, 对用户的属性进行查询,更新和添加都比较方便 保存用户相关数据的方式比较

资源下载

更多资源
腾讯云软件源

腾讯云软件源

为解决软件依赖安装时官方源访问速度慢的问题,腾讯云为一些软件搭建了缓存服务。您可以通过使用腾讯云软件源站来提升依赖包的安装速度。为了方便用户自由搭建服务架构,目前腾讯云软件源站支持公网访问和内网访问。

Spring

Spring

Spring框架(Spring Framework)是由Rod Johnson于2002年提出的开源Java企业级应用框架,旨在通过使用JavaBean替代传统EJB实现方式降低企业级编程开发的复杂性。该框架基于简单性、可测试性和松耦合性设计理念,提供核心容器、应用上下文、数据访问集成等模块,支持整合Hibernate、Struts等第三方框架,其适用范围不仅限于服务器端开发,绝大多数Java应用均可从中受益。

Rocky Linux

Rocky Linux

Rocky Linux(中文名:洛基)是由Gregory Kurtzer于2020年12月发起的企业级Linux发行版,作为CentOS稳定版停止维护后与RHEL(Red Hat Enterprise Linux)完全兼容的开源替代方案,由社区拥有并管理,支持x86_64、aarch64等架构。其通过重新编译RHEL源代码提供长期稳定性,采用模块化包装和SELinux安全架构,默认包含GNOME桌面环境及XFS文件系统,支持十年生命周期更新。

Sublime Text

Sublime Text

Sublime Text具有漂亮的用户界面和强大的功能,例如代码缩略图,Python的插件,代码段等。还可自定义键绑定,菜单和工具栏。Sublime Text 的主要功能包括:拼写检查,书签,完整的 Python API , Goto 功能,即时项目切换,多选择,多窗口等等。Sublime Text 是一个跨平台的编辑器,同时支持Windows、Linux、Mac OS X等操作系统。

用户登录
用户注册