本人所有文章,只属学习整理及个人理解!有误还望谅解并指出,谢谢!持续更新中…
线程同步
-
问题描述:
当我们使用多个线程访问同一资源的时候,且多个线程中对资源有写操作,就容易出现线程安全问题。 -
解决方案:
要解决上述多线程并发访问一个资源的安全性问题;也就是解决重复票不不存在票的问题,Java中提供了同步机制(synchronized)来解决。
为了保证每个线程都能正常执行原子操作,Java引入了线程同步机制。
实现线程同步的三种方式:
一、同步代码块。
同样是卖票案例,小二上代码:
/**
* 卖票案例 出现了线程安全问题;
* 卖出了不存在和相同的票
* 1、使用同步代码块
* 格式:
* synchronized(锁对象){
* 可能会出现线程安全问题的代码(访问了共享数据的代码)
* }
* 注意:
* 1、通过代码块中的锁对象,可以使用任意的对象
* 2、但是必须保证多个线程使用的锁对象是同一个
* 3、锁对象作用:
* 把同步代码块给锁住,只让一个线程在同步代码块中执行
*
*/
public class RunnableImpl implements Runnable {
// 定义一个多线程共享的票源
private int ticket = 100;
//创建一个锁对象,且所有线程得到的是同一个对象锁,所以只能在run方法外部去创建
Object obj = new Object();
// 设置线程任务:开始买票
@Override
public void run() {
// 设置不停的买票
while (true) {
// 同步代码块
synchronized (obj) {
// 判断票是否存在
if (ticket > 0) {
// 票存在,买票 ticket--
System.out.println(Thread.currentThread().getName()+"--->正在卖第"+ticket+"张票");
// 卖出去一张,ticket 递减
ticket--;
}
}
}
}
}
小二卖票的窗口一同给开了吧:
public class TicketTest {
public static void main(String[] args) {
// 创建 Runnable 接口的实现类对象
Runnable r1 = new RunnableImpl();
// 创建 Thread 类对象,构造方法中传递 Runnable 接口的实现对象
Thread t0 = new Thread(r1);
Thread t1 = new Thread(r1);
Thread t2 = new Thread(r1);
// 调用 start 方法 开启多线程
t0.start();
t1.start();
t2.start();
/*
Thread-0--->正在卖第100张票
Thread-0--->正在卖第99张票
Thread-2--->正在卖第98张票
Thread-2--->正在卖第97张票
Thread-2--->正在卖第96张票
Thread-2--->正在卖第95张票
Thread-2--->正在卖第94张票
Thread-1--->正在卖第93张票
Thread-1--->正在卖第92张票
Thread-1--->正在卖第91张票
Thread-1--->正在卖第90张票
Thread-1--->正在卖第89张票
Thread-1--->正在卖第88张票
Thread-1--->正在卖第87张票
Thread-1--->正在卖第86张票
Thread-1--->正在卖第85张票
Thread-1--->正在卖第84张票
Thread-1--->正在卖第83张票
Thread-1--->正在卖第82张票
Thread-1--->正在卖第81张票
Thread-1--->正在卖第80张票
Thread-1--->正在卖第79张票
Thread-1--->正在卖第78张票
Thread-1--->正在卖第77张票
Thread-1--->正在卖第76张票
Thread-1--->正在卖第75张票
Thread-1--->正在卖第74张票
Thread-1--->正在卖第73张票
Thread-1--->正在卖第72张票
Thread-1--->正在卖第71张票
Thread-1--->正在卖第70张票
Thread-1--->正在卖第69张票
Thread-1--->正在卖第68张票
Thread-1--->正在卖第67张票
Thread-1--->正在卖第66张票
Thread-2--->正在卖第65张票
Thread-2--->正在卖第64张票
Thread-2--->正在卖第63张票
Thread-2--->正在卖第62张票
Thread-2--->正在卖第61张票
Thread-2--->正在卖第60张票
Thread-2--->正在卖第59张票
Thread-2--->正在卖第58张票
Thread-2--->正在卖第57张票
Thread-2--->正在卖第56张票
Thread-2--->正在卖第55张票
Thread-2--->正在卖第54张票
Thread-2--->正在卖第53张票
Thread-2--->正在卖第52张票
Thread-2--->正在卖第51张票
Thread-2--->正在卖第50张票
Thread-2--->正在卖第49张票
Thread-2--->正在卖第48张票
Thread-2--->正在卖第47张票
Thread-2--->正在卖第46张票
Thread-2--->正在卖第45张票
Thread-2--->正在卖第44张票
Thread-2--->正在卖第43张票
Thread-2--->正在卖第42张票
Thread-2--->正在卖第41张票
Thread-2--->正在卖第40张票
Thread-2--->正在卖第39张票
Thread-2--->正在卖第38张票
Thread-2--->正在卖第37张票
Thread-2--->正在卖第36张票
Thread-2--->正在卖第35张票
Thread-2--->正在卖第34张票
Thread-2--->正在卖第33张票
Thread-2--->正在卖第32张票
Thread-2--->正在卖第31张票
Thread-2--->正在卖第30张票
Thread-2--->正在卖第29张票
Thread-2--->正在卖第28张票
Thread-2--->正在卖第27张票
Thread-2--->正在卖第26张票
Thread-2--->正在卖第25张票
Thread-2--->正在卖第24张票
Thread-2--->正在卖第23张票
Thread-2--->正在卖第22张票
Thread-2--->正在卖第21张票
Thread-2--->正在卖第20张票
Thread-2--->正在卖第19张票
Thread-2--->正在卖第18张票
Thread-2--->正在卖第17张票
Thread-2--->正在卖第16张票
Thread-2--->正在卖第15张票
Thread-2--->正在卖第14张票
Thread-2--->正在卖第13张票
Thread-2--->正在卖第12张票
Thread-2--->正在卖第11张票
Thread-2--->正在卖第10张票
Thread-2--->正在卖第9张票
Thread-2--->正在卖第8张票
Thread-2--->正在卖第7张票
Thread-2--->正在卖第6张票
Thread-2--->正在卖第5张票
Thread-2--->正在卖第4张票
Thread-2--->正在卖第3张票
Thread-2--->正在卖第2张票
Thread-2--->正在卖第1张票
*/
}
}
同步技术的原理:
- 使用了一个锁对象,这个锁对象叫同步锁,也叫对象锁,也叫对象监听器
3个(t0、t1、t2)个线程开始抢夺 cpu 的执行权,谁抢到了谁执行run方法开始卖票。
- t0 抢到了cpu的执行权,开始执行run方法,遇到 synchronized 代码块,这时 t0 会检查 synchronized 代码块是否存在锁对象,发现 【存在锁对象】 ,就会获取锁对象,进入到同步中执行;
- t1 抢到了cpu的执行,开始执行 run 方法,遇到 synchronized 代码块,这时 t1 会检查 synchronized 代码块是否存在锁对象,发现【不存在锁对象】,t1 就会进入阻塞状态,会一直等待 t0 线程归还锁对象,一直到 t0 线程执行完同步代码,会把锁对象归还给同步代码块,t1 才会获取锁对象进入到同步中执行。
总结:同步中的线程,没有执行完毕不会释放锁对象,同步外的线程没有锁对象进不去同步。