当前位置: 首页 > 编程日记 > 正文

没听说过这些,就不要说你懂并发了,three。

引言

 

  很久没有跟大家再聊聊并发了,今天LZ闲来无事,跟大家再聊聊并发。由于时间过去的有点久,因此LZ就不按照常理出牌了,只是把自己的理解记录在此,如果各位猿友觉得有所收获,就点个推荐或者留言激励下LZ,如果觉得浪费了自己宝贵的时间,也可以发下牢骚。

  好了,废话就不多说了,现在就开始咱们的并发之旅吧。

 

并发编程的简单分类

  

  并发常见的编程场景,一句话概括就是,需要协调多个线程之间的协作,已保证程序按照自己原本的意愿执行。那么究竟应该如何协调多个线程?

  这个问题比较宽泛,一般情况下,我们按照方式的纬度去简单区分,有以下两种方式:

  1,第一种是利用JVM的内部机制。

  2,第二种是利用JVM外部的机制,比如JDK或者一些类库。

  第一种方式一般是通过synchronized关键字等方式去实现,第二种则一般是使用JDK当中的类去手动实现。两种方式十分相似,他们的区别有点类似于C/C++和Java的垃圾搜集方式的区别,C/C++手动释放内存的方式更加灵活和高效,而Java自动垃圾搜集的方式则更加安全和方便。

  并发一直被认为是编程当中的高级特性,也是很多大公司在面试的时候都比较在意的部分,因此掌握好并发的简单技巧,还是能够让自己的技术沉淀有质的飞跃的。

  

详解JVM内部机制——同步篇

 

  JVM有很多内部同步机制,这在有的时候是非常值得我们去使用和学习的,接下来咱们就一起看看,JVM到底提供了哪些内部的同步方式。

  1,static的强制同步机制

  static这个关键字相信大家都不陌生,不过它附带的同步机制估计是很多猿友都不知道的。例如下面这个简单的类。

public class Static {private static String someField1 = someMethod1();private static String someField2;static {someField2 = someMethod2();}}
View Code

  首先上面这一段代码在编译以后会变成下面这个样子,这点各位可以使用反编译工具去验证。

public class Static {private static String someField1;private static String someField2;static {someField1 = someMethod1();someField2 = someMethod2();}}
View Code

  不过在JVM真正执行这段代码的时候,其实它又变成了下面这个样子。

public class Static {private static String someField1;private static String someField2;private static volatile boolean isCinitMethodInvoked = false;static {synchronized (Static.class) {if (!isCinitMethodInvoked) {someField1 = someMethod1();someField2 = someMethod2();isCinitMethodInvoked = true;}}}}
View Code

  也就是说在实际执行一个类的静态初始化代码块时,虚拟机内部其实对其进行了同步,这就保证了无论多少个线程同时加载一个类,静态块中的代码执行且只执行一次。这点在单例模式当中得到了有效的应用,各位猿友有兴趣的可以去翻看LZ之前的单例模式博文。

  2,synchronized的同步机制

  synchronized是JVM提供的同步机制,它可以修饰方法或者代码块。此外,在修饰代码块的时候,synchronized可以指定锁定的对象,比如常用的有this,类字面常量等。在使用synchronized的时候,通常情况下,我们会针对特定的属性进行锁定,有时也会专门建立一个加锁对象。

  直接给方法加synchronized关键字,或者使用this,类字面常量作为锁的方式比较常用,也比较简单,这里就不再举例了。我们来看看对某一属性进行锁定的方式,如下。

public class Synchronized {private List<String> someFields;public void add(String someText) {//some codesynchronized (someFields) {someFields.add(someText);}//some code
    }public Object[] getSomeFields() {//some codesynchronized (someFields) {return someFields.toArray();}}}
View Code

  这种方式一般要优于使用this或者类字面常量进行锁定的方式,因为synchronized修饰的非静态成员方法默认是使用的this进行锁定,而synchronized修饰的静态成员方法默认是使用的类字面常量进行的锁定,因此如果直接在synchronized代码块中使用this或者类字面常量,可能会不经意的与synchronized方法产生互斥。通常情况下,使用属性进行加锁,能够更加有效的提高并发度,从而在保证程序正确的前提下尽可能的提高性能。

  再来看一段比较特殊的代码,如果猿友们经常看JDK源码或者一些优秀的开源框架源码的话,或许会见过这种方式。

public class Synchronized {private Object lock = new Object();private List<String> someFields1;private List<String> someFields2;public void add(String someText) {//some codesynchronized (lock) {someFields1.add(someText);someFields2.add(someText);}//some code
    }public Object[] getSomeFields() {//some codeObject[] objects1 = null;Object[] objects2 = null;synchronized (lock) {objects1 = someFields1.toArray();objects2 = someFields2.toArray();}Object[] objects = new Object[someFields1.size() + someFields2.size()];System.arraycopy(objects1, 0, objects, 0, objects1.length);System.arraycopy(objects2, 0, objects, objects1.length, objects2.length);return objects;}}
View Code

  lock是一个专门用于监控的对象,它没有任何实际意义,只是为了与synchronized配合,完成对两个属性的统一锁定。当然,一般情况下,也可以使用this代替lock,这其实没有什么死的规定,完全可以按照实际情况而定。还有一种比较不推荐的方式,就是下面这种。

public class Synchronized {private List<String> someFields1;private List<String> someFields2;public void add(String someText) {//some codesynchronized (someFields1) {synchronized (someFields2) {someFields1.add(someText);someFields2.add(someText);}}//some code
    }public Object[] getSomeFields() {//some codeObject[] objects1 = null;Object[] objects2 = null;synchronized (someFields1) {synchronized (someFields2) {objects1 = someFields1.toArray();objects2 = someFields2.toArray();}}Object[] objects = new Object[someFields1.size() + someFields2.size()];System.arraycopy(objects1, 0, objects, 0, objects1.length);System.arraycopy(objects2, 0, objects, objects1.length, objects2.length);return objects;}}
View Code

  这种加锁方式比较挑战人的细心程度,万一哪个不小心把顺序搞错了,就可能造成死锁。因此如果你非要使用这种方式,请做好被你的上司行刑的准备。

  

详解JVM外部机制——同步篇

  

  与JVM内部的同步机制对应的,就是外部的同步机制,也可以叫做编程式的同步机制。接下来,咱们就看看一些常用的外部同步方法。

  ReentrantLock(可重入的锁)

  ReentrantLock是JDK并发包中locks当中的一个类,专门用于弥补synchronized关键字的一些不足。接下来咱们就看一下synchronized关键字都有哪些不足,接着咱们再尝试使用ReentrantLock去解决这些问题。

  1)synchronized关键字同步的时候,等待的线程将无法控制,只能死等。

  解决方式:ReentrantLock可以使用tryLock(timeout, unit)方法去控制等待获得锁的时间,也可以使用无参数的tryLock方法立即返回,这就避免了死锁出现的可能性。

  2)synchronized关键字同步的时候,不保证公平性,因此会有线程插队的现象。

  解决方式:ReentrantLock可以使用构造方法ReentrantLock(fair)来强制使用公平模式,这样就可以保证线程获得锁的顺序是按照等待的顺序进行的,而synchronized进行同步的时候,是默认非公平模式的,但JVM可以很好的保证线程不被饿死。

  ReentrantLock有这样一些优点,当然也有不足的地方。最主要不足的一点,就是ReentrantLock需要开发人员手动释放锁,并且必须在finally块中释放。

  下面给出两个简单的ReentrantLock例子,请各位猿友收看。

public class Lock {private ReentrantLock nonfairLock = new ReentrantLock();private ReentrantLock fairLock = new ReentrantLock(true);private List<String> someFields;public void add(String someText) {// 等待获得锁,与synchronized类似
        nonfairLock.lock();try {someFields.add(someText);} finally {// finally中释放锁是无论如何都不能忘的
            nonfairLock.unlock();}}public void addTimeout(String someText) {// 尝试获取,如果10秒没有获取到则立即返回try {if (!fairLock.tryLock(10, TimeUnit.SECONDS)) {return;}} catch (InterruptedException e) {return;}try {someFields.add(someText);} finally {// finally中释放锁是无论如何都不能忘的
            fairLock.unlock();}}}
View Code

以上主要展示了ReentrantLock的基本用法和限时的等待,接下来咱们来看看当需要锁定多个对象的时候,ReentrantLock是如何使用的。从以下代码可以看出,用法与上面的synchronized中的方式非常相似。

package concurrent;import java.util.List;
import java.util.concurrent.locks.ReentrantLock;/*** @author zuoxiaolong**/
public class ReentrantLockTest {private ReentrantLock lock = new ReentrantLock();private List<String> someFields1;private List<String> someFields2;public void add(String someText) {//some code
        lock.lock();try {someFields1.add(someText);someFields2.add(someText);} finally {lock.unlock();}//some code
    }public Object[] getSomeFields() {//some codeObject[] objects1 = null;Object[] objects2 = null;lock.lock();try {objects1 = someFields1.toArray();objects2 = someFields2.toArray();} finally {lock.unlock();}Object[] objects = new Object[someFields1.size() + someFields2.size()];System.arraycopy(objects1, 0, objects, 0, objects1.length);System.arraycopy(objects2, 0, objects, objects1.length, objects2.length);return objects;}}
View Code

详解JVM内部机制——条件等待篇

  

  刚才已经讨论过JVM内部同步的机制,接下来咱们一起看一下JVM内部的条件等待机制。Java当中的类有一个共同的父类Object,而在Object中,有一个wait的本地方法,这是一个神奇的方法。

  它可以用来协调线程之间的协作,使用方式也比较简单,看一下下面这个例子,你就基本入门了哦。

public class ObjectWait {private volatile static boolean lock;public static void main(String[] args) throws InterruptedException {final Object object = new Object();Thread thread1 = new Thread(new Runnable() {@Overridepublic void run() {System.out.println("等待被通知!");try {synchronized (object) {while (!lock) {object.wait();}}} catch (InterruptedException e) {throw new RuntimeException(e);}System.out.println("已被通知");}});Thread thread2 = new Thread(new Runnable() {@Overridepublic void run() {System.out.println("马上开始通知!");synchronized (object) {object.notify();lock = true;}System.out.println("已通知");}});thread1.start();thread2.start();Thread.sleep(100000);}
}
View Code

  这是一个最基本的例子,我们使用一个线程在object对象上等待另外一个线程的通知,当另外一个线程通知了以后,等待的线程将会继续运行。其实初次接触这个东西,是不是感觉很有意思呢。

  wait一般情况下最常用的场景是构造一个花销非常大的对象的时候,比如JDK动态代理在生成代理类的时候就使用了这种方式。JDK6在生成一个代理类之前,会先检测一个是否正在生成中的标识,如果正在生成的话,JDK6就会在对象上等待,直到正在生成的代理类生成完毕,然后直接从缓存中获取。

  这里需要提醒大家的一点是,wait,notify和notifyAll方法在使用前,必须获取到当前对象的锁,否则会告诉你非法的监控状态异常。还有一点,则是如果有多个线程在wait等待,那么调用notify会随机通知其中一个线程,而不会按照顺序通知。换句话说,notify的通知机制是非公平的,notify并不保证先调用wait方法的线程优先被唤醒。notifyAll方法则不存在这个问题,它将通知所有处于wait等待的线程。

  

详解JVM外部机制——条件等待篇

  

  上面咱们已经看过JVM自带的条件控制机制,是使用的本地方法wait实现的。那么在JDK的类库中,也有这样的一个类Condition,来弥补wait方法本身的不足。与之前一样,说到这里,咱们就来谈谈wait到底有哪些不足。

  1)wait方法当使用带参数的方法wait(timeout)或者wait(timeout,nanos)时,无法反馈究竟是被唤醒还是到达了等待时间,大部分时候,我们会使用循环(就像上面的例子一样)来检测是否达到了条件。

  解决方式:Condition可以使用返回值标识是否达到了超时时间。

  2)由于wait,notify,notifyAll方法都需要获得当前对象的锁,因此当出现多个条件等待时,则需要依次获得多个对象的锁,这是非常恶心麻烦且繁琐的事情。

  解决方式:Condition之需要获得Lock的锁即可,一个Lock可以拥有多个条件。

  第一个问题比较好理解,只是Condition的await方法多了一个返回参数boolean去标识究竟是被唤醒还是超时。但是第二个问题比较繁琐一些,因此这里给出一个简单的示例,如下。

package concurrent;/*** @author zuoxiaolong**/
public class ObjectWait {public static void main(String[] args) throws InterruptedException {final Object object1 = new Object();final Object object2 = new Object();Thread thread1 = new Thread(new Runnable() {public void run() {try {System.out.println("等待object1被通知!");synchronized (object1) {object1.wait();}System.out.println("object1已被通知,马上开始通知object2!");synchronized (object2) {object2.notify();}System.out.println("通知object2完毕!");} catch (InterruptedException e) {throw new RuntimeException(e);}}});Thread thread2 = new Thread(new Runnable() {public void run() {try {System.out.println("马上开始通知object1!");synchronized (object1) {object1.notify();}System.out.println("通知object1完毕,等待object2被通知!");synchronized (object2) {object2.wait();}System.out.println("object2已被通知!");} catch (InterruptedException e) {throw new RuntimeException(e);}}});thread1.start();Thread.sleep(1000);thread2.start();}}
View Code

  这是一个多条件的示例。基本逻辑是,线程1先等待线程2通知,然后线程2再等待线程1通知。请记住,这是两个不同的条件。可以看到,如果使用wait的话,必须两次获得两个锁,一不小心可能还会出现死锁。接下来,咱们看看Condition实现一样的功能是怎么实现的。

package concurrent;import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;/*** @author zuoxiaolong**/
public class ConditionTest {private static ReentrantLock lock = new ReentrantLock();public static void main(String[] args) throws InterruptedException {final Condition condition1 = lock.newCondition();final Condition condition2 = lock.newCondition();Thread thread1 = new Thread(new Runnable() {public void run() {lock.lock();try {System.out.println("等待condition1被通知!");condition1.await();System.out.println("condition1已被通知,马上开始通知condition2!");condition2.signal();System.out.println("通知condition2完毕!");} catch (InterruptedException e) {throw new RuntimeException(e);} finally {lock.unlock();}}});Thread thread2 = new Thread(new Runnable() {public void run() {lock.lock();try {System.out.println("马上开始通知condition1!");condition1.signal();System.out.println("通知condition1完毕,等待condition2被通知!");condition2.await();System.out.println("condition2已被通知!");} catch (InterruptedException e) {throw new RuntimeException(e);} finally {lock.unlock();}}});thread1.start();Thread.sleep(1000);thread2.start();}}
View Code

  可以看到,我们只需要获取lock一次就可以了,在内部咱们可以使用两个或多个条件而不再需要多次获得锁。这种方式会更加直观,大大增加程序的可读性。

  

详解JVM外部机制——线程协作篇

  

  JDK当中除了以上的ReentrantLock和Condition之外,还有很多帮助猿友们协调线程的工具类。接下来咱们就一一混个脸熟。

  1,CountDownLatch

  这个类是为了帮助猿友们方便的实现一个这样的场景,就是某一个线程需要等待其它若干个线程完成某件事以后才能继续进行。比如下面的这个程序。

package concurrent;import java.util.concurrent.CountDownLatch;/*** @author zuoxiaolong**/
public class CountDownLatchTest {public static void main(String[] args) throws InterruptedException {final CountDownLatch countDownLatch = new CountDownLatch(10);for (int i = 0; i < 10; i++) {final int number = i + 1;Runnable runnable = new Runnable() {public void run() {try {Thread.sleep(1000);} catch (InterruptedException e) {}System.out.println("执行任务[" + number + "]");countDownLatch.countDown();System.out.println("完成任务[" + number + "]");}};Thread thread = new Thread(runnable);thread.start();}System.out.println("主线程开始等待...");countDownLatch.await();System.out.println("主线程执行完毕...");}}
View Code

  这个程序的主线程会等待CountDownLatch进行10次countDown方法的调用才会继续执行。我们可以从打印的结果上看出来,尽管有的时候完成任务的打印会出现在主线程执行完毕之后,但这只是因为countDown已经执行完毕,主线程的打印语句先一步执行而已。

  2,CyclicBarrier

  这个类是为了帮助猿友们方便的实现多个线程一起启动的场景,就像赛跑一样,只要大家都准备好了,那就开始一起冲。比如下面这个程序,所有的线程都准备好了,才会一起开始执行。

package concurrent;import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;/*** @author zuoxiaolong**/
public class CyclicBarrierTest {public static void main(String[] args) {final CyclicBarrier cyclicBarrier = new CyclicBarrier(10);for (int i = 0; i < 10; i++) {final int number = i + 1;Runnable runnable = new Runnable() {public void run() {try {Thread.sleep(1000);} catch (InterruptedException e) {}System.out.println("等待执行任务[" + number + "]");try {cyclicBarrier.await();} catch (InterruptedException e) {} catch (BrokenBarrierException e) {}System.out.println("开始执行任务[" + number + "]");}};Thread thread = new Thread(runnable);thread.start();}}}
View Code

  3,Semaphore

  这个类是为了帮助猿友们方便的实现控制数量的场景,可以是线程数量或者任务数量等等。来看看下面这段简单的代码。

package concurrent;import java.util.concurrent.Semaphore;
import java.util.concurrent.atomic.AtomicInteger;/*** @author zuoxiaolong**/
public class SemaphoreTest {public static void main(String[] args) throws InterruptedException {final Semaphore semaphore = new Semaphore(10);final AtomicInteger number = new AtomicInteger();for (int i = 0; i < 100; i++) {Runnable runnable = new Runnable() {public void run() {try {Thread.sleep(1000);} catch (InterruptedException e) {}try {semaphore.acquire();number.incrementAndGet();} catch (InterruptedException e) {}}};Thread thread = new Thread(runnable);thread.start();}Thread.sleep(10000);System.out.println("共" + number.get() + "个线程获得到信号");System.exit(0);}}
View Code

  从结果上可以看出,LZ设定了总数为10,却开了100个线程,但是最终只有10个线程获取到了信号量,如果这10个线程不主动调用release方法的话,那么其余90个线程将一起挂死。

  4,Exchanger

  这个类是为了帮助猿友们方便的实现两个线程交换数据的场景,使用起来非常简单,看看下面这段代码。

package concurrent;import java.util.concurrent.Exchanger;/*** @author zuoxiaolong**/
public class ExchangerTest {public static void main(String[] args) throws InterruptedException {final Exchanger<String> exchanger = new Exchanger<String>();Thread thread1 = new Thread(new Runnable() {public void run() {try {System.out.println("线程1等待接受");String content = exchanger.exchange("thread1");System.out.println("线程1收到的为:" + content);} catch (InterruptedException e) {}}});Thread thread2 = new Thread(new Runnable() {public void run() {try {System.out.println("线程2等待接受并沉睡3秒");Thread.sleep(3000);String content = exchanger.exchange("thread2");System.out.println("线程2收到的为:" + content);} catch (InterruptedException e) {}}});thread1.start();thread2.start();}}
View Code

  两个线程在只有一个线程调用exchange方法的时候调用方会被挂起,当都调用完毕时,双方会交换数据。在任何一方没调用exchange之前,线程都会处于挂起状态。

  

小结

  

  今天LZ和各位一起见识了一下Java并发编程的一些基础,掌握以上信息对于并发编程还是非常有必要的。希望各位猿友能够有所收获,咱们下次见!

转载于:https://www.cnblogs.com/zuoxiaolong/p/con2.html

相关文章:

设计模式之代理模式(Proxy Pattern)

定义&#xff1a;为其他对象提供一种代理以控制这个对象的访问&#xff0c;也叫做委托模式。 咱们比作游戏&#xff0c;通俗讲代理模式就是&#xff0c;一个主题虚基类派生出两个子类&#xff0c;一个玩家类&#xff0c;实现相关操作&#xff0c;一个是代练类&#xff0c;代替执…

[微信小程序]给data的对象的属性赋值

有问题可以扫码加我微信&#xff0c;有偿解决问题。承接小程序开发。 微信小程序开发交流qq群 173683895 、 526474645 &#xff1b; 正文&#xff1a; <view wx:for"{{leixing}}"><button class"leixing_btn {{user_infor.lx_btnitem.divingtype…

无家可归的iPhone

by Fabrice Dubois通过Fabrice Dubois 无家可归的iPhone (Homeless iPhone) So, apparently the next iPhone won’t have a physical Home button. There’s been much speculation already about what that means for the user. The bottom area of the device, for some, w…

Spring 自动化装配Bean

声明一张cd的接口&#xff1a; public interface CompactDisc {public abstract void play(); } 实现cd接口&#xff1a; Component("SgtPeppers") public class SgtPeppers implements CompactDisc {private String title "Sgt.Peppers Lonely Hearts Club Ba…

js中函数,方法,事件对比区分,什么是方法,什么是函数

微信小程序开发交流qq群 581478349 承接微信小程序开发。扫码加微信。 正文&#xff1a; 简单的理解&#xff1a;函数是运行在本地的&#xff0c;方法是公用的。 事件是开关&#xff0c;通过某某事件触发某个函数 通常命名规范 函数的命名使用小写字母和下划线&#xff…

笔记 JVM调优流程

待续 转载于:https://www.cnblogs.com/leeeee/p/7276287.html

创建新的apple id_Google是新的Apple吗?

创建新的apple idby Sumit Gupta由Sumit Gupta Google是新的Apple吗&#xff1f; (Is Google the new Apple?) 随着众多设备的推出&#xff0c;谷歌试图击败苹果。 “由Google制造”会使Google更像Apple吗&#xff1f; (With the launch of numerous devices, Google is tryi…

yeomen/bower/grunt

yeomen: npm install yo angular-in-action project npm install -g generator-angular npm install -g genrator-webapp yo angular learnangular new angular project yo webapp grunt-by-yeomen package.json npm install (执行package.json所指定的依赖包) bower: npm ins…

Window Server 2008 R2 安装 Share Point 2013

原文地址&#xff1a;http://www.cnblogs.com/jianyus/p/3631905.html转载于:https://www.cnblogs.com/gaobing/p/4191060.html

esp freertos_如何开始使用FreeRTOS和ESP8266

esp freertosby Denis Nuțiu丹尼斯努尤(Denis Nuțiu) 如何开始使用FreeRTOS和ESP8266 (How to get started with FreeRTOS and ESP8266) Recently, I purchased a NodeMCU from AliExpress for about $4. The reason I did this was to find out what all the fuss is about…

[SCOI2007]修车

题目描述 同一时刻有N位车主带着他们的爱车来到了汽车维修中心。维修中心共有M位技术人员&#xff0c;不同的技术人员对不同的车进行维修所用的时间是不同的。现在需要安排这M位技术人员所维修的车及顺序&#xff0c;使得顾客平均等待的时间最小。 说明&#xff1a;顾客的等待时…

[微信小程序]时间戳转日期

有问题可以扫码加我微信&#xff0c;有偿解决问题。承接小程序开发。 微信小程序开发交流qq群 173683895 、 526474645 &#xff1b; 正文&#xff1a; // util.js //时间戳转换成日期时间 function js_date_time(unixtime) {var dateTime new Date(parseInt(unixtime) …

React模式:集中式PropTypes

通过集中化PropType避免重复自己 (Avoid repeating yourself by centralizing PropTypes) There are three popular ways to handle types in React: PropTypes, TypeScript and Flow. This post is about PropTypes, which are currently the most popular.在React中有三种流行…

Java Class SecurityManager

# 前言 简单了解 SecurityManager。具体查阅 API。 # What 它是 Java 沙盒模型控制安全的重要一个环节。它是 Java 的一个类。下面一段话源于SecurityManager API&#xff1a; The security manager is a class that allows applications to implement a security policy. It a…

微信小程序,对象转换成数组

有问题可以扫码加我微信&#xff0c;有偿解决问题。承接小程序开发。 微信小程序开发交流qq群 173683895 、 526474645 &#xff1b; 正文&#xff1a; 对象转数组: var jiu res.data.k4.f3var nArr [];for (var i in jiu){nArr.push(jiu[i]);}console.log(jiu);consol…

sql 事务使用

BEGIN TRAN Tran_Money --开始事务DECLARE tran_error int; SET tran_error 0;BEGIN TRY UPDATE tb_Money SET MyMoney MyMoney - 30 WHERE Name 刘备;SET tran_error tran_error ERROR;--测试出错代码&#xff0c;看看刘备的钱减少&#xff0c;关羽的钱是否会增加--SE…

一群算法_树遍历解释:他们就像一群懒惰的学生,试图欺骗他们的考试

一群算法by Sachin Malhotra由Sachin Malhotra 树遍历解释&#xff1a;他们就像一群懒惰的学生&#xff0c;试图欺骗他们的考试 (Tree Traversals explained: They’re like a class of lazy students trying to cheat on their exam) Imagine that you are enrolled in a mat…

微信小程序转发 分享 打电话功能,完整代码附效果图

微信小程序开发交流qq群 581478349 承接微信小程序开发。扫码加微信。 正文&#xff1a; 按钮绑定在页面内发起转发事件onShareApp:(注意这里是button 并且给他设置了open-type"share" 属性) <button classbottom_1 bottom_1_zf open-type"share" bi…

《DSP using MATLAB》示例 Example 6.25

代码&#xff1a; % x [-0.9, 0.81]; [y, L, B] QCoeff(x, 3) % Unquantized parameters r 0.9; theta pi/3; a1 -2*r*cos(theta); a2 r*r; p1 r*exp(j*theta); p2 p1;% Quantized parameters: N 3; [ahat, L, B] QCoeff([a1, a2], 3); rhat sqrt(ahat(2)); thetah…

sqlserver查询自定义的函数

1)sp_helptext同样适应用自定义函数 2)sys.sql_modules表也可以查 查看函数的源代码: exec sp_helptext 函数名转载于:https://www.cnblogs.com/toSeeMyDream/p/4195030.html

辍学的名人_我辍学去追求成为网络开发人员和设计师的梦想

辍学的名人by Carlos Sz由Carlos Sz 我辍学去追求成为网络开发人员和设计师的梦想 (I dropped out of college to pursue my dreams of being a web developer and designer) When I was 14, I discovered HTML. Thanks to my computer science teacher at school. And from t…

Java中的static关键字的用法

1.静态方法 static&#xff1a;通常在一个类中定义一个方法为static&#xff0c;那就是说&#xff0c;无需本类的对象即可调用此方法 声明为static的方法有以下几条限制&#xff1a; &#xff08;1&#xff09;它们仅能调用其他的static方法。 &#xff08;2&#xff09;它们只…

[微信小程序]上传单张和多张图片

微信小程序开发交流qq群 173683895 承接微信小程序开发。扫码加微信。 上传单张图片并展示, <button bindtap"upimg" classjia_img >上传</button> <image src"{{tempFilePaths[0]}}"></image> data{ tempFilePaths:[]; },u…

“猪”飞起来了吗

&#xff08;让我们用代码改变世界&#xff0c;也让代码改变自己的生活&#xff09; 台风来了&#xff0c;猪都能飞起来。 这是投资者对A股近期走势最生动的形容。当成交量不断的刷新纪录&#xff0c;直冲1.2万亿天量成交时&#xff0c;股指的高度也在不断的被刷新&#xff0c;…

微信公众号开发本地环境开发_如何在5分钟内使HTTPS在本地开发环境上工作

微信公众号开发本地环境开发Almost any website you visit today is protected by HTTPS. If yours isn’t yet, it should be. Securing your server with HTTPS also means that you can’t send requests to this server from one that isn’t protected by HTTPS. This pos…

pcntl_fork 导致 MySQL server has gone away 解决方案

pcntl_fork 前连数据库&#xff0c;就会报 MySQL server has gone away 错误。原因是子进程会继承主进程的数据库连接&#xff0c;当mysql返回数据时&#xff0c;这些子进程都可以通过这个连接读到数据&#xff0c;造成数据错乱。 该操作数据库的地方还是要操作数据库&#xff…

[微信小程序]滚动选择器

微信小程序开发交流qq群 581478349 承接微信小程序开发。扫码加微信。 正文&#xff1a; <view class"section"><view class"section__title">普通选择器</view><picker bindchange"bindPickerChange" value"{{ind…

北航MOOC客户端

我们的团队作业终于完成了&#xff0c;欢迎下载使用我们的北航MOOC手机客户端软件&#xff08;Android端&#xff09;——北航学堂&#xff0c;学习北航的公开课程。 安装包下载地址&#xff1a; http://pan.baidu.com/s/1jGvH7fS 团队发布博客 http://www.cnblogs.com/sevens/…

大学毕业没有实习经历_我是如何在大学毕业后没有实习的情况下获得第一份开发人员工作的...

大学毕业没有实习经历by Tim Park蒂姆帕克(Tim Park) 我是如何在大学毕业后没有实习的情况下获得第一份开发人员工作的 (How I got my first developer job with no internships straight out of college) 5个关键要素&#xff0c;将在求职中发挥重要作用 (5 key elements tha…

java的线程中断

在java中中断线程可以使用interrupt&#xff08;&#xff09;函数。此函数虽然不能终止线程的运行&#xff0c;但是可以改变线程的状态为true 即&#xff1a;isInterrupted()的值返回为true 注意&#xff1a;当函数调用了已经被阻塞的线程后&#xff0c;被阻塞的线程将会接收到…