Java工程师知识树 / Java基础
共享资源与数据同步
什么是共享资源?共享资源指的是多个线程同时对同一份资源进行访问(读写操作)。
被多个线程访问的资源就称为共享资源,如何保证多个线程访问到的数据是一致的,则被称为数据同步或者资源同步。
串行化的程序,运行效率低下,不能最大化地利用CPU的计算能力;多线程以空间换时间,减少程序的响应时间,提供程序的性能。
但是多线程的引入也带来了共享资源安全的隐患。
数据不同步情况示例代码:
package com.thread.study;
public class TicketWindow implements Runnable {
public int TICKET_NUM = 10;
@Override
public void run() {
while (true) {
if (TICKET_NUM > 0) {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "卖了票号为" + TICKET_NUM-- + "的票");
} else {
return;
}
}
}
public static void main(String[] args) {
TicketWindow ticketWindow = new TicketWindow();
Thread t1 = new Thread(ticketWindow, "1号售票窗口");
Thread t2 = new Thread(ticketWindow, "2号售票窗口");
Thread t3 = new Thread(ticketWindow, "3号售票窗口");
t1.start();
t2.start();
t3.start();
}
}
//只有t1.start();执行的结果为
1号售票窗口卖了票号为10的票
1号售票窗口卖了票号为9的票
1号售票窗口卖了票号为8的票
1号售票窗口卖了票号为7的票
1号售票窗口卖了票号为6的票
1号售票窗口卖了票号为5的票
1号售票窗口卖了票号为4的票
1号售票窗口卖了票号为3的票
1号售票窗口卖了票号为2的票
1号售票窗口卖了票号为1的票
//t1,t2,t3;执行的结果为
2号售票窗口卖了票号为10的票
3号售票窗口卖了票号为9的票
1号售票窗口卖了票号为8的票
3号售票窗口卖了票号为7的票
2号售票窗口卖了票号为6的票
1号售票窗口卖了票号为5的票
3号售票窗口卖了票号为4的票 --- 号码重复
2号售票窗口卖了票号为4的票 --- 号码重复
1号售票窗口卖了票号为3的票
3号售票窗口卖了票号为2的票
2号售票窗口卖了票号为1的票
1号售票窗口卖了票号为0的票 --- 号码超过限制值
3号售票窗口卖了票号为-1的票 --- 号码超过限制值
线程安全
线程安全是多线程编程时的计算机程序代码中的一个概念。在拥有共享数据的多条线程并行执行的程序中,线程安全的代码会通过同步机制保证各个线程都可以正常且正确的执行,不会出现数据污染等意外情况。
线程安全问题大多是由全局变量及静态变量引起的,局部变量逃逸也可能导致线程安全问题。
若每个线程中对全局变量、静态变量只有读操作,而无写操作,一般来说,这个全局变量是线程安全的;若有多个线程同时执行写操作,一般都需要考虑线程同步,否则的话就可能影响线程安全。
线程同步
线程同步机制是一套用于协调线程之间的数据访问的机制,该机制可以保障线程安全.
Java平台提供的线程同步机制包括:
锁;volatile关键字;final关键字;static关键字;原子类;相关的API如Object.wait()/Object.notify(),Condition.await()/Condition.signal()等.
线程安全问题的产生前提是多个线程并发访问共享数据。
从另一方面来说Java主要是通过关键字synchronized,volatile与JUC包实现线程同步解决线程安全问题.
- synchronized
- volatile
- JUC包
同步与异步
同步(Sync)
所谓同步,就是发出一个功能调用时,在没有得到结果之前,该调用就不返回或继续执行后续操作。
根据这个定义,Java中所有方法都是同步调用,应为必须要等到结果后才会继续执行。
我们在说同步、异步的时候,一般而言是特指那些需要其他端协作或者需要一定时间完成的任务。
简单来说,同步就是必须一件一件事做,等前一件做完了才能做下一件事。
异步(Async)
异步与同步相对,当一个异步过程调用发出后,调用者在没有得到结果之前,就可以继续执行后续操作。
当这个调用完成后,一般通过状态、通知和回调来通知调用者。对于异步调用,调用的返回并不受调用者控制。
临界资源与临界区
临界资源
临界资源是一次仅允许一个进程使用的共享资源。各进程采取互斥的方式,实现共享的资源称作临界资源。
属于临界资源的硬件有,打印机,磁带机等;软件有消息队列,变量,数组,缓冲区等。
各个进程间采取互斥方式,实现对这种资源的共享。
临界区
每个进程中访问临界资源的那段代码称为临界区(criticalsection),每次只允许一个进程进入临界区,进入后,不允许其他进程进入。
不论是硬件临界资源还是软件临界资源,多个进程必须互斥的对它进行访问。
多个进程涉及到同一个临界资源的的临界区称为相关临界区。
使用临界区时,一般不允许其运行时间过长,只要运行在临界区的线程还没有离开,其他所有进入此临界区的线程都会被挂起而进入等待状态,并在一定程度上影响程序的运行性能。
阻塞和非阻塞
阻塞和非阻塞通常被用来形容多线程间的相互影响。
解释:当一个线程占用了临界区资源,那么其它需要使用这个资源的线程都必须在这个临界区上等待。等待会导致线程挂起,这样就形成了阻塞。如果占用资源的线程一直没有释放资源,那么其它的线程在这个临界区上都不能继续工作。
相反,非阻塞表明多个线程之间的执行是不会相互影响的。
阻塞调用和同步调用不同点:
对于同步调用来说,很多时候当前线程可能还是激活的,只是从逻辑上当前函数没有返回而已(正在执行中),此时,这个线程可能也会处理其他的消息。阻塞调用是指调用结果返回之前,当前线程会被挂起,一直处于等待消息通知,不能够执行其他业务 , 函数只有在得到结果之后才会返回。
扩展同步阻塞交叉关系:
- a.如果这个线程在等待当前函数返回时,仍在执行其他消息处理,那这种情况就叫做同步非阻塞;
- b.如果这个线程在等待当前函数返回时,没有执行其他消息处理,而是处于挂起等待状态,那这种情况就叫做同步阻塞;
所以同步的实现方式会有两种:同步阻塞、同步非阻塞;同理,异步也会有两种实现:异步阻塞、异步非阻塞。
实例解析:
老张爱喝茶,废话不说,煮开水。
出场人物:老张,水壶两把(普通水壶,简称水壶;会响的水壶,简称响水壶)。
1 老张把水壶放到火上,立等水开。(同步阻塞)
老张觉得自己有点傻
2 老张把水壶放到火上,去客厅看电视,时不时去厨房看看水开没有。(同步非阻塞)
老张还是觉得自己有点傻,于是变高端了,买了把会响笛的那种水壶。水开之后,能大声发出嘀~~~~的噪音。
3 老张把响水壶放到火上,立等水开。(异步阻塞)
老张觉得这样傻等意义不大
4 老张把响水壶放到火上,去客厅看电视,水壶响之前不再去看它了,响了再去拿壶。(异步非阻塞)
老张觉得自己聪明了
同步就是烧开水,通过“要自己来看开没开”这种形式通知你水开了;异步就是水开了,通过“水壶响了”这种方式通知你水开了。
阻塞就是烧开水的过程中,你一直盯着水开没开,不能干其他事情了(即你被阻塞住了);非阻塞是烧开水的过程里可以干其他事情(看电视)。
同步与异步说的是你获得水开了的方式不同。阻塞与非阻塞说的是你得到结果之前能不能干其他事情。两组概念描述的是不同的内容。