一、线程基本概述

1.1、进程和线程

进程:一个应用程序一般都是一个进程,正在进行的程序

每一个进程最少都有一个线程,都有一个执行顺序,该顺序是一个执行路径或者一个控制单元

线程:进程中一个独立的控制单元,线程控制着进程的执行。

windows中的任务管理器,可以查看进程,linux下通过ps命令

线程是进程的最小单位

线程依赖于进程

线程随着进程的创建和创建,随着进程的结束而消亡

如迅雷:可以同时开启多个下载,就是多线程

多个程序同时执行,时CPU在很快的切换,看上去是同时执行,实际上是在CPU在切换执行。

多线程存在的意义:可以让程序中的内容同时执行。

二、继承线程的创建

2.1、继承Thread,重写run方法

package com.pb.thread.demo1;

/**
*
* @author Denny
* 继承Thread 类并重写run方法来创建线程类
*
*/
public class MyThread extends Thread { private int count; /*
* 重写run 方法
* @see java.lang.Thread#run()
*/
@Override
public void run(){
/*
* 要运行的代码块或者方法,在这里调用,或者直接写在这里
*/
while(count<10){
count++;
System.out.println("My thread run方法启动了"+count);
}
}
public static void main(String[] args) {
//声明线程类
MyThread mt=new MyThread();
//启动线程,JVM调用run方法
mt.start();
//主线程
for(int x=0;x<10;x++){
System.out.println("main thread"+x);
}
} }

多个线程都在抢占CPU的执行权,谁抢到,谁就执行

在某一时刻,只能有一个程序在运行

CPU在做着快速切换,以达到看上去是同时运行的效果。

多线程一个特性:随机性,谁抢到谁执行.

三、线程的运行和线程状态

3.1、调用start()方法

使该线程开始执行,Java虚拟机调用该线程的run()方法

为什么要重写run方法.

Thread类用于描述线程。

这个类定义一个功能,用于存储线程要运行的代码。该存储功能就是run方法.

也就是说,Thread类中的run方法是用于存储线程要运行的代码

如果直接调用run()方法,就是调用对象的普通方法一样.,仅仅是对象调用方法.线程创建了并没有运行。

只有start()才可以运行线程。

3.2、线程状态

新生:创建线程 new Thread()或者子类

运行:正在运行的线程,调用start()方法

冻结:已经创建的线程,但非运行状态的线程。sleep(long 毫秒), wait()线程等待,直到notify()唤醒才可以继续运行

临时状态阻塞:具备运行资格,但没有执行权,就是还没有被CPU切换到.

消亡:线程死亡stop(),或者线程运行完成。

四、获取线程对象

4.1、获取当前线程对象和名称

继承thread类实现线程,局部变量都有单独的一份不能共享数据

package com.pb.thread.demo1;

/**
*
* @author Denny
* 继承Thread 类并重写run方法来创建线程类
*Thread.currentThread(),获取当前线程对象
*继承thread类实现线程,局部变量都有单独的一份不能共享数据
*/
public class MyThread extends Thread { public MyThread(String name){
super(name);
}
/*
* 重写run 方法
* @see java.lang.Thread#run()
*/
@Override
public void run(){
/*
* 要运行的代码块或者方法,在这里调用,或者直接写在这里
*/
for(int x=0;x<10;x++){ System.out.println("My thread run的名字:"+Thread.currentThread().getName()+","+x);
}
}
public static void main(String[] args) {
//声明线程类
MyThread mt1=new MyThread("mt1");
// mt1.setName("张三"); 设置线程名称
MyThread mt2=new MyThread("mt2");
//启动线程,JVM调用run方法
mt1.start();
mt2.start();
//主线程
for(int x=0;x<10;x++){
System.out.println(currentThread().getName()+","+x);
}
} }

示例:

五、接口实现线程

5.1、实现Runnable接口

重写run方法()

实现接口的方式最大的好处是可以共享数据

示例:多窗口同时售票,

package com.pb.thread.demo1;

/**
*
* @author Administrator 多个窗口同时卖票
*
*/
public class Ticket implements Runnable { private int tick = 100; public void run() { while (true) {
if (tick > 0) {
System.out.println(Thread.currentThread().getName()+"卖:" + tick--);
}else{
break;
}
}
}
public static void main(String[] args) {
//声明线程类
Ticket ticket=new Ticket();
//创建线程对象,并将类做为参数
Thread t1=new Thread(ticket);
t1.setName("一号窗口,");
Thread t2=new Thread(ticket);
t2.setName("二号窗口,");
Thread t3=new Thread(ticket);
t3.setName("三号窗口,");
Thread t4=new Thread(ticket);
t4.setName("四号窗口,");
t1.start();
t2.start();
t3.start();
t4.start(); } }

结果:

 一号窗口,卖:100
一号窗口,卖:98
一号窗口,卖:97
一号窗口,卖:96
一号窗口,卖:95
一号窗口,卖:94
二号窗口,卖:99
二号窗口,卖:92
一号窗口,卖:93
一号窗口,卖:88
一号窗口,卖:87
一号窗口,卖:86
二号窗口,卖:89
二号窗口,卖:84
二号窗口,卖:83
二号窗口,卖:82
二号窗口,卖:81
二号窗口,卖:80
二号窗口,卖:79
二号窗口,卖:78
二号窗口,卖:77
二号窗口,卖:76
二号窗口,卖:75
二号窗口,卖:74
二号窗口,卖:73
二号窗口,卖:72
二号窗口,卖:71
二号窗口,卖:70
二号窗口,卖:69
二号窗口,卖:68
二号窗口,卖:67
二号窗口,卖:66
二号窗口,卖:65
二号窗口,卖:64
二号窗口,卖:63
二号窗口,卖:62
二号窗口,卖:61
二号窗口,卖:60
二号窗口,卖:59
二号窗口,卖:58
二号窗口,卖:57
二号窗口,卖:56
二号窗口,卖:55
二号窗口,卖:54
二号窗口,卖:53
二号窗口,卖:52
二号窗口,卖:51
二号窗口,卖:50
二号窗口,卖:49
二号窗口,卖:48
二号窗口,卖:47
二号窗口,卖:46
二号窗口,卖:45
二号窗口,卖:44
二号窗口,卖:43
二号窗口,卖:42
二号窗口,卖:41
二号窗口,卖:40
二号窗口,卖:39
二号窗口,卖:38
二号窗口,卖:37
二号窗口,卖:36
二号窗口,卖:35
二号窗口,卖:34
二号窗口,卖:33
二号窗口,卖:32
二号窗口,卖:31
二号窗口,卖:30
二号窗口,卖:29
二号窗口,卖:28
二号窗口,卖:27
二号窗口,卖:26
二号窗口,卖:25
二号窗口,卖:24
二号窗口,卖:23
二号窗口,卖:22
二号窗口,卖:21
三号窗口,卖:90
四号窗口,卖:91
三号窗口,卖:19
二号窗口,卖:20
一号窗口,卖:85
二号窗口,卖:16
三号窗口,卖:17
四号窗口,卖:18
三号窗口,卖:13
三号窗口,卖:11
三号窗口,卖:10
三号窗口,卖:9
三号窗口,卖:8
三号窗口,卖:7
三号窗口,卖:6
三号窗口,卖:5
三号窗口,卖:4
三号窗口,卖:3
三号窗口,卖:2
三号窗口,卖:1
二号窗口,卖:14
一号窗口,卖:15
四号窗口,卖:12

两种创建线程方式:

  优点    缺点
继承Thread类

1.编写简单

2.可以使用this关键字直接访问当前线程

无法继承其它类

无法实现数据共享

实现Runnable接口

1.可以继承其它类

2.多个线程之间可以使用同一个Runnable对象

3.可以共享数据

编程方式稍微复杂,如需访问当前线程,需要调用Thread类的currentThread()方法                                                                                                              

                                   
                                 

六、线程安全

6.1、还是上面的例子

加上sleep(1000)睡觉1秒

package com.day10.thread.demo1;

public class Ticket implements Runnable {
private int num=100; @Override
public void run() { while(true){
if(num>0){
try {
Thread.sleep(1000); //当前线程睡觉1秒,毫秒单位
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+",卖出"+num--+"号票");
}
}
}
public static void main(String[] args) {
Ticket t=new Ticket();
Thread t1=new Thread(t);
Thread t2=new Thread(t);
Thread t3=new Thread(t);
Thread t4=new Thread(t);
t1.start();
t2.start();
t3.start();
t4.start();
} }

结果:

Picked up JAVA_TOOL_OPTIONS: -javaagent:/usr/share/java/jayatanaag.jar
Thread-0,卖出100号票
Thread-2,卖出99号票
Thread-1,卖出98号票
Thread-3,卖出97号票
Thread-0,卖出96号票
Thread-2,卖出95号票
Thread-1,卖出94号票
Thread-3,卖出93号票
Thread-0,卖出92号票
Thread-2,卖出91号票
Thread-1,卖出90号票
Thread-3,卖出89号票
Thread-0,卖出88号票
Thread-2,卖出87号票
Thread-1,卖出86号票
Thread-3,卖出85号票
Thread-0,卖出84号票
Thread-2,卖出83号票
Thread-1,卖出82号票
Thread-3,卖出81号票
Thread-0,卖出80号票
Thread-2,卖出79号票
Thread-1,卖出78号票
Thread-3,卖出77号票
Thread-0,卖出76号票
Thread-2,卖出75号票
Thread-1,卖出74号票
Thread-3,卖出73号票
Thread-0,卖出72号票
Thread-2,卖出71号票
Thread-1,卖出70号票
Thread-3,卖出69号票
Thread-0,卖出68号票
Thread-2,卖出67号票
Thread-1,卖出66号票
Thread-3,卖出65号票
Thread-0,卖出64号票
Thread-2,卖出63号票
Thread-1,卖出62号票
Thread-3,卖出61号票
Thread-0,卖出60号票
Thread-2,卖出59号票
Thread-1,卖出58号票
Thread-3,卖出57号票
Thread-0,卖出56号票
Thread-2,卖出55号票
Thread-1,卖出54号票
Thread-3,卖出53号票
Thread-0,卖出52号票
Thread-2,卖出51号票
Thread-1,卖出50号票
Thread-3,卖出49号票
Thread-0,卖出48号票
Thread-2,卖出47号票
Thread-1,卖出46号票
Thread-3,卖出45号票
Thread-0,卖出44号票
Thread-2,卖出43号票
Thread-1,卖出42号票
Thread-3,卖出41号票
Thread-0,卖出40号票
Thread-2,卖出39号票
Thread-1,卖出38号票
Thread-3,卖出37号票
Thread-0,卖出36号票
Thread-2,卖出35号票
Thread-1,卖出34号票
Thread-3,卖出33号票
Thread-0,卖出32号票
Thread-2,卖出31号票
Thread-1,卖出30号票
Thread-3,卖出29号票
Thread-0,卖出28号票
Thread-2,卖出27号票
Thread-1,卖出26号票
Thread-3,卖出25号票
Thread-0,卖出24号票
Thread-2,卖出23号票
Thread-1,卖出22号票
Thread-3,卖出21号票
Thread-0,卖出20号票
Thread-2,卖出19号票
Thread-1,卖出18号票
Thread-3,卖出17号票
Thread-0,卖出16号票
Thread-2,卖出15号票
Thread-1,卖出14号票
Thread-3,卖出13号票
Thread-0,卖出12号票
Thread-2,卖出11号票
Thread-1,卖出10号票
Thread-3,卖出9号票
Thread-0,卖出8号票
Thread-2,卖出7号票
Thread-1,卖出6号票
Thread-3,卖出5号票
Thread-0,卖出4号票
Thread-2,卖出3号票
Thread-1,卖出2号票
Thread-3,卖出1号票
Thread-0,卖出0号票
Thread-2,卖出-1号票
Thread-1,卖出-2号票

发现多卖票了,

为什么?因为卖票当前线程睡眠了1秒,其它线程就可以把这张票已经卖出了,

条件检查1>0,如果4个人同时检查,条件成立,就继续卖票就出现了,负数

怎么解决?给线程加锁

线程安全问题产生的原因:

1. 多个线程在操作共享的数据。
    2. 操作共享数据的线程代码有多条。
    当一个线程在执行操作共享数据的多条代码过程中,其他线程参与了运算,就会导致线程安全问题的产生。

解决方式:就是将多条操作共享数据的线程代码封装起来,当有线程在执行这些代码的时候,其他线程不可以参与运算。必须要当前线程把这些代码都执行完毕后,其他线程才可以参与运算。

看下面的同步synchronized

七、同步代码块

7.1、synchronized

同步代码块的格式:
    synchronized(对象){
           需要被同步的代码;
    } 
    同步的好处:解决了线程的安全问题。
    同步的弊端:当线程相当多时,因为每个线程都会去判断同步上的锁,这是很耗费资源的,无形中会降低程序的运行效率。
    同步的前提:必须有多个线程并使用同一个锁。

修改上面的代码

package com.day10.thread.demo1;

public class Ticket implements Runnable {
private int num=100; @Override
public void run() { while(true){
synchronized(this){
if(num>0){
try {
Thread.sleep(1000); //当前线程睡觉1秒,毫秒单位
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+",卖出"+num--+"号票");
}else{
break;
}
}
}
}
public static void main(String[] args) {
Ticket t=new Ticket();
Thread t1=new Thread(t);
Thread t2=new Thread(t);
Thread t3=new Thread(t);
Thread t4=new Thread(t);
t1.start();
t2.start();
t3.start();
t4.start();
} }

八、同步函数 

8.1、同步方法

package com.day10.thread.demo2;

public class Account {

    //余额
private double blance=500; //取款
public void withDraw(int num){ blance=blance-num; } public double getBlance() {
return blance;
} public void setBlance(double blance) {
this.blance = blance;
} }
package com.day10.thread.demo2; public class AccountThread implements Runnable { private Account account=new Account();
@Override
public void run() {
for (int i = 0; i < 5; i++) {
if(account.getBlance()<0){
System.out.println("透支了!!");
}
makWithDraw(100); //每次取100
} } /*
* 取款的方法
*/
public void makWithDraw(int num){
//判断余额是不是大于要取款的数
if(account.getBlance()>=num){
System.out.println(Thread.currentThread().getName()+":准备取款!");
try {
Thread.sleep(1000); //等待1秒
account.withDraw(num);
System.out.println(Thread.currentThread().getName()+":取款完成!,当前余额为:"+account.getBlance());
} catch (InterruptedException e) {
e.printStackTrace();
} }else{
System.out.println(Thread.currentThread().getName()+" 余额不足以支付当前取款, 余额为: "+account.getBlance());
}
} }
package com.day10.thread.demo2; public class Demo { public static void main(String[] args) { AccountThread at=new AccountThread();
Thread t1=new Thread(at);
t1.setName("张三");
Thread t2=new Thread(at);
t2.setName("张三老婆");
t1.start();
t2.start();
} }

结果:

张三:准备取款!
张三老婆:准备取款!
张三:取款完成!,当前余额为:400.0
张三:准备取款!
张三老婆:取款完成!,当前余额为:300.0
张三老婆:准备取款!
张三:取款完成!,当前余额为:200.0
张三:准备取款!
张三老婆:取款完成!,当前余额为:100.0
张三老婆:准备取款!
张三:取款完成!,当前余额为:0.0
张三 余额不足以支付当前取款, 余额为: 0.0
张三 余额不足以支付当前取款, 余额为: 0.0
张三老婆:取款完成!,当前余额为:-100.0
透支了!!
张三老婆 余额不足以支付当前取款, 余额为: -100.0
透支了!!
张三老婆 余额不足以支付当前取款, 余额为: -100.0

加上同步关键字

/*
* 取款的方法
*/
public synchronized void makWithDraw(int num){
//判断余额是不是大于要取款的数
if(account.getBlance()>=num){
System.out.println(Thread.currentThread().getName()+":准备取款!");
try {
Thread.sleep(1000); //等待1秒
account.withDraw(num);
System.out.println(Thread.currentThread().getName()+":取款完成!,当前余额为:"+account.getBlance());
} catch (InterruptedException e) {
e.printStackTrace();
} }else{
System.out.println(Thread.currentThread().getName()+" 余额不足以支付当前取款, 余额为: "+account.getBlance());
}
}

结果:

张三:准备取款!
张三:取款完成!,当前余额为:400.0
张三老婆:准备取款!
张三老婆:取款完成!,当前余额为:300.0
张三:准备取款!
张三:取款完成!,当前余额为:200.0
张三:准备取款!
张三:取款完成!,当前余额为:100.0
张三:准备取款!
张三:取款完成!,当前余额为:0.0
张三 余额不足以支付当前取款, 余额为: 0.0
张三老婆 余额不足以支付当前取款, 余额为: 0.0
张三老婆 余额不足以支付当前取款, 余额为: 0.0
张三老婆 余额不足以支付当前取款, 余额为: 0.0
张三老婆 余额不足以支付当前取款, 余额为: 0.0

没有发生透支现象

同步代码块:

/*
* 取款的方法
*/
public void makWithDraw(int num){
synchronized(account){
//判断余额是不是大于要取款的数
if(account.getBlance()>=num){
System.out.println(Thread.currentThread().getName()+":准备取款!");
try {
Thread.sleep(1000); //等待1秒
account.withDraw(num);
System.out.println(Thread.currentThread().getName()+":取款完成!,当前余额为:"+account.getBlance());
} catch (InterruptedException e) {
e.printStackTrace();
} }else{
System.out.println(Thread.currentThread().getName()+" 余额不足以支付当前取款, 余额为: "+account.getBlance());
}
}
}

结果同上,没有发生透支现象

同步函数和同步代码块的区别:
    1. 同步函数的锁是固定的this。
    2. 同步代码块的锁是任意的对象。
    建议使用同步代码块。
    由于同步函数的锁是固定的this,同步代码块的锁是任意的对象,那么如果同步函数和同步代码块都使用this作为锁,就可以实现同步。

九、同步锁

9.1、同锁

同步方法同步对象是this,同步代码块也可以使用this来同步

十、静态同步

10.1、静态同步

不是this,因为静态方法中也不可以定义this

静态进内存,内存中没有本类对象,但是一定有该类对应的字节码文件对象

静态的同步函数使用的锁是该函数所属字节码文件对象,可以用getClass方法获取,也可以用当前类名.class表示。

synchronizid(类名.class){  或者对象.getClass

}

十一、单例模式加上同步懒汉式

11.1、懒汉式加锁

package com.day10.thread.demo2;
/**
*
* @author denny
*单例模式
*饿汉式,不存在复生成对象的问题
*懒汉工,加synchronize
*/
public class SingleDemo {
private static SingleDemo singleDemo; private SingleDemo(){ }
public static SingleDemo getNewIntance(){
if(singleDemo==null){//判断是不是空,不是空就直接返回,是空就加锁
synchronized(SingleDemo.class){ //加锁 //this.getClass() 直接写对象singleDemo.getClass()也行 if(singleDemo==null){
singleDemo=new SingleDemo();
}
}
}
return singleDemo; } }

十二、死锁

12.1、死锁常见情景之一:同步的嵌套。

 class MyLock{
static Object lockA=new Object();
static Object lockB=new Object();
} public class Demo implements Runnable { private boolean flag; public Demo(boolean flag) {
this.flag = flag;
} @Override
public void run() { if (flag) {
while (true) {
synchronized (MyLock.lockA) {
System.out.println("if.........lockA");
synchronized (MyLock.lockB) {
System.out.println("if.........lockB");
}
}
}
}
else {
while(true){
synchronized (MyLock.lockB) {
System.out.println("else.........lockB");
synchronized (MyLock.lockA) {
System.out.println("else.........lockA");
}
}
} }
} public static void main(String[] args) {
Thread t1 = new Thread(new Demo(true));
Thread t2 = new Thread(new Demo(false));
t1.start();
t2.start();
} }

建议将要锁的对象,声明为static

基础学习day11--多线程一线程的创建,运行,同步和锁的更多相关文章

  1. Java多线程之线程的创建

    好久没有更博客了,最近一直在忙工作的事情.现在终于空下来了,这2天会抓紧时间整理多线程和socket,把JavaSE结束掉. 关于多线程,首先会涉及到哪些东西呢?首先要了解线程,为什么要使用线程,线程 ...

  2. C/C++ Muti-Thread多线程编程学习(之)线程Thread | 创建、运行、结束

    文章目录 前言 线程 Thread 创建线程 CreateThread _beginthread _beginthreadex pthread_create 线程运行 结束线程 前言   多线程(Mu ...

  3. Java基础加强之多线程篇(线程创建与终止、互斥、通信、本地变量)

    线程创建与终止 线程创建 Thread类与Runnable接口的关系 public interface Runnable { public abstract void run(); } public ...

  4. Linux多线程编程——线程的创建与退出

    POSIX线程标准:该标准定义了创建和操纵线程的一整套API.在类Unix操作系统(Unix.Linux.Mac OS X等)中,都使用Pthreads作为操作系统的线程.Windows操作系统也有其 ...

  5. .net学习之多线程、线程死锁、线程通信 生产者消费者模式、委托的简单使用、GDI(图形设计接口)常用的方法

    1.多线程简单使用(1)进程是不执行代码的,执行代码的是线程,一个进程默认有一个线程(2)线程默认情况下都是前台线程,要所有的前台线程退出以后程序才会退出,进程里默认的线程我们叫做主线程或者叫做UI线 ...

  6. 黑马程序员——JAVA基础之简述多线程,两种创建多线程的方式

    ------- android培训.java培训.期待与您交流! ---------- 多线程: 进程和线程: 进程:是一个正在执行中的程序.每一个进程执行都有一个执行顺序.该顺序是一个执行路径,或者 ...

  7. Java并发学习之中的一个——线程的创建

    本文是学习网络上的文章时的总结,感谢大家无私的分享. 1.与每一个Java语言中的元素一样,线程是对象.在Java中,我们有两种方式创建线程: a.通过直接继承thread类,然后覆盖run方法. b ...

  8. java学习笔记 --- 多线程(线程安全问题——同步代码块)

    1.导致出现安全问题的原因: A:是否是多线程环境 B:是否有共享数据 C:是否有多条语句操作共享数据 2.解决线程安全问题方法: 同步代码块: synchronized(对象){ 需要同步的代码; ...

  9. (C#- 多线程) 在线程中创建object,共享问题。

    研究如下问题: 1. 在一个进程的主线程中创建一个Object,其他线程都可以访问这个Object,并操作Object的方法. - 多线程同步问题. 2. 在一个进程的多个线程里面,每个线程都创建同一 ...

  10. 0038 Java学习笔记-多线程-传统线程间通信、Condition、阻塞队列、《疯狂Java讲义 第三版》进程间通信示例代码存在的一个问题

    调用同步锁的wait().notify().notifyAll()进行线程通信 看这个经典的存取款问题,要求两个线程存款,两个线程取款,账户里有余额的时候只能取款,没余额的时候只能存款,存取款金额相同 ...

随机推荐

  1. Nopcommerce主要的技术

    Nopcommerce主要用到的技术及特点: 1.Entity Framework 2.ASP.NET mvc 3.IoC容器+依赖注入(Autofac) 4.使用EF中的EntityTypeConf ...

  2. Java 集合系列09之 Map架构

    概要 前面,我们已经系统的对List进行了学习.接下来,我们先学习Map,然后再学习Set:因为Set的实现类都是基于Map来实现的(如,HashSet是通过HashMap实现的,TreeSet是通过 ...

  3. iOS-UIButton-文字位置,字体大小,边角样式,button种类,点击事件,内容位置

    一. 设置button的文字居左,居中,居右 //设置button居左 button.contentHorizontalAlignment = UIControlContentHorizontalAl ...

  4. HTML简明教程(二)

    HTML简明教程(二) 一.HTML 图像 二.HTML 表格 三.HTML 列表 四.HTML div和 span 五.HTML 布局 六.HTML 表单和输入 七.HTML 框架 八.HTML内联 ...

  5. 学期总结ngu

    不知不觉一年就过去了,真可谓光阴似箭,日月如梭,在这一年里,我成长了许多,懂得了如何跟队友合作,提高了我的交际能力,懂得了许多课本知识,增进了我的编写能力.最重要的是学会了总结经验,这无疑是我这一年里 ...

  6. ompparticles.cpp:(.text+0x322): undefined reference to `omp_set_num_threads'

    参考 http://www.code-by.org/viewtopic.php?f=54&t=163

  7. PHP mail

    PHP 允许您从脚本直接发送电子邮件. PHP mail() 函数 PHP mail() 函数用于从脚本中发送电子邮件. 语法 mail(to,subject,message,headers,para ...

  8. 通过发布项目到IIS上,登录访问报系统找不到System.Web.Mvc

    我发布项目到IIs,通过IIS的端口来访问直接下面的错误

  9. Jquery Validation 多按钮,多表单,分组验证

    真正做到了 多按钮的验证. 在用户输入的时候就可以验证,而网上大部分多按钮验证都是必须要用户点击按钮后才可以验证. 研究了两天终于弄出来了,不知道两天是过长还是过段,现在分享给小伙伴们. 小伙伴们支持 ...

  10. .net多线程的发展

    APM和EAP是在async/await之前的两种不同的异步编程模式. APM如果不阻塞主线程,那么完成通知(回调)就会执行在另外一个线程中,从而给我们更新UI带来一定的问题. EAP的通知事件是在主 ...