Discovery8200机台电脑开机用户名和密码都没有了没有了怎么办

&figure&&img src=&https://pic2.zhimg.com/v2-94c1f5f02fc8deb3a4cf8_b.jpg& data-rawwidth=&374& data-rawheight=&135& class=&content_image& width=&374&&&/figure&&h2&&b&1.概述&/b&&/h2&&p&Lambda表达式(也称为闭包)是整个Java 8发行版中最受期待的在Java语言层面上的改变,Lambda允许把函数作为一个方法的参数(函数作为参数传递进方法中),或者把代码看成数据。&/p&&p&在最简单的形式中,一个lambda可以由用逗号分隔的参数列表、–&符号与函数体三部分表示。例如:&/p&&div class=&highlight&&&pre&&code class=&language-java&&&span&&/span&&span class=&n&&Arrays&/span&&span class=&o&&.&/span&&span class=&na&&asList&/span&&span class=&o&&(&/span& &span class=&s&&&a&&/span&&span class=&o&&,&/span& &span class=&s&&&b&&/span&&span class=&o&&,&/span& &span class=&s&&&d&&/span& &span class=&o&&).&/span&&span class=&na&&forEach&/span&&span class=&o&&(&/span& &span class=&n&&e&/span& &span class=&o&&-&&/span& &span class=&n&&System&/span&&span class=&o&&.&/span&&span class=&na&&out&/span&&span class=&o&&.&/span&&span class=&na&&println&/span&&span class=&o&&(&/span& &span class=&n&&e&/span& &span class=&o&&)&/span& &span class=&o&&);&/span&
&/code&&/pre&&/div&&p&本文主要讲解如果利用lambda表达式来编写比较器对集合进行排序。&/p&&p&首先,我们定义一个实体类:&/p&&div class=&highlight&&&pre&&code class=&language-java&&&span&&/span&&span class=&kd&&public&/span& &span class=&kd&&class&/span& &span class=&nc&&Car&/span& &span class=&o&&{&/span&
&span class=&kd&&private&/span& &span class=&n&&String&/span& &span class=&n&&name&/span&&span class=&o&&;&/span&
&span class=&kd&&private&/span& &span class=&kt&&int&/span& &span class=&n&&price&/span&&span class=&o&&;&/span&
&span class=&kd&&public&/span& &span class=&nf&&Car&/span&&span class=&o&&()&/span& &span class=&o&&{&/span&
&span class=&kd&&super&/span&&span class=&o&&();&/span&
&span class=&o&&}&/span&
&span class=&kd&&public&/span& &span class=&nf&&Car&/span&&span class=&o&&(&/span&&span class=&n&&String&/span& &span class=&n&&name&/span&&span class=&o&&,&/span& &span class=&kt&&int&/span& &span class=&n&&price&/span&&span class=&o&&)&/span& &span class=&o&&{&/span&
&span class=&kd&&super&/span&&span class=&o&&();&/span&
&span class=&k&&this&/span&&span class=&o&&.&/span&&span class=&na&&name&/span& &span class=&o&&=&/span& &span class=&n&&name&/span&&span class=&o&&;&/span&
&span class=&k&&this&/span&&span class=&o&&.&/span&&span class=&na&&price&/span& &span class=&o&&=&/span& &span class=&n&&price&/span&&span class=&o&&;&/span&
&span class=&o&&}&/span&
&span class=&kd&&public&/span& &span class=&n&&String&/span& &span class=&nf&&getName&/span&&span class=&o&&()&/span& &span class=&o&&{&/span&
&span class=&k&&return&/span& &span class=&n&&name&/span&&span class=&o&&;&/span&
&span class=&o&&}&/span&
&span class=&kd&&public&/span& &span class=&kt&&void&/span& &span class=&nf&&setName&/span&&span class=&o&&(&/span&&span class=&n&&String&/span& &span class=&n&&name&/span&&span class=&o&&)&/span& &span class=&o&&{&/span&
&span class=&k&&this&/span&&span class=&o&&.&/span&&span class=&na&&name&/span& &span class=&o&&=&/span& &span class=&n&&name&/span&&span class=&o&&;&/span&
&span class=&o&&}&/span&
&span class=&kd&&public&/span& &span class=&kt&&int&/span& &span class=&nf&&getPrice&/span&&span class=&o&&()&/span& &span class=&o&&{&/span&
&span class=&k&&return&/span& &span class=&n&&price&/span&&span class=&o&&;&/span&
&span class=&o&&}&/span&
&span class=&kd&&public&/span& &span class=&kt&&void&/span& &span class=&nf&&setPrice&/span&&span class=&o&&(&/span&&span class=&kt&&int&/span& &span class=&n&&price&/span&&span class=&o&&)&/span& &span class=&o&&{&/span&
&span class=&k&&this&/span&&span class=&o&&.&/span&&span class=&na&&price&/span& &span class=&o&&=&/span& &span class=&n&&price&/span&&span class=&o&&;&/span&
&span class=&o&&}&/span&
&span class=&o&&}&/span&
&/code&&/pre&&/div&&h2&&b&2. Java7排序方法&/b&&/h2&&p&Java 7及以前我们通常通过&b&创建内部匿名类&/b&用于排序 :&/p&&div class=&highlight&&&pre&&code class=&language-java&&&span&&/span&
&span class=&k&&new&/span& &span class=&n&&Comparator&/span&&span class=&o&&&&/span&&span class=&n&&Car&/span&&span class=&o&&&()&/span& &span class=&o&&{&/span&
&span class=&nd&&@Override&/span&
&span class=&kd&&public&/span& &span class=&kt&&int&/span& &span class=&nf&&compare&/span&&span class=&o&&(&/span&&span class=&n&&Car&/span& &span class=&n&&c1&/span&&span class=&o&&,&/span& &span class=&n&&Car&/span& &span class=&n&&c2&/span&&span class=&o&&)&/span& &span class=&o&&{&/span&
&span class=&k&&return&/span& &span class=&n&&c1&/span&&span class=&o&&.&/span&&span class=&na&&getName&/span&&span class=&o&&().&/span&&span class=&na&&compareTo&/span&&span class=&o&&(&/span&&span class=&n&&c2&/span&&span class=&o&&.&/span&&span class=&na&&getName&/span&&span class=&o&&());&/span&
&span class=&o&&}&/span&
&span class=&o&&};&/span&
&/code&&/pre&&/div&&p&然后使用Collections.sort方法对List&car&排序&/p&&div class=&highlight&&&pre&&code class=&language-java&&&span&&/span&&span class=&n&&Collections&/span&&span class=&o&&.&/span&&span class=&na&&sort&/span&&span class=&o&&(&/span&&span class=&n&&cars&/span&&span class=&o&&,&/span& &span class=&k&&new&/span& &span class=&n&&Comparator&/span&&span class=&o&&&&/span&&span class=&n&&Car&/span&&span class=&o&&&()&/span& &span class=&o&&{&/span&
&span class=&nd&&@Override&/span&
&span class=&kd&&public&/span& &span class=&kt&&int&/span& &span class=&nf&&compare&/span&&span class=&o&&(&/span&&span class=&n&&Car&/span& &span class=&n&&c1&/span&&span class=&o&&,&/span& &span class=&n&&Car&/span& &span class=&n&&c2&/span&&span class=&o&&)&/span& &span class=&o&&{&/span&
&span class=&k&&return&/span& &span class=&n&&c1&/span&&span class=&o&&.&/span&&span class=&na&&getName&/span&&span class=&o&&().&/span&&span class=&na&&compareTo&/span&&span class=&o&&(&/span&&span class=&n&&c2&/span&&span class=&o&&.&/span&&span class=&na&&getName&/span&&span class=&o&&());&/span&
&span class=&o&&}&/span&
&span class=&o&&});&/span&
&/code&&/pre&&/div&&h2&&b&3. 用lambda表达式排序&/b&&/h2&&p&通过lambda表达式,我们可以绕过内部匿名类:&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&(Car c1, Car c2) -& c1.getName().compareTo(c2.getName());
&/code&&/pre&&/div&&p&具体如下:&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&List&Car& cars2 = Lists.newArrayList(new Car(&benci&, 10),
new Car(&baoma&, 12));
cars2.sort((Car c1, Car c2) -& c1.getName().compareTo(c2.getName()));
&/code&&/pre&&/div&&p&注意:我们现在使用Java8中在&b&&i&java.util.List&/i& 新sortAPI&/b&代替了以前的&i&Collections.sort&/i& API&/p&&h2&&b&4. 使用无类型声明lambda表达式排序&/b&&/h2&&p&事实上,可以省略这里的lambda参数的类型声明,编译器可以从列表的类属性推测出来。&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&(c1, c2) -& c1.getName().compareTo(c2.getName())
&/code&&/pre&&/div&&p&具体如下:&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&List&Car& cars3 = Lists.newArrayList(new Car(&benci&, 10),
new Car(&baoma&, 12));
cars3.sort((c1, c2) -& c1.getName().compareTo(c2.getName()));
&/code&&/pre&&/div&&h2&&b&5. 静态方法引用&/b&&/h2&&p&lambda表达式表示在函数接口中定义的匿名函数。&/p&&p&方法引用使用现有方法创建lambda表达式。&/p&&p&方法引用的一般语法是&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&Qualifier::MethodName
&/code&&/pre&&/div&&p&我们用&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&cars4.sort(Car::compareByNameThenPrice);
&/code&&/pre&&/div&&p&在Car中加入静态方法:&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&
public static int compareByNameThenPrice(Car car1, Car car2) {
if (car1.name.equals(car2.name)) {
return car1.price - car2.
return car1.name.compareTo(car2.name);
&/code&&/pre&&/div&&p&具体:&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&List&Car& cars4 = Lists.newArrayList(new Car(&benci&, 10),
new Car(&baoma&, 12));
cars4.sort(Car::compareByNameThenPrice);
&/code&&/pre&&/div&&h2&&b&6. 使用多条件排序&/b&&/h2&&p&我们可以同时将多个比较器串起来:&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&
List&Car& cars5 = Lists.newArrayList(new Car(&benci&, 10),
new Car(&baoma&, 12));
cars5.sort(Comparator.comparing(Car::getName)
.thenComparing(Car::getPrice));
&/code&&/pre&&/div&&h2&&b&7.总结&/b&&/h2&&p&我们学习了如何使用lambda表达式与Comparator 类来对Java集合进行排序。&/p&
1.概述Lambda表达式(也称为闭包)是整个Java 8发行版中最受期待的在Java语言层面上的改变,Lambda允许把函数作为一个方法的参数(函数作为参数传递进方法中),或者把代码看成数据。在最简单的形式中,一个lambda可以由用逗号分隔的参数列表、–&符号与函…
&p&&b&前言&/b&&/p&&p&个人认为,学习,内容越多、越杂的知识,越需要进行深刻的总结,这样才能记忆深刻,将知识变成自己的。这篇文章主要是对多线程的问题进行总结的,因此罗列了40个多线程的问题。&br&这些多线程的问题,有些来源于各大网站、有些来源于自己的思考。可能有些问题网上有、可能有些问题对应的答案也有、也可能有些各位网友也都看过,但是本文写作的重心就是所有的问题都会按照自己的理解回答一遍,不会去看网上的答案,因此可能有些问题讲的不对,能指正的希望大家不吝指教。&br&&/p&&figure&&img src=&https://pic2.zhimg.com/v2-a3e3b16dadb0ff8e217527a_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&651& data-rawheight=&463& class=&origin_image zh-lightbox-thumb& width=&651& data-original=&https://pic2.zhimg.com/v2-a3e3b16dadb0ff8e217527a_r.jpg&&&/figure&&p&&b&40个问题汇总&/b&&/p&&p&&b&1、多线程有什么用?&/b&&/p&&p&一个可能在很多人看来很扯淡的一个问题:我会用多线程就好了,还管它有什么用?在我看来,这个回答更扯淡。所谓”知其然知其所以然”,”会用”只是”知其然”,”为什么用”才是”知其所以然”,只有达到”知其然知其所以然”的程度才可以说是把一个知识点运用自如。OK,下面说说我对这个问题的看法:&br&(1)发挥多核CPU的优势&br&随着工业的进步,现在的笔记本、台式机乃至商用的应用服务器至少也都是双核的,4核、8核甚至16核的也都不少见,如果是单线程的程序,那么在双核CPU上就浪费了50%,在4核CPU上就浪费了75%。单核CPU上所谓的”多线程”那是假的多线程,同一时间处理器只会处理一段逻辑,只不过线程之间切换得比较快,看着像多个线程”同时”运行罢了。多核CPU上的多线程才是真正的多线程,它能让你的多段逻辑同时工作,多线程,可以真正发挥出多核CPU的优势来,达到充分利用CPU的目的。&br&(2)防止阻塞&br&从程序运行效率的角度来看,单核CPU不但不会发挥出多线程的优势,反而会因为在单核CPU上运行多线程导致线程上下文的切换,而降低程序整体的效率。但是单核CPU我们还是要应用多线程,就是为了防止阻塞。试想,如果单核CPU使用单线程,那么只要这个线程阻塞了,比方说远程读取某个数据吧,对端迟迟未返回又没有设置超时时间,那么你的整个程序在数据返回回来之前就停止运行了。多线程可以防止这个问题,多条线程同时运行,哪怕一条线程的代码执行读取数据阻塞,也不会影响其它任务的执行。&br&(3)便于建模&br&这是另外一个没有这么明显的优点了。假设有一个大的任务A,单线程编程,那么就要考虑很多,建立整个程序模型比较麻烦。但是如果把这个大的任务A分解成几个小任务,任务B、任务C、任务D,分别建立程序模型,并通过多线程分别运行这几个任务,那就简单很多了。&/p&&p&&b&2、创建线程的方式&/b&&/p&&p&比较常见的一个问题了,一般就是两种:&br&(1)继承Thread类&br&(2)实现Runnable接口&br&至于哪个好,不用说肯定是后者好,因为实现接口的方式比继承类的方式更灵活,也能减少程序之间的耦合度,面向接口编程也是设计模式6大原则的核心。&/p&&p&&b&3、start()方法和run()方法的区别&/b&&/p&&p&只有调用了start()方法,才会表现出多线程的特性,不同线程的run()方法里面的代码交替执行。如果只是调用run()方法,那么代码还是同步执行的,必须等待一个线程的run()方法里面的代码全部执行完毕之后,另外一个线程才可以执行其run()方法里面的代码。&/p&&p&&b&4、Runnable接口和Callable接口的区别&/b&&/p&&p&有点深的问题了,也看出一个Java学习知识的广度。&br&Runnable接口中的run()方法的返回值是void,它做的事情只是纯粹地去执行run()方法中的代码而已;Callable接口中的call()方法是有返回值的,是一个泛型,和Future、FutureTask配合可以用来获取异步执行的结果。&br&这其实是很有用的一个特性,因为多线程相比单线程更难、更复杂的一个重要原因就是因为多线程充满着未知性,某条线程是否执行了?某条线程执行了多久?某条线程执行的时候我们期望的数据是否已经赋值完毕?无法得知,我们能做的只是等待这条多线程的任务执行完毕而已。而Callable+Future/FutureTask却可以获取多线程运行的结果,可以在等待时间太长没获取到需要的数据的情况下取消该线程的任务,真的是非常有用。&/p&&p&&b&5、CyclicBarrier和CountDownLatch的区别&/b&&/p&&p&两个看上去有点像的类,都在java.util.concurrent下,都可以用来表示代码运行到某个点上,二者的区别在于:&br&(1)CyclicBarrier的某个线程运行到某个点上之后,该线程即停止运行,直到所有的线程都到达了这个点,所有线程才重新运行;CountDownLatch则不是,某线程运行到某个点上之后,只是给某个数值-1而已,该线程继续运行&br&(2)CyclicBarrier只能唤起一个任务,CountDownLatch可以唤起多个任务&br&(3)CyclicBarrier可重用,CountDownLatch不可重用,计数值为0该CountDownLatch就不可再用了&/p&&p&&b&6、Volatile关键字的作用&/b&&/p&&p&一个非常重要的问题,是每个学习、应用多线程的Java程序员都必须掌握的。理解volatile关键字的作用的前提是要理解Java内存模型,这里就不讲Java内存模型了,可以参见第31点,volatile关键字的作用主要有两个:&br&(1)多线程主要围绕可见性和原子性两个特性而展开,使用volatile关键字修饰的变量,保证了其在多线程之间的可见性,即每次读取到volatile变量,一定是最新的数据&br&(2)代码底层执行不像我们看到的高级语言—-Java程序这么简单,它的执行是Java代码–&字节码–&根据字节码执行对应的C/C++代码–&C/C++代码被编译成汇编语言–&和硬件电路交互,现实中,为了获取更好的性能JVM可能会对指令进行重排序,多线程下可能会出现一些意想不到的问题。使用volatile则会对禁止语义重排序,当然这也一定程度上降低了代码执行效率&br&从实践角度而言,volatile的一个重要作用就是和CAS结合,保证了原子性,详细的可以参见java.util.concurrent.atomic包下的类,比如AtomicInteger。&/p&&p&&b&7、什么是线程安全&/b&&/p&&p&又是一个理论的问题,各式各样的答案有很多,我给出一个个人认为解释地最好的:如果你的代码在多线程下执行和在单线程下执行永远都能获得一样的结果,那么你的代码就是线程安全的。&br&这个问题有值得一提的地方,就是线程安全也是有几个级别的:&br&(1)不可变&br&像String、Integer、Long这些,都是final类型的类,任何一个线程都改变不了它们的值,要改变除非新创建一个,因此这些不可变对象不需要任何同步手段就可以直接在多线程环境下使用&br&(2)绝对线程安全&br&不管运行时环境如何,调用者都不需要额外的同步措施。要做到这一点通常需要付出许多额外的代价,Java中标注自己是线程安全的类,实际上绝大多数都不是线程安全的,不过绝对线程安全的类,Java中也有,比方说CopyOnWriteArrayList、CopyOnWriteArraySet&br&(3)相对线程安全&br&相对线程安全也就是我们通常意义上所说的线程安全,像Vector这种,add、remove方法都是原子操作,不会被打断,但也仅限于此,如果有个线程在遍历某个Vector、有个线程同时在add这个Vector,99%的情况下都会出现ConcurrentModificationException,也就是fail-fast机制。&br&(4)线程非安全&br&这个就没什么好说的了,ArrayList、LinkedList、HashMap等都是线程非安全的类&/p&&p&&b&8、Java中如何获取到线程dump文件&/b&&/p&&p&死循环、死锁、阻塞、页面打开慢等问题,打线程dump是最好的解决问题的途径。所谓线程dump也就是线程堆栈,获取到线程堆栈有两步:&br&(1)获取到线程的pid,可以通过使用jps命令,在Linux环境下还可以使用ps -ef | grep java&br&(2)打印线程堆栈,可以通过使用jstack pid命令,在Linux环境下还可以使用kill -3 pid&br&另外提一点,Thread类提供了一个getStackTrace()方法也可以用于获取线程堆栈。这是一个实例方法,因此此方法是和具体线程实例绑定的,每次获取获取到的是具体某个线程当前运行的堆栈,&/p&&p&&b&9、一个线程如果出现了运行时异常会怎么样&/b&&/p&&p&如果这个异常没有被捕获的话,这个线程就停止执行了。另外重要的一点是:如果这个线程持有某个某个对象的监视器,那么这个对象监视器会被立即释放&/p&&p&&b&10、如何在两个线程之间共享数据&/b&&/p&&p&通过在线程之间共享对象就可以了,然后通过wait/notify/notifyAll、await/signal/signalAll进行唤起和等待,比方说阻塞队列BlockingQueue就是为线程之间共享数据而设计的。&/p&&p&&b&11、sleep方法和wait方法有什么区别&/b&&/p&&p&这个问题常问,sleep方法和wait方法都可以用来放弃CPU一定的时间,不同点在于如果线程持有某个对象的监视器,sleep方法不会放弃这个对象的监视器,wait方法会放弃这个对象的监视器&/p&&p&&b&12、生产者消费者模型的作用是什么&/b&&/p&&p&这个问题很理论,但是很重要:&br&(1)通过平衡生产者的生产能力和消费者的消费能力来提升整个系统的运行效率,这是生产者消费者模型最重要的作用&br&(2)解耦,这是生产者消费者模型附带的作用,解耦意味着生产者和消费者之间的联系少,联系越少越可以独自发展而不需要收到相互的制约&/p&&p&&b&13、ThreadLocal有什么用&/b&&br&简单说ThreadLocal就是一种以空间换时间的做法,在每个Thread里面维护了一个以开地址法实现的ThreadLocal.ThreadLocalMap,把数据进行隔离,数据不共享,自然就没有线程安全方面的问题了&/p&&p&&b&14、为什么wait()方法和notify()/notifyAll()方法要在同步块中被调用&/b&&/p&&p&这是JDK强制的,wait()方法和notify()/notifyAll()方法在调用前都必须先获得对象的锁&/p&&p&&b&15、wait()方法和notify()/notifyAll()方法在放弃对象监视器时有什么区别&/b&&/p&&p&wait()方法和notify()/notifyAll()方法在放弃对象监视器的时候的区别在于:wait()方法立即释放对象监视器,notify()/notifyAll()方法则会等待线程剩余代码执行完毕才会放弃对象监视器。&/p&&p&&b&16、为什么要使用线程池&/b&&/p&&p&避免频繁地创建和销毁线程,达到线程对象的重用。另外,使用线程池还可以根据项目灵活地控制并发的数目。&/p&&p&&b&17、怎么检测一个线程是否持有对象监视器&/b&&/p&&p&我也是在网上看到一道多线程面试题才知道有方法可以判断某个线程是否持有对象监视器:Thread类提供了一个holdsLock(Object obj)方法,当且仅当对象obj的监视器被某条线程持有的时候才会返回true,注意这是一个static方法,这意味着“某条线程”指的是当前线程。&/p&&p&&b&18、synchronized和ReentrantLock的区别&/b&&/p&&p&synchronized是和if、else、for、while一样的关键字,ReentrantLock是类,这是二者的本质区别。既然ReentrantLock是类,那么它就提供了比synchronized更多更灵活的特性,可以被继承、可以有方法、可以有各种各样的类变量,ReentrantLock比synchronized的扩展性体现在几点上:&br&(1)ReentrantLock可以对获取锁的等待时间进行设置,这样就避免了死锁&br&(2)ReentrantLock可以获取各种锁的信息&br&(3)ReentrantLock可以灵活地实现多路通知&br&另外,二者的锁机制其实也是不一样的。ReentrantLock底层调用的是Unsafe的park方法加锁,synchronized操作的应该是对象头中mark word,这点我不能确定。&/p&&p&&b&19、ConcurrentHashMap的并发度是什么&/b&&/p&&p&ConcurrentHashMap的并发度就是segment的大小,默认为16,这意味着最多同时可以有16条线程操作ConcurrentHashMap,这也是ConcurrentHashMap对Hashtable的最大优势,任何情况下,Hashtable能同时有两条线程获取Hashtable中的数据吗?&/p&&p&&b&20、ReadWriteLock是什么&/b&&/p&&p&首先明确一下,不是说ReentrantLock不好,只是ReentrantLock某些时候有局限。如果使用ReentrantLock,可能本身是为了防止线程A在写数据、线程B在读数据造成的数据不一致,但这样,如果线程C在读数据、线程D也在读数据,读数据是不会改变数据的,没有必要加锁,但是还是加锁了,降低了程序的性能。&br&因为这个,才诞生了读写锁ReadWriteLock。ReadWriteLock是一个读写锁接口,ReentrantReadWriteLock是ReadWriteLock接口的一个具体实现,实现了读写的分离,读锁是共享的,写锁是独占的,读和读之间不会互斥,读和写、写和读、写和写之间才会互斥,提升了读写的性能。&/p&&p&&b&21、FutureTask是什么&/b&&/p&&p&这个其实前面有提到过,FutureTask表示一个异步运算的任务。FutureTask里面可以传入一个Callable的具体实现类,可以对这个异步运算的任务的结果进行等待获取、判断是否已经完成、取消任务等操作。当然,由于FutureTask也是Runnable接口的实现类,所以FutureTask也可以放入线程池中。&/p&&p&&b&22、Linux环境下如何查找哪个线程使用CPU最长&/b&&/p&&p&这是一个比较偏实践的问题,这种问题我觉得挺有意义的。可以这么做:&br&(1)获取项目的pid,jps或者ps -ef | grep java,这个前面有讲过&br&(2)top -H -p pid,顺序不能改变&br&这样就可以打印出当前的项目,每条线程占用CPU时间的百分比。注意这里打出的是LWP,也就是操作系统原生线程的线程号,我笔记本山没有部署Linux环境下的Java工程,因此没有办法截图演示,网友朋友们如果公司是使用Linux环境部署项目的话,可以尝试一下。&br&使用”top -H -p pid”+”jps pid”可以很容易地找到某条占用CPU高的线程的线程堆栈,从而定位占用CPU高的原因,一般是因为不当的代码操作导致了死循环。&br&最后提一点,”top -H -p pid”打出来的LWP是十进制的,”jps pid”打出来的本地线程号是十六进制的,转换一下,就能定位到占用CPU高的线程的当前线程堆栈了。&/p&&p&&b&23、Java编程写一个会导致死锁的程序&/b&&/p&&p&第一次看到这个题目,觉得这是一个非常好的问题。很多人都知道死锁是怎么一回事儿:线程A和线程B相互等待对方持有的锁导致程序无限死循环下去。当然也仅限于此了,问一下怎么写一个死锁的程序就不知道了,这种情况说白了就是不懂什么是死锁,懂一个理论就完事儿了,实践中碰到死锁的问题基本上是看不出来的。&br&真正理解什么是死锁,这个问题其实不难,几个步骤:&br&(1)两个线程里面分别持有两个Object对象:lock1和lock2。这两个lock作为同步代码块的锁;&br&(2)线程1的run()方法中同步代码块先获取lock1的对象锁,Thread.sleep(xxx),时间不需要太多,50毫秒差不多了,然后接着获取lock2的对象锁。这么做主要是为了防止线程1启动一下子就连续获得了lock1和lock2两个对象的对象锁&br&(3)线程2的run)(方法中同步代码块先获取lock2的对象锁,接着获取lock1的对象锁,当然这时lock1的对象锁已经被线程1锁持有,线程2肯定是要等待线程1释放lock1的对象锁的&br&这样,线程1″睡觉”睡完,线程2已经获取了lock2的对象锁了,线程1此时尝试获取lock2的对象锁,便被阻塞,此时一个死锁就形成了。代码就不写了,占的篇幅有点多。&/p&&p&&b&24、怎么唤醒一个阻塞的线程&/b&&/p&&p&如果线程是因为调用了wait()、sleep()或者join()方法而导致的阻塞,可以中断线程,并且通过抛出InterruptedException来唤醒它;如果线程遇到了IO阻塞,无能为力,因为IO是操作系统实现的,Java代码并没有办法直接接触到操作系统。&/p&&p&&b&25、不可变对象对多线程有什么帮助&/b&&/p&&p&前面有提到过的一个问题,不可变对象保证了对象的内存可见性,对不可变对象的读取不需要进行额外的同步手段,提升了代码执行效率。&/p&&p&&b&26、什么是多线程的上下文切换&/b&&/p&&p&多线程的上下文切换是指CPU控制权由一个已经正在运行的线程切换到另外一个就绪并等待获取CPU执行权的线程的过程。&/p&&p&&b&27、如果你提交任务时,线程池队列已满,这时会发生什么&/b&&/p&&p&如果你使用的LinkedBlockingQueue,也就是无界队列的话,没关系,继续添加任务到阻塞队列中等待执行,因为LinkedBlockingQueue可以近乎认为是一个无穷大的队列,可以无限存放任务;如果你使用的是有界队列比方说ArrayBlockingQueue的话,任务首先会被添加到ArrayBlockingQueue中,ArrayBlockingQueue满了,则会使用拒绝策略RejectedExecutionHandler处理满了的任务,默认是AbortPolicy。&/p&&p&&b&28、Java中用到的线程调度算法是什么&/b&&/p&&p&抢占式。一个线程用完CPU之后,操作系统会根据线程优先级、线程饥饿情况等数据算出一个总的优先级并分配下一个时间片给某个线程执行。&/p&&p&&b&29、Thread.sleep(0)的作用是什么&/b&&/p&&p&这个问题和上面那个问题是相关的,我就连在一起了。由于Java采用抢占式的线程调度算法,因此可能会出现某条线程常常获取到CPU控制权的情况,为了让某些优先级比较低的线程也能获取到CPU控制权,可以使用Thread.sleep(0)手动触发一次操作系统分配时间片的操作,这也是平衡CPU控制权的一种操作。&/p&&p&&b&30、什么是自旋&/b&&/p&&p&很多synchronized里面的代码只是一些很简单的代码,执行时间非常快,此时等待的线程都加锁可能是一种不太值得的操作,因为线程阻塞涉及到用户态和内核态切换的问题。既然synchronized里面的代码执行地非常快,不妨让等待锁的线程不要被阻塞,而是在synchronized的边界做忙循环,这就是自旋。如果做了多次忙循环发现还没有获得锁,再阻塞,这样可能是一种更好的策略。&/p&&p&&b&31、什么是Java内存模型&/b&&/p&&p&Java内存模型定义了一种多线程访问Java内存的规范。Java内存模型要完整讲不是这里几句话能说清楚的,我简单总结一下Java内存模型的几部分内容:&br&(1)Java内存模型将内存分为了主内存和工作内存。类的状态,也就是类之间共享的变量,是存储在主内存中的,每次Java线程用到这些主内存中的变量的时候,会读一次主内存中的变量,并让这些内存在自己的工作内存中有一份拷贝,运行自己线程代码的时候,用到这些变量,操作的都是自己工作内存中的那一份。在线程代码执行完毕之后,会将最新的值更新到主内存中去&br&(2)定义了几个原子操作,用于操作主内存和工作内存中的变量&br&(3)定义了volatile变量的使用规则&br&(4)happens-before,即先行发生原则,定义了操作A必然先行发生于操作B的一些规则,比如在同一个线程内控制流前面的代码一定先行发生于控制流后面的代码、一个释放锁unlock的动作一定先行发生于后面对于同一个锁进行锁定lock的动作等等,只要符合这些规则,则不需要额外做同步措施,如果某段代码不符合所有的happens-before规则,则这段代码一定是线程非安全的&/p&&p&&b&32、什么是CAS&/b&&/p&&p&CAS,全称为Compare and Set,即比较-设置。假设有三个操作数:内存值V、旧的预期值A、要修改的值B,当且仅当预期值A和内存值V相同时,才会将内存值修改为B并返回true,否则什么都不做并返回false。当然CAS一定要volatile变量配合,这样才能保证每次拿到的变量是主内存中最新的那个值,否则旧的预期值A对某条线程来说,永远是一个不会变的值A,只要某次CAS操作失败,永远都不可能成功。&/p&&p&&b&33、什么是乐观锁和悲观锁&/b&&/p&&p&(1)乐观锁:就像它的名字一样,对于并发间操作产生的线程安全问题持乐观状态,乐观锁认为竞争不总是会发生,因此它不需要持有锁,将比较-设置这两个动作作为一个原子操作尝试去修改内存中的变量,如果失败则表示发生冲突,那么就应该有相应的重试逻辑。&br&(2)悲观锁:还是像它的名字一样,对于并发间操作产生的线程安全问题持悲观状态,悲观锁认为竞争总是会发生,因此每次对某资源进行操作时,都会持有一个独占的锁,就像synchronized,不管三七二十一,直接上了锁就操作资源了。&/p&&p&&b&34、什么是AQS&/b&&/p&&p&简单说一下AQS,AQS全称为AbstractQueuedSychronizer,翻译过来应该是抽象队列同步器。&br&如果说java.util.concurrent的基础是CAS的话,那么AQS就是整个Java并发包的核心了,ReentrantLock、CountDownLatch、Semaphore等等都用到了它。AQS实际上以双向队列的形式连接所有的Entry,比方说ReentrantLock,所有等待的线程都被放在一个Entry中并连成双向队列,前面一个线程使用ReentrantLock好了,则双向队列实际上的第一个Entry开始运行。&br&AQS定义了对双向队列所有的操作,而只开放了tryLock和tryRelease方法给开发者使用,开发者可以根据自己的实现重写tryLock和tryRelease方法,以实现自己的并发功能。&/p&&p&&b&35、单例模式的线程安全性&/b&&/p&&p&老生常谈的问题了,首先要说的是单例模式的线程安全意味着:某个类的实例在多线程环境下只会被创建一次出来。单例模式有很多种的写法,我总结一下:&br&(1)饿汉式单例模式的写法:线程安全&br&(2)懒汉式单例模式的写法:非线程安全&br&(3)双检锁单例模式的写法:线程安全&/p&&p&&b&36、Semaphore有什么作用&/b&&/p&&p&Semaphore就是一个信号量,它的作用是限制某段代码块的并发数。Semaphore有一个构造函数,可以传入一个int型整数n,表示某段代码最多只有n个线程可以访问,如果超出了n,那么请等待,等到某个线程执行完毕这段代码块,下一个线程再进入。由此可以看出如果Semaphore构造函数中传入的int型整数n=1,相当于变成了一个synchronized了。&/p&&p&&b&37、Hashtable的size()方法中明明只有一条语句”return count”,为什么还要做同步?&/b&&/p&&p&这是我之前的一个困惑,不知道大家有没有想过这个问题。某个方法中如果有多条语句,并且都在操作同一个类变量,那么在多线程环境下不加锁,势必会引发线程安全问题,这很好理解,但是size()方法明明只有一条语句,为什么还要加锁?&br&关于这个问题,在慢慢地工作、学习中,有了理解,主要原因有两点:&br&(1)同一时间只能有一条线程执行固定类的同步方法,但是对于类的非同步方法,可以多条线程同时访问。所以,这样就有问题了,可能线程A在执行Hashtable的put方法添加数据,线程B则可以正常调用size()方法读取Hashtable中当前元素的个数,那读取到的值可能不是最新的,可能线程A添加了完了数据,但是没有对size++,线程B就已经读取size了,那么对于线程B来说读取到的size一定是不准确的。而给size()方法加了同步之后,意味着线程B调用size()方法只有在线程A调用put方法完毕之后才可以调用,这样就保证了线程安全性&br&(2)CPU执行代码,执行的不是Java代码,这点很关键,一定得记住。Java代码最终是被翻译成汇编代码执行的,汇编代码才是真正可以和硬件电路交互的代码。即使你看到Java代码只有一行,甚至你看到Java代码编译之后生成的字节码也只有一行,也不意味着对于底层来说这句语句的操作只有一个。一句”return count”假设被翻译成了三句汇编语句执行,完全可能执行完第一句,线程就切换了。&/p&&p&&b&38、线程类的构造方法、静态块是被哪个线程调用的&/b&&/p&&p&这是一个非常刁钻和狡猾的问题。请记住:线程类的构造方法、静态块是被new这个线程类所在的线程所调用的,而run方法里面的代码才是被线程自身所调用的。&br&如果说上面的说法让你感到困惑,那么我举个例子,假设Thread2中new了Thread1,main函数中new了Thread2,那么:&br&(1)Thread2的构造方法、静态块是main线程调用的,Thread2的run()方法是Thread2自己调用的&br&(2)Thread1的构造方法、静态块是Thread2调用的,Thread1的run()方法是Thread1自己调用的&/p&&p&&b&39、同步方法和同步块,哪个是更好的选择&/b&&/p&&p&同步块,这意味着同步块之外的代码是异步执行的,这比同步整个方法更提升代码的效率。请知道一条原则:同步的范围越少越好。&br&借着这一条,我额外提一点,虽说同步的范围越少越好,但是在Java虚拟机中还是存在着一种叫做锁粗化的优化方法,这种方法就是把同步范围变大。这是有用的,比方说StringBuffer,它是一个线程安全的类,自然最常用的append()方法是一个同步方法,我们写代码的时候会反复append字符串,这意味着要进行反复的加锁-&解锁,这对性能不利,因为这意味着Java虚拟机在这条线程上要反复地在内核态和用户态之间进行切换,因此Java虚拟机会将多次append方法调用的代码进行一个锁粗化的操作,将多次的append的操作扩展到append方法的头尾,变成一个大的同步块,这样就减少了加锁–&解锁的次数,有效地提升了代码执行的效率。&/p&&p&&b&40、高并发、任务执行时间短的业务怎样使用线程池?并发不高、任务执行时间长的业务怎样使用线程池?并发高、业务执行时间长的业务怎样使用线程池?&/b&&/p&&p&这是我在并发编程网上看到的一个问题,把这个问题放在最后一个,希望每个人都能看到并且思考一下,因为这个问题非常好、非常实际、非常专业。关于这个问题,个人看法是:&br&(1)高并发、任务执行时间短的业务,线程池线程数可以设置为CPU核数+1,减少线程上下文的切换&br&(2)并发不高、任务执行时间长的业务要区分开看:&br&a)假如是业务时间长集中在IO操作上,也就是IO密集型的任务,因为IO操作并不占用CPU,所以不要让所有的CPU闲下来,可以加大线程池中的线程数目,让CPU处理更多的业务&br&b)假如是业务时间长集中在计算操作上,也就是计算密集型任务,这个就没办法了,和(1)一样吧,线程池中的线程数设置得少一些,减少线程上下文的切换&br&(3)并发高、业务执行时间长,解决这种类型任务的关键不在于线程池而在于整体架构的设计,看看这些业务里面某些数据是否能做缓存是第一步,增加服务器是第二步,至于线程池的设置,设置参考(2)。最后,业务执行时间长的问题,也可能需要分析一下,看看能不能使用中间件对任务进行拆分和解耦。&/p&&figure&&img src=&https://pic4.zhimg.com/v2-d77aaf29b5068feace82_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&1270& data-rawheight=&847& class=&origin_image zh-lightbox-thumb& width=&1270& data-original=&https://pic4.zhimg.com/v2-d77aaf29b5068feace82_r.jpg&&&/figure&&p&&/p&
前言个人认为,学习,内容越多、越杂的知识,越需要进行深刻的总结,这样才能记忆深刻,将知识变成自己的。这篇文章主要是对多线程的问题进行总结的,因此罗列了40个多线程的问题。 这些多线程的问题,有些来源于各大网站、有些来源于自己的思考。可能有些…
&p&作者:&a href=&https://link.zhihu.com/?target=https%3A//github.com/nnngu& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&nnngu&/a&&br&GitHub:&a href=&https://link.zhihu.com/?target=https%3A//github.com/nnngu& class=& external& target=&_blank& rel=&nofollow noreferrer&&&span class=&invisible&&https://&/span&&span class=&visible&&github.com/nnngu&/span&&span class=&invisible&&&/span&&/a&&br&博客园:&a href=&https://link.zhihu.com/?target=http%3A//www.cnblogs.com/nnngu/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&http://www.cnblogs.com/nnngu&/a&&br&简书:&a href=&https://link.zhihu.com/?target=https%3A//www.jianshu.com/users/1df20d76ea5c& class=& external& target=&_blank& rel=&nofollow noreferrer&&&span class=&invisible&&https://www.&/span&&span class=&visible&&jianshu.com/users/1df20&/span&&span class=&invisible&&d76ea5c&/span&&span class=&ellipsis&&&/span&&/a&&br&知乎:&a href=&https://www.zhihu.com/people/nnngu/posts& class=&internal&&&span class=&invisible&&https://www.&/span&&span class=&visible&&zhihu.com/people/nnngu/&/span&&span class=&invisible&&posts&/span&&span class=&ellipsis&&&/span&&/a&&/p&&hr&&p&HashMap 是非线程安全的。在多线程条件下,容易导致死循环,具体表现为CPU使用率100%。因此多线程环境下保证 HashMap 的线程安全性,主要有如下几种方法:&/p&&ol&&li&使用 java.util.Hashtable 类,此类是线程安全的。&/li&&li&使用 java.util.concurrent.ConcurrentHashMap,此类是线程安全的。&/li&&li&使用 java.util.Collections.synchronizedMap() 方法包装 HashMap object,得到线程安全的Map,并在此Map上进行操作。&/li&&li&自己在程序的关键代码段加锁,保证多线程安全(不推荐)&/li&&/ol&&h2&接下来分析上面列举的几种方法实现并发安全的 HashMap 的原理:&/h2&&p&(一)java.util.Hashtable类:&/p&&p&查看该类的源码&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&public synchronized V get(Object key) {
…… //具体的实现省略,请参考 jdk实现
public synchronized V put(K key, V value) {
…… //具体的实现省略,请参考 jdk实现
public synchronized V remove(Object key) {
…… //具体的实现省略,请参考 jdk实现
&/code&&/pre&&/div&&p&
上面是 Hashtable 类提供的几个主要方法,包括 get(),put(),remove() 等。注意到&b&每个方法本身都是 synchronized 的,不会出现两个线程同时对数据进行操作的情况,因此保证了线程安全性,但是也大大的降低了执行效率&/b&。因此是不推荐的。&/p&&p&(二)使用 java.util.concurrent.ConcurrentHashMap 类:&/p&&p&该类是 HashMap 的线程安全版,与 Hashtable 相比, ConcurrentHashMap 不仅保证了访问的线程安全性,而且在效率上有较大的提高。&/p&&p&ConcurrentHashMap的数据结构如下:&/p&&p&&br&&/p&&figure&&img src=&https://pic2.zhimg.com/v2-2d4c9ed17c1c27a00b5ff48e2c8850a1_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&640& data-rawheight=&341& class=&origin_image zh-lightbox-thumb& width=&640& data-original=&https://pic2.zhimg.com/v2-2d4c9ed17c1c27a00b5ff48e2c8850a1_r.jpg&&&/figure&&p&&br&&/p&&p&可以看出,相对 HashMap 和 Hashtable, ConcurrentHashMap 增加了Segment 层,每个Segment 原理上等同于一个 Hashtable, ConcurrentHashMap 等同于一个 Segment 的数组。下面是 ConcurrentHashMap 的 put 和 get 方法:&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&final Segment segmentFor(int hash) {
return segments[(hash &&& segmentShift) & segmentMask];
public V put(K key, V value) {
if (value == null)
throw new NullPointerException();
int hash = hash(key.hashCode());
return segmentFor(hash).put(key, hash, value, false);
public V get(Object key) {
int hash = hash(key.hashCode());
return segmentFor(hash).get(key, hash);
&/code&&/pre&&/div&&p&向 ConcurrentHashMap 中插入数据(put) 或者 读取数据(get),首先都要将相应的 Key 映射到对应的 Segment,&b&因此不用锁定整个类, 只要对单个的 Segment 操作进行上锁操作就可以了&/b&。理论上如果有 n 个 Segment,那么最多可以同时支持 n 个线程的并发访问,从而大大提高了并发访问的效率。&/p&
作者: GitHub: 博客园: 简书: 知乎:HashMap 是非线程安全的。在多线程条件下,容易导致死循环,具体表现为CPU使用率100%。因此多线程环境下保证 HashM…
&figure&&img src=&https://pic4.zhimg.com/v2-82df13dca9d2767acb0a926_b.jpg& data-rawwidth=&4096& data-rawheight=&2734& class=&origin_image zh-lightbox-thumb& width=&4096& data-original=&https://pic4.zhimg.com/v2-82df13dca9d2767acb0a926_r.jpg&&&/figure&&p&&b&欢迎大家前往腾讯云+社区,获取更多腾讯海量技术实践干货哦~&/b&&/p&&blockquote&本文由&a href=&https://link.zhihu.com/?target=https%3A//cloud.tencent.com/developer/user/FfromSource%3Dwaitui& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&腾讯数据库技术 &/a& 发表于&a href=&https://link.zhihu.com/?target=https%3A//cloud.tencent.com/developer/column/3192%3FfromSource%3Dwaitui& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&云+社区专栏&/a&&br&提示:公众号展示代码会自动折行,建议横屏阅读。&/blockquote&&h2&&b&第一部分:SKIP LOCKED/NOWAIT订座功能实现&/b&&/h2&&p&订座在现实生活中是一种很常见的场景,比较常见的有火车票席位选择,电影院席位选择等等。那么如何实现订座功能呢?应用程序可能有很多种不同的实现方式,当然,肯定离不开数据库。这里将介绍一种纯数据库的实现方式。&/p&&p&设想我们有一张座位表如下:&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&CREATE TABLE seats (
seat_no INT PRIMARY KEY,
booked ENUM('YES', 'NO') DEFAULT 'NO') ENGINE=InnoDB;
&/code&&/pre&&/div&&p&表中有100个席位,从0到99。例如我们要预定席位2,3,我们可以先开启事务,锁定席位:&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&START TRANSACTION;SELECT * FROM seats WHERE seat_no IN (2,3) AND booked = 'NO' FOR UPDATE;
&/code&&/pre&&/div&&p&SELECT… FOR UPDATE语句返回结果有如下三种情况:&/p&&ul&&li&1.返回成功,并且结果集包含2和3,那么说明锁定成功。我们可以之行下一步操作,等待支付完成,并更新席位状态并提交事务,订座完成。
UPDATE seats SET booked = 'YES' WHERE seat_no IN (2,3)
COMMIT;&/li&&li&2.返回成功,但结果集为空,或者只包含2或者3,那么说明锁定失败。&/li&&li&3.很长时间不返回直到返回超时。比如席位2或者3已经被另一事务锁定,并且在等待支付完成或者发生其他情况,导致该事务一直未提交(commit)或者回滚(rollback)。返回超时默认需要等待50秒,我们可以通过修改innodb_lock_wait_timeout参数来配置合理的等待时间。超时之后返回的错误如下:
ERROR 1205 (HY000): Lock w try restarting transaction&/li&&/ul&&p&情况3对用户来说,意味着卡死,完全不能接受。为什么会发生等待?在InnoDB的锁系统(lock system)中,席位2如果被一个事务上了X(写锁)锁或者IX锁(意向更新锁),那么下一个事务要对席位2上X锁或者IX锁的事务,就要等待。这是由事务本身的特性(ACID)决定的。&/p&&p&那么是否有一种方法避免等待以及后续可能发生的超时呢?MySQL 8.0 提供的新功能SKIP LOCKED/NOWAIT就可以。 SKIP LOCKED的意思是跳过那些已经被其他事务锁定了的席位。使用如下SKIP LOCKED语句进行席位锁定,那么返回的结果集可能为空,2或3,2和3。当结果集不为空时,返回的席位即被锁定成功。&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&SELECT * FROM seats WHERE seat_no IN (2,3) AND booked = 'NO'FOR UPDATE SKIP LOCKED;
&/code&&/pre&&/div&&p&NOWAIT的意思是如果碰到被其他事务锁定的席位,不等待并直接返回错误。使用如下NOWAIT语句进行席位锁定,那么返回结果集2和3,要么返回错误。&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&SELECT * FROM seats WHERE seat_no IN (2,3) AND booked = 'NO'FOR UPDATE NOWAIT;
&/code&&/pre&&/div&&p&如果返回错误,如下:&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&ERROR 3572 (HY000): Do not wait for lock.
&/code&&/pre&&/div&&p&如果成功锁定两个席位,通过如下语句查询锁系统的状态:&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&SELECT thread_id, object_name, lock_type, lock_mode, lock_data, lock_status FROM performance_schema.data_+-----------+-------------+-----------+-----------+-----------+-------------+| thread_id | object_name | lock_type | lock_mode | lock_data | lock_status |
+-----------+-------------+-----------+-----------+-----------+-------------+|
43 | seats
43 | seats
42 | seats
42 | seats
42 | seats
+-----------+-------------+-----------+-----------+-----------+-------------+
&/code&&/pre&&/div&&p&SKIP LOCKED还可以很方便的用来进行随机分配席位。例如我们只需要锁定两个空的席位就可以通过如下语句实现。&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&SELECT * FROM seats WHERE booked = 'NO' LIMIT 2 FOR UPDATE SKIP LOCKED;
&/code&&/pre&&/div&&p&SKIP LOCKED/NOWAIT功能只针对行锁(record lock),不包括表锁(table lock),元数据锁(metadata lock/MDL)。因此,带有SKIP LOCKED/NOWAIT的查询语句依然可能会因为表锁或元数据库锁而阻塞。元数据锁是MySQL Server层用来保护数据库对象的并发访问的一致性而创建的,数据库对象不仅包括表,同时包括库,函数,存储过程,触发器,事件等等。表和行锁是InnoDB存储引擎内部为了保证事务的一致性而创建的不同粒度的锁。&/p&&p&另外,SKIP LOCKED/NOWAIT还可以配合FOR SHARE使用,并且可以与单表绑定。例如:&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&SELECT seat_noFROM seats JOIN seat_rows USING ( row_no )WHERE seat_no IN (2,3) AND seat_rows.row_no IN (12)AND booked = 'NO'FOR UPDATE OF seats SKIP LOCKEDFOR SHARE OF seat_rows NOWAIT;
&/code&&/pre&&/div&&h2&&b&第二部分:SKIP LOCKED/NOWAIT在InnoDB中的代码实现&/b&&/h2&&p&在InnoDB中,实现SKIP LOCKED/NOWAIT具体实现如下:&/p&&ul&&li&1.增加新的查询模式
enum select_mode {
SELECT_ORDINARY = 0,
/* default behaviour &i&/
SELECT_SKIP_LOCKED,
/&/i& skip the row if row is locked &i&/
SELECT_NO_WAIT
/&/i& return immediately if row is locked */
};&/li&&li&2.在查询开始前,设置查询模式
ha_innobase::store_lock():
/* Set select mode for SKIP LOCKED / NO_WAIT */
switch (lock_type) {
case TL_READ_SHARED_SKIP_LOCKED:
case TL_WRITE_SKIP_LOCKED:
m_prebuilt-&select_mode = SELECT_SKIP_LOCKED;
case TL_READ_SHARED_NO_WAIT:
case TL_WRITE_NO_WAIT:
m_prebuilt-&select_mode = SELECT_NO_WAIT;
m_prebuilt-&select_mode = SELECT_ORDINARY;
}&/li&&li&3.上锁函数中,如果记录已被锁定,针对对不同查询模式进行相应处理:
lock_rec_lock_slow():
if (wait_for != NULL) {
switch (sel_mode) {
case SELECT_SKIP_LOCKED:
err = DB_SKIP_LOCKED;
case SELECT_NO_WAIT:
err = DB_LOCK_NOWAIT;
&/li&&li&4.查询中对上锁结果进行处理:
row_search_mvcc():
case DB_SKIP_LOCKED:
goto next_ 对DB_LOCK_NOWAIT的处理则是回滚当前语句(statement),见函数row_mysql_handle_errors()。&/li&&li&5.二级索引(secondary index)的处理 在InnoDB中,对表中记录的锁定分两种情况。第一种是查询使用是聚集索引(cluster index),那么直接对聚集索引的记录上锁;第二中是查询使用的是二级索引,那么首先对二级索引的记录上锁,然后根据二级索引的记录,找到对应的聚集索引记录进行上锁。 所以,对于第一部分订座的席位表中,如果存在二级索引,对于锁定表中一条记录而言,最终锁定成功与否,还是以锁定聚集索引记录为准。&/li&&/ul&&p&SKIP LOCKED/NOWAIT可以非常高效地实现订座这个场景,作为InnoDB部分(WL#8919: InnoDB: Implement NOWAIT and SKIP LOCKED)的原作者,我也期待着大家来分享该功能更多的使用场景。&/p&&h2&&b&参考链接:&/b&&/h2&&ul&&li&1.MySQL 8.0.1: Using SKIP LOCKED and NOWAIT to handle hot rows&/li&&li&2.WL#3597: Implement NOWAIT and SKIP LOCKED&/li&&li&3.WL#8919: InnoDB: Implement NOWAIT and SKIP LOCKED&/li&&li&4.WL#6657: PERFORMANCE_SCHEMA, DATA LOCKS&/li&&/ul&&hr&&p&&a href=&https://link.zhihu.com/?target=http%3A//weixin.qq.com/r/iz_7o0jEDRVyrQzA92qZ& class=& external& target=&_blank& rel=&nofollow noreferrer&&&span class=&invisible&&http://&/span&&span class=&visible&&weixin.qq.com/r/iz_7o0j&/span&&span class=&invisible&&EDRVyrQzA92qZ&/span&&span class=&ellipsis&&&/span&&/a& (二维码自动识别)&/p&&p&&br&&/p&&blockquote&腾讯数据库技术团队对内支持微信红包,彩票、数据银行等集团内部业务,对外为腾讯云提供各种数据库产品,如CDB、CTSDB、CKV、CMongo, 腾讯数据库技术团队专注于增强数据库内核功能,提升数据库性能,保证系统稳定性并解决用户在生产过程中遇到的问题,并对生产环境中遇到的问题及知识进行分享。&br&&b&问答&/b&&br&&a href=&https://link.zhihu.com/?target=https%3A//cloud.tencent.com/developer/ask/65732%3FfromSource%3Dwaitui& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&更新MySQL主键&/a&&br&&b&相关阅读&/b&&br&&a href=&https://link.zhihu.com/?target=https%3A//cloud.tencent.com/developer/article/FfromSource%3Dwaitui& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Linux调度原理介绍和应用(前篇)&/a&&br&&a href=&https://link.zhihu.com/?target=https%3A//cloud.tencent.com/developer/article/FfromSource%3Dwaitui& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&如何备份你的MySQL数据库&/a&&br&&a href=&https://link.zhihu.com/?target=https%3A//cloud.tencent.com/developer/article/FfromSource%3Dwaitui& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&MySQL 8.0 版本功能变更介绍&/a&&/blockquote&&p&&b&此文已由作者授权腾讯云+社区发布,原文链接:&a href=&https://link.zhihu.com/?target=https%3A//cloud.tencent.com/developer/article/FfromSource%3Dwaitui& class=& external& target=&_blank& rel=&nofollow noreferrer&&&span class=&invisible&&https://&/span&&span class=&visible&&cloud.tencent.com/devel&/span&&span class=&invisible&&oper/article/1163316?fromSource=waitui&/span&&span class=&ellipsis&&&/span&&/a&&/b&&/p&&p&&b&欢迎大家前往腾讯云+社区或关注云加社区微信公众号(QcloudCommunity),第一时间获取更多海量技术实践干货哦~&/b&&/p&&p&海量技术实践经验,尽在&a href=&https://zhuanlan.zhihu.com/%20https://cloud.tencent.com/developer?fromSource=waitui& class=&internal&&云加社区&/a&!&/p&
欢迎大家前往腾讯云+社区,获取更多腾讯海量技术实践干货哦~本文由 发表于 提示:公众号展示代码会自动折行,建议横屏阅读。第一部分:SKIP LOCKED/NOWAIT订座功能实现订座在现实生活中是一种很常见的场景,比较常见的有火车票席…
&figure&&img src=&https://pic4.zhimg.com/v2-3fbc7eff45df433e2fd3_b.jpg& data-rawwidth=&1746& data-rawheight=&868& class=&origin_image zh-lightbox-thumb& width=&1746& data-original=&https://pic4.zhimg.com/v2-3fbc7eff45df433e2fd3_r.jpg&&&/figure&&p&刚实习的时候用过AngularJS,那时候真的是连原生JavaScript都不会写,依样画葫芦做了几个管理后台。然后突然换项目了,AngularJS就不写了,感觉前前后后接触了一年多的AngularJS,结果只懂点皮毛。&/p&&p&最近又有个管理后台的需求,决定再拾起,但现在是升级版的Angular了。终于,有机会好好再看一眼Angular了,这次希望能深入一点了解。&/p&&p&本文是笔者在学习开发过程中的总结输出,目的在于让初次接触Angular的开发者对该框架能有整体的认识,并且能快速上手开发工作。&/p&&h2&AngularJS VS Angular&/h2&&p&AngularJS最大版本号只有1.x,2.x/4.x的版本号都是针对于全新的框架Angular。但不能说Angular和AngularJS一点关系都没有,你看名字这么像,是吧?!回忆一下AngularJS被人念念不忘的特性,双向数据绑定,MVC,指令,服务,过滤器,模块化,脏检查机制,依赖注入,Scope,路由,表单校验等等。&/p&&p&看下AngularJS到Angular的过程中,哪些概念被保留下来,哪些被剔除了(所谓的取其精华,去其糟粕)。&/p&&p&剔除的部分:&/p&&ul&&li&ng-controller指令:控制器主要是业务逻辑的控制部分&/li&&li&$scope概念:很强大又很复杂&/li&&li&数据双向绑定:数据双向流通可能导致数据的震荡(故才有最多检查10次的限制,10次之后还不稳定就报错)&/li&&/ul&&p&保留/改善的部分:&/p&&ul&&li&路由嵌套:AngularJS自带的路由系统是不能嵌套路由的,到了Angular你想怎么嵌套就怎么嵌套&/li&&li&过滤器(Filter)变成管道(Pipe),概念的变化&/li&&li&依赖注入机制:直接在构造器中注入,还有分层依赖注入的概念&/li&&li&指令写法:&/li&&ul&&li&(事件) ng-click变成(click)&/li&&li&[属性] href = &{{}}&可以写成 [href]&/li&&li&[(ngModel)]代替以前的ng-model&/li&&li&*ngFor 代替 ng-repeat,不适用于对象,适用任何有Symbol.iterator属性的数据结构(能用for...of来访问),比如数组,集合等&/li&&li&*ngIf 代替 ng-if,去掉ng-show,ng-hide&/li&&/ul&&li&对移动端的支持&/li&&li&模版,数据绑定,服务,模块,脏检查机制等&/li&&/ul&&p&新增的部分:&/p&&ul&&li&组件化:Angular的核心所在&/li&&li&Typescript作为默认的开发语言&/li&&li&ZoneJS监听所有(可能导致数据变化)的异步事件&/li&&li&支持服务端渲染&/li&&/ul&&h2&Angular Cli&/h2&&p&Angular团队为开发者提供了一个开箱即用(out of the box)的脚手架工具:Angular Cli。我们再也不用担心在项目初始化时,要搭建配置一系列的工具,比如webpack,karma,tslint,protractor等。&/p&&p&操作很简单,只要运行如下命令行就搞定了。&/p&&figure&&img src=&https://pic3.zhimg.com/v2-fe44c38b7911_b.jpg& data-rawwidth=&694& data-rawheight=&410& class=&origin_image zh-lightbox-thumb& width=&694& data-original=&https://pic3.zhimg.com/v2-fe44c38b7911_r.jpg&&&/figure&&p&具体的语法教程可参考&a href=&https://link.zhihu.com/?target=https%3A//github.com/angular/angular-cli/wiki& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&这里&/a&。&/p&&p&安装之后,文件目录如下:&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&my-dream-app
// 端到端测试
app.e2e-spec.ts
tsconfig.e2e.json
node_modules/...
angular-cli.json
.editorconfig
// 编辑器配置
.gitignore
// git忽略文件配置
karma.conf.js
// karma配置
package.json
// npm配置
protractor.conf.js
// 测试配置项
// 项目说明
tsconfig.json
// ts编辑器的配置
tslint.json
// tslint配置项
&/code&&/pre&&/div&&p&我们需要关注的是src文件夹,这里存放我们所有的源代码,开发的时候基本都在src中。&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&src
// 代码的主要文件夹
app.component.css
// 根组件样式
app.component.html
// 根组件模版
app.component.spec.ts// 根组件测试
app.component.ts
// 根组件脚本
app.module.ts
// 静态资源
// 保存空文件夹
environments
// 环境配置
environment.prod.ts
environment.ts
favicon.ico
index.html
// 页面主入口
// 脚本主入口
polyfills.ts
// 兼容浏览器
styles.css
// 全局css样式
// 单元测试主入口
&/code&&/pre&&/div&&h2&模块&/h2&&p&Angular很重要的概念之一仍然是模块。Angular整个框架就是由很多个模块组成的,而不同的模块需要从不同的地方导入。打开package.json文件,可以看到依赖的angular包可能是这样的:&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&&@angular/common&: &^2.3.1&,
&@angular/compiler&: &^2.3.1&,
&@angular/core&: &^2.3.1&,
&@angular/forms&: &^2.3.1&,
&@angular/http&: &^2.3.1&,
&@angular/platform-browser&: &^2.3.1&,
&@angular/platform-browser-dynamic&: &^2.3.1&,
&@angular/router&: &^3.3.1&,
&/code&&/pre&&/div&&p&来简单看下这些angular包中包含了哪些常用的模块(至少目前为止,我觉得常用的)。&/p&&ul&&li&@angular/core:这里包含了很多常用的模块&/li&&ul&&li&NgModule:模块定义装饰器&/li&&li&Component:组件定义装饰器&/li&&li&Directive:指令定义装饰器&/li&&li&Pipe :管道定义装饰器&/li&&li&PipeTransform:管道接口&/li&&li&Injectable:服务定义装饰器&/li&&li&ElmentRef:元素引用&/li&&li&ViewChild:获取子元素&/li&&li&Render:渲染&/li&&li&Input:接受参数输入&/li&&li&Output:事件输出&/li&&li&EventEmitter:触发自定义事件&/li&&/ul&&li&@angular/common&/li&&ul&&li&CommonModule:通用模块,包含内置指令ngIf,ngFor&/li&&/ul&&li&@angular/forms&/li&&ul&&li&FormsModule:定义模版驱动表单&/li&&li&ReactiveFormsModule:定义响应式表单&/li&&li&FormGroup, FormArray, FormControl, FormBuilder:响应式表单元素&/li&&li&Validators:表单校验&/li&&/ul&&li&@angular/http&/li&&ul&&li&HttpModule:http请求模块&/li&&/ul&&li&@angular/router&/li&&ul&&li&RouterModule 路由模块&/li&&li&Routes 路由数据结构&/li&&/ul&&li&@angular/platform-browser&/li&&ul&&li&platformBrowser:AoT编译&/li&&li&BrowserModule:浏览器支持,注意该模块导入了CommonModule,然后导出去,所以引用了这个模块也就引用了CommonModule&/li&&/ul&&li&@angular/platform-browser-dynamic&/li&&ul&&li&platformBrowserDynamic:JIT编译&/li&&/ul&&/ul&&p&&br&&/p&&p&以上模块都是Angular框架中的自带模块,而我们开发的完整单元也是模块。&b&一个应用中至少要有一个模块,也就是根模块。&/b& 一些共享的功能属性我们可以抽象出来,成为共享模块。然后就是一些特性模块了。&/p&&p&模块的组成由组件,服务,指令,管道等等组成,这些概念会在下面讲到。定义模块的语法如下:&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&@NgModuel({
declarations: [],
// 用到的组件,指令,管道
providers: [],
// 依赖注入服务
imports: [],
// 导入需要的模块
exports: [],
// 导出的模块,跨模块交流
entryComponents: [] // 需提前编译好的模块
bootstrap: []
// 设置根组件
export class AppModule { }
&/code&&/pre&&/div&&blockquote&所有用到的组件,指令,管道,模块都需要事先在模块中声明好,才能在具体组件中使用。服务可以在模块,组件,指令中的providers声明,也可以直接在运行时提供(参见Trotyl Yu的&a href=&https://link.zhihu.com/?target=http%3A//plnkr.co/edit/fiOq9J3BpD2csuV3FGhu%3Fp%3Dpreview& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&例子&/a&)。&/blockquote&&p&一般情况下,在根模块的bootstrap中设置启动的根组件即可,但也可以动态处理(参见Trotyl Yu的&a href=&https://link.zhihu.com/?target=http%3A//plnkr.co/edit/fzJtvnPKTyzOB8UILtoD%3Fp%3Dpreview& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&例子&/a&)。&/p&&p&那如何启动根模块呢?&/p&&p&在入口脚本中,也就是Angular Cli项目中的main.ts中,启动如下:&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&// 导入需要模块
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { AppModule } from './app/app.module';
// 编译启动模块
platformBrowserDynamic().bootstrapModule(AppModule);
&/code&&/pre&&/div&&p&至此,我们对模块有所了解,也知道了模块的定义。&/p&&h2&组件&/h2&&p&自从采用组件化的React大火之后,目前市面上炙手可热的框架全都采用了组件化的理念,Angular当然也不能落后了。可以说,组件化是Angular的核心理念。按Angular在中国的布道者 &a class=&member_mention& href=&https://www.zhihu.com/people/4866fceb246d9ea621fd5& data-hash=&4866fceb246d9ea621fd5& data-hovercard=&p$b$4866fceb246d9ea621fd5&&@大漠穷秋&/a&的话来说,就是:&/p&&blockquote&Angular的核心概念是组件,模块化机制NgModule是为组件化服务的,实际上所有其它机制都是围绕组件化而来的。只有从组件化这个角度才能把握Angular的精神内核。&/blockquote&&p&组件通常都是由模版和业务逻辑组成,看一下如何用Angular写一个很简单的组件:&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&// hello.component.ts
import { Component } from '@angular/core';
@Component({
selector: 'hello',
template: '&p& {{greeting}} &/p&',
styles: [`p { color:}`]
export class HelloComponent{
private greeting:
constructor(){
this.greeting = 'Hello, Angular2!';
&hello&&/hello&
// 渲染结果
&p& Hello, Angular2! &/p&
&/code&&/pre&&/div&&p&定义类HelloComponent的时候,加上装饰器@Component(Typescript语法),告诉Angular这个类是组件类。里面的数据称之为元数据(metadata),selector属性说明了该组件对外的使用标记,template就是组件的模版,styles是组件的样式。而HelloComponent中定义的就是该组件的业务逻辑了。&/p&&p&如果模版内容太多,可以单独写在一个html文件中,用templateUrl属性引入;同理,样式文件用styleUrls引入。&/p&&h2&组件生命周期&/h2&&p&正如其他框架的组件,Angular的组件也是有生命周期这个概念。在不同的阶段不同的场景下,可以调用不同的生命周期函数钩子(hook)。&/p&&figure&&img src=&https://pic4.zhimg.com/v2-efac904ee6_b.jpg& data-rawwidth=&200& data-rawheight=&283& class=&content_image& width=&200&&&/figure&&ul&&li&constructor:构造器函数,一般用于注入服务&/li&&li&ngOnChanges:检测到输入数据变化,首次触发发生在ngOnInit前。注意对象的属性发生变化时监听不到&/li&&li&ngOnInit:组件初始化,通常会设置一些初始值&/li&&li&ngDoCheck:手动触发更新检查&/li&&ul&&li&ngAfterContentInit:内容初始化到组件之后&/li&&li&ngAfterContentChecked:内容变更检测之后&/li&&li&ngAfterViewInit:视图 初始化之后&/li&&li&ngAfterViewChecked:视图发生变化检测之后,这个可以用来保证用户视图的及时更新&/li&&/ul&&li&ngOnDestroy:组件注销时的清理工作,通常用于移除事件监听,退订可观察对象等&/li&&/ul&&p&具体说明可以参考&a href=&https://link.zhihu.com/?target=https%3A//angular.io/guide/lifecycle-hooks& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&这里&/a&。&/p&&h2&组件通信&/h2&&p&可以想像得到,组件化的页面结构最终会形成一颗组件树。盗一张Vue的图:&/p&&figure&&img src=&https://pic2.zhimg.com/v2-c7b535e898bc597be2f8b_b.jpg& data-rawwidth=&1406& data-rawheight=&544& class=&origin_image zh-lightbox-thumb& width=&1406& data-original=&https://pic2.zhimg.com/v2-c7b535e898bc597be2f8b_r.jpg&&&/figure&&p&不可避免,我们需要考虑父子组件之间的参数传递问题。Anuglar提供的通信方式有如下几种:&/p&&ul&&li&父组件到子组件:父组件用属性绑定将值传入,子组件通过@Input来接收。&/li&&/ul&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&// 父组件
import { Component } from '@angular/core';
@Component({
selector: 'hero-parent',
template: `&h2& heroes &/h2&
&hero-child *ngFor=&let hero of heroes&
[hero]=&hero& &
&/hero-child&
export class HeroParentComponent {
heroes = [{
name: 'John'
name: 'Lily'
import { Component, Input } from '@angular/core';
import { Hero } from './hero';
@Component({
selector: 'hero-child',
template: `
&h3&{{hero.name}}&/h3&
export class HeroChildComponent {
@Input() hero: H
&/code&&/pre&&/div&&ul&&li&子组件到父组件:子组件自定义事件用@Output传出,父组件用事件绑定获取。&/li&&/ul&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&// 子组件
import { Component, EventEmitter, Output } from '@angular/core';
@Component({
selector: 'my-voter',
template: `
&h4&{{name}}&/h4&
&button (click)=&vote(true)&&Agree&/button&
export class VoterComponent {
@Output() onVoted = new EventEmitter&boolean&();
vote(agreed: boolean) {
this.onVoted.emit(agreed);
import { Component } from '@angular/core';
@Component({
selector: 'vote-taker',
template: `
&h2&Should mankind colonize the Universe?&/h2&
&h3&Agree: {{agreed}}, Disagree: {{disagreed}}&/h3&
&my-voter *ngFor=&let voter of voters&
[name]=&voter&
(onVoted)=&onVoted($event)&&
&/my-voter&
export class VoteTakerComponent {
agreed = 0;
disagreed = 0;
voters = ['Mr. IQ', 'Ms. Universe', 'Bombasto'];
onVoted(agreed: boolean) {
agreed ? this.agreed++ : this.disagreed++;
&/code&&/pre&&/div&&ul&&li&子组件引用:在父组件模版中添加对子组件的引用,即可通过该子组件去访问子组件的方法。&/li&&/ul&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&&h3&Countdown to Liftoff (via local variable)&/h3&
&button (click)=&timer.start()&&Start&/button&
&button (click)=&timer.stop()&&Stop&/button&
&div class=&seconds&&{{timer.seconds}}&/div&
&countdown-timer #timer&&/countdown-timer&
&/code&&/pre&&/div&&ul&&li&@ViewChild():类似的,也可以在脚本中用@ViewChild()来获取子组件&/li&&/ul&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&import { AfterViewInit, ViewChild } from '@angular/core';
import { Component }
from '@angular/core';
import { CountdownTimerComponent }
from './countdown-timer.component';
@Component({
selector: 'countdown-parent-vc',
template: `
&h3&Countdown to Liftoff (via ViewChild)&/h3&
&button (click)=&start()&&Start&/button&
&button (click)=&stop()&&Stop&/button&
&div class=&seconds&&{{ seconds() }}&/div&
&countdown-timer&&/countdown-timer&
export class CountdownViewChildParentComponent implements AfterViewInit {
@ViewChild(CountdownTimerComponent)
private timerComponent: CountdownTimerC
seconds() { return 0; }
ngAfterViewInit() {
setTimeout(() =& this.seconds = () =& this.timerComponent.seconds, 0);
start() { this.timerComponent.start(); }
stop() { this.timerComponent.stop(); }
&/code&&/pre&&/div&&ul&&li&将数据保存在服务中&/li&&li&@ngrx/store:参见&a href=&https://zhuanlan.zhihu.com/p/& class=&internal&&【译】手把手教你用ngrx管理Angular状态&/a&&/li&&/ul&&h2&模板与数据绑定&/h2&&p&模版说白了就是html的内容,常规的html基本都是静态内容,而模版结合了框架中的新语法使得html动态化。来看看Angular中的模版有什么便利的语法:&/p&&ul&&li&插值绑定:双花括号{{}}&/li&&/ul&&p&我们可以看到上一节组件例子中的{{greeting}}就是插值绑定。不仅可以获取变量的值,还可以直接写表达式。&/p&&ul&&li&属性(Property)绑定&/li&&/ul&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&&input [value]='myData'&
&/code&&/pre&&/div&&p&还有其他的,比如样式绑定:&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&&div [ngClass]=&{special: isSpecial}&&&/div&
&/code&&/pre&&/div&&blockquote&注意点:property和attribute不一样,想要绑定attribute,你需要写成property。比如:&/blockquote&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&&tr&&td colspan=&{{1 + 1}}&&Three-Four&/td&&/tr&
&/code&&/pre&&/div&&p&你将会得到如下错误信息:&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&Template parse errors:
Can't bind to 'colspan' since it isn't a known native property
&/code&&/pre&&/div&&p&你需要改写成这样:&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&&tr&&td [attr.colspan]=&1 + 1&&One-Two&/td&&/tr&
&tr&&td attr.colspan=&{{1 + 1}}&&One-Two&/td&&/tr&
&/code&&/pre&&/div&&ul&&li&事件绑定&/li&&/ul&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&&input (keyup)='handle($event)' &
&/code&&/pre&&/div&&p&可以是原生的事件:click,change,keydown,mousemove等,也可以是自定义事件,也可以是指令事件,比如ngSubmit。&/p&&ul&&li&双向绑定&/li&&/ul&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&&input [(ngModel)] = 'data'&
// 双向绑定的背后其实是单向绑定和事件触发,等价于下面
&input [ngModel]=&data& (ngModelChange)=&data=$event&&
&/code&&/pre&&/div&&blockquote&注意点:使用ngModel,需要引入FormsModule模块。&/blockquote&&p&还有些内置的指令:&/p&&ul&&li&模版引用变量(# / ref-)&/li&&/ul&&p&可以在元素上用#或者ref-前缀来标记这个元素,然后在其他地方引用。&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&&input #fax placeholder=&fax number&&
( &input ref-fax placeholder=&fax number&& )
&button (click)=&callFax(fax.value)&&Fax&/button&
&/code&&/pre&&/div&&ul&&li&*ngIf:控制内容的有无&/li&&/ul&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&&div *ngIf=&show&& Can you see this? &/div&
&/code&&/pre&&/div&&p&如果还有else部分,可以如下操作:&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&&div *ngIf=& else elseBlock&& Can you see this? &/div&
&ng-template #elseBlock&
else block
&/ng-template&
&/code&&/pre&&/div&&ul&&li&*ngFor:循环&/li&&/ul&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&&div *ngFor=& let i=index& {{i}}: {{hero.name}}&/div&
&/code&&/pre&&/div&&p&具体的模版语法可以参考&a href=&https://link.zhihu.com/?target=https%3A//angular.io/guide/template-syntax& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&这里&/a&。&/p&&h2&路由&/h2&&p&一个模块有了多个组件之后,需要用路由来配置哪个url呈现哪个组件。&/p&&p&首先,我们需要在入口页面的index.html中配置根路径:&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&...
&base href=&/&&
&/code&&/pre&&/div&&p&然后创建一个路由模块:&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&import { NgModule }
from '@angular/core';
import { RouterModule, Routes }
from '@angular/router';
// 路由配置
const appRoutes: Routes = [
{ path: 'home', component: HomeComponent },
{ path: 'heroes', component: HeroesComponent },
{ path: '',
redirectTo: '/home', pathMatch: 'full' },
{ path: '**', component: PageNotFoundComponent }
@NgModule({
imports: [
RouterModule.forRoot(appRoutes)
exports: [
RouterModule
export class AppRoutingModule {}
&/code&&/pre&&/div&&p&在主模块中导入配置好的路由模块:&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&import { NgModule }
from '@angular/core';
import { BrowserModule }
from '@angular/platform-browser';
import { FormsModule }
from '@angular/forms';
@NgModule({
imports: [
BrowserModule,
FormsModule,
AppRoutingModule
declarations: [
AppComponent,
HomeComponent,
HeroesComponent,
PageNotFoundComponent
bootstrap: [ AppComponent ]
export class AppModule { }
&/code&&/pre&&/div&&p&而在页面中需要一个容器&router-outlet&&/router-outlet&去承载:&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&import { Component } from '@angular/core';
@Component({
selector: 'my-app',
template: `
&h1&Angular Router&/h1&
&a routerLink=&/home& routerLinkActive=&active&&Home&/a&
&a routerLink=&/heroes& routerLinkActive=&active&&Heroes&/a&
&router-outlet&&/router-outlet&
export class AppComponent { }
&/code&&/pre&&/div&&p&上面代码中的routerLink定义了用户点击后的路由跳转,routerLinkActive定义该路由激活时的样式类。&/p&&p&路由上还可以带上一些索引参数:&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&{ path: 'heroes/:id', component: HeroesComponent },
&/code&&/pre&&/div&&p&获取的方式:&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&import { ActivatedRoute, Params }
from '@angular/router';
export class a {
constructor(
private route: ActivatedRoute
// 路由参数
this.route.params
&/code&&/pre&&/div&&p&当模块很多,路由也很多的时候,我们可以使用模块懒加载的方式。懒加载的方式也很简单,在配置路由的时候修改如下即可:&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&const routes: Routes = [
// 默认转到订单管理
redirectTo: '/order',
pathMatch: 'full'
path: 'order',
loadChildren: './order/order.module#OrderModule'
path: 'warehouse',
loadChildren: './warehouse/warehouse.module#WarehouseModule'
path: 'statistics/sales',
component: SalesComponent
// 在子模块中用RouterModule.forChild
import { NgModule } from '@angular/core';
import { RouterModule } from '@angular/router';
import { OrderComponent } from './order.component';
const orderRoutes = [
component: OrderComponent
@NgModule({
imports: [RouterModule.forChild(orderRoutes)],
exports: [RouterModule]
export class OrderRoutingModule {
&/code&&/pre&&/div&&h2&服务与依赖注入&/h2&&p&服务是什么概念?可以简单地认为它是一个功能模块,重要在于它是单例对象,并且可以注入到其他的地方使用。&/p&&p&依赖注入是来自后端的概念,其实就是自动创建一个实例,省去每次需要手动创建的麻烦。&/p&&p&在Angular中定义一个服务很简单,主要在类之前加上@Injectable装饰器的功能。这是最常见的依赖注入方式useClass,其他具体参见&a href=&https://link.zhihu.com/?target=https%3A//angular.io/guide/dependency-injection& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&这里&/a&。&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&import { Injectable } from '@angular/core';
@Injectable()
export class Service {
counter: number = 0;
getData(){
return this.counter++;
&/code&&/pre&&/div&&p&然后在模块的providers中声明:&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&import { Service } from './service';
@NgModule({
imports: [
declarations: [
providers: [ Service ],
// 注入服务
bootstrap: [...]
export class AppModule {
&/code&&/pre&&/div&&p&使用的时候需要在构造器中建立关联:&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&import { Component } from '@angular/core';
import { Service } from './service';
@Component({
selector: 'my-app',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
export class AppComponent {
constructor(public service: Service) {
// this.service被成功注入
// 相当于 this.service = new Service();
// 然后可以调用服务
this.service.getData();
&/code&&/pre&&/div&&p&由于该服务是在模块中注入,所以该模块中的所有组件使用这个服务时,使用的都是同一个实例。&/p&&p&除了在模块中声明,还可以在组件中声明。假设AppComponent下还有组件HomeComponent,此时我们在AppComponent中注入这个服务:&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&import { Component } from '@angular/core';
import { Service } from './service';
@Component({
selector: 'my-app',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css'],
providers: [ Service ],
// 注入服务
export class AppComponent {
constructor(public service: Service) {
// this.service被成功注入
// 相当于 this.service = new Service();
// 然后可以调用服务
this.service.getData();
&/code&&/pre&&/div&&p&如果HomeComponent也使用了这个服务,那它使用的将是同一个实例。这个可以从Service中的数据变化来看出。&/p&&p&Angular还有个分层依赖注入的概念,也就是说,你可以为任一组件创建自己独立的服务。就像上面的例子,如果想要HomeComponent不和它的父组件同使用一个服务实例的话,只要在该组件中重新注入即可:&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&...
@Component({
selector: 'home',
templateUrl: './home.component.html',
styleUrls: ['./home.component.css'],
providers: [ Service ],
// 重新注入服务
export class HomeComponent {
&/code&&/pre&&/div&&p&对于前后端的接口,通常会写成服务。下面说下请求后端数据这块应该怎么写。在模块这节中提过,http有专门的HttpModule模块处理请求。首先要在模块中导入HttpModule,然后引入http服务,调用相应的请求方法即可。&/p&&div class=&highlight&&&pre&&code class=}

我要回帖

更多关于 微信上面用户名没有了 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信