前言
可能我们从小学习的一种方法就是比较,比较不同点从而更好记住理解这个知识点,包括以前学习英语单词也是如此。但是,有时两个事物并无完全界限分明的区别,甚至无需非得用来比较,它们只是从不同角度出发而已。关于同步异步和阻塞非阻塞我个人认为它们只是从不同的角度和层次来看待程序访问调用的过程,关注点并不一样。
一 同步异步阻塞非阻塞介绍
1 同步异步
同步和异步针对应用程序,关注的是程序间的协作关系。表示的是一种协作方式,从更高的全局角度来看这种进程间的协作方式。
同步:执行一个操作之后,等待结果,然后才继续执行后续的操作;
异步:执行一个操作后,可以去执行其他的操作,然后等待通知再回来执行刚才没执行完的操作。
2 阻塞非阻塞
阻塞与非阻塞更关注的是单个进程的执行状态。过程中可能出现的状态,从该单进程的角度来看这个过程。
阻塞:进程给CPU传达一个任务之后,一直等待CPU处理完成,然后才执行后面的操作;
非阻塞:进程给CPU传达任务后,继续处理后续的操作,隔断时间再来询问之前的操作是否完成。这样的过程其实也叫轮询。
二 Demo
public class Test {
public static void main(String[] args) {
User u = new User(100);
MyThread t1 = new MyThread("线程1", u, 10);
MyThread t2 = new MyThread("线程2", u, -10);
MyThread t3 = new MyThread("线程3", u, 20);
MyThread t4 = new MyThread("线程4", u, -20);
MyThread t5 = new MyThread("线程5", u, 50);
t1.start();
t2.start();
t3.start();
t4.start();
t5.start();
}
}
class MyThread extends Thread {
private User u;
private int x = 0;
MyThread(String name, User u, int x) {
super(name);
this.u = u;
this.x = x;
}
public void run() {
u.use(x);
}
}
//用户类
class User {
private int cash;
User( int cash) {
this.cash = cash;
}
//增加x元
public synchronized void use(int x) {
try {
this.cash += x;
System.out.println(Thread.currentThread().getName() + "操作,增加了" + x + ",当前用户账户余额为:" + cash);
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
添加synchronized运行结果:
线程1操作,增加了10,当前用户账户余额为:110 线程5操作,增加了50,当前用户账户余额为:160 线程4操作,增加了-20,当前用户账户余额为:140 线程3操作,增加了20,当前用户账户余额为:160 线程2操作,增加了-10,当前用户账户余额为:150
去掉synchronized运行结果:
线程1操作,增加了10,当前用户账户余额为:100 线程4操作,增加了-20,当前用户账户余额为:100 线程2操作,增加了-10,当前用户账户余额为:100 线程3操作,增加了20,当前用户账户余额为:120 线程5操作,增加了50,当前用户账户余额为:150
分析:
上面的Demo中模拟了一个银行账户,账户里面有100元,其中user类里的use方法代表着取钱(小于0的x)和存钱(大于0的x)的操作。银行账户user对象是个竞争资源,因为有多个线程都要访问它,而多个并发操作的是方法use(x) 。
从运行结果1可以看出在use()方法加上同步,而结果2的运行结果是多个线程并发访问了竞争资源u,并对u的属性做了改动。
还有比较经典的线程同步的例子是生产者消费者模型。
三 总结&注意
(1) 同步和异步针对应用程序来,关注的是程序中间的协作关系;阻塞与非阻塞更关注的是单个进程的执行状态。
(2) 阻塞就是进程被“休息”,CPU处理其它事情去了。
(3) 同步才有阻塞非阻塞的之分,异步就是非阻塞的。