赞
踩
目录
1个窗口卖100张票:单线程不会出现线程安全
3个窗口卖100张票:各卖各的不同票(多线程程序,没有访问共享数据,不会产生问题)
3个窗口卖100张票:可以卖相同的一张票(多线程访问了共享的数据,会产生安全问题)
3个窗口卖100张票:可以卖相同的一张票(多线程访问了共享的数据,会产生安全问题)
线程不安全的代码
- package threadSafe;
-
- /**
- * 实现卖票安全
- */
- public class MyThreadSafe implements Runnable{
- private int ticketAccount = 100;
- @Override
- public void run() {
- while(ticketAccount>0){
- //为了提高线程出现安全的问题,睡眠一下,提高概率
- try {
- Thread.sleep(200);
- } catch (Exception e) {
- e.printStackTrace();
- }
- System.out.println(Thread.currentThread().getName()+"正在卖第"+(100-ticketAccount+1)+"张票");
- ticketAccount--;
- }
-
-
- }
- }
- package threadSafe;
-
-
- public class MainMethod {
- public static void main(String[] args) {
-
- //创建1个实现类,实现共享票源
- MyThreadSafe myThreadSafe = new MyThreadSafe();
- //创建3个线程模拟3个窗口卖票
- Thread t0 = new Thread(myThreadSafe);
- Thread t1 = new Thread(myThreadSafe);
- Thread t2 = new Thread(myThreadSafe);
- t0.start();
- t1.start();
- t2.start();
- }
- }
执行结果
卖出不存在的的票
卖出相同的一张票
ticketAccount=1 剩最后一张票的时候:
t0线程抢到了CPU的执行权,进入循环体,sleep了
t2线程抢到了CPU的执行权,进入循环体,sleep了
t1线程抢到了CPU的执行权,进入循环体,sleep了
t2醒了抢到cpu的执行权,打印正在卖第100张票,执行ticketAccount--
ticketAccount=0,0不大于0,t2就停止循环了
t1醒了抢到cpu的执行权,在循环体里继续执行,此时ticketAccount=0,打印正在卖第101张票,执行ticketAccount--,
ticketAccount=-1,-1不大于0,t1就停止循环了
t0醒了抢到cpu的执行权,在循环体里继续执行,此时ticketAccount=-1,打印正在卖第102张票,执行ticketAccount--,
ticketAccount=-2,-2不大于0,t0就停止循环了
t1 t2 同时执行到了正在卖第44张票,这时候还没执行ticketAccount--
上述的线程安全问题不能使之出现,
我们可以让一个线程在访问共享数据的时候,(不管它有没有失去cpu的执行权),让其他线程只能等待,
等待当前线程执行完了,再允许其他线程继续执行.
保证始终一个线程在卖票就OK了
java引入了线程同步机制,有3种方法完成线程同步操作:
1 同步代码块
2 同步方法
3 锁机制
格式:
synchronized (锁对象){
可能会出现线程安全的代码(访问共享数据的代码)
}
- package threadSafe;
-
- /**
- * 实现卖票安全
- */
- public class MyThreadSafe implements Runnable {
- private int ticketAccount = 100;
-
- //创建一个锁对象
- Object object = new Object();
-
- @Override
- public void run() {
- //同步代码块
- synchronized (object) {
- while (ticketAccount > 0) {
- //为了提高线程出现安全的问题,睡眠一下,提高概率
- try {
- Thread.sleep(200);
- } catch (Exception e) {
- e.printStackTrace();
- }
- System.out.println(Thread.currentThread().getName() + "正在卖第" + (100 - ticketAccount + 1) + "张票");
- ticketAccount--;
-
- }
- }
-
-
- }
- }
备注:main方法的代码同之前不变
结果
同步技术的原理:
使用了一个锁对象,叫同步锁(也叫对象锁、对象监视器)
锁对象的作用:把同步代码块锁住,只让一个线程在同步代码块中执行
t0抢到了cpu的执行权,执行run()方法,遇到了同步码块,这时,t0会检查同步代码块是否有锁对象,
发现有,就会获取到锁对象,进入到同步中执行.
t1抢到了cpu的执行权,执行run()方法,遇到了同步码块,这时,t1会检查同步代码块是否有锁对象,
发现没有,t1就会进入到阻塞状态,会一直等待t0线程归还锁对象.
一直等到t0线程执行完同步中的代码,会把锁对象归还给同步代码块,t1才能获取到锁对象,进入到同步中执行.
总结:
同步中的线程,没有执行完毕,不会释放锁
同步外的线程,没有锁,进不去同步
但是会出现一个问题:程序频繁地判断锁,获取锁,释放锁,程序的效率会降低.
格式:
public synchronized 返回值 method(){
可能会产生线程安全的代码
}
- package threadSafe2;
-
- /**
- * 实现卖票安全
- */
- public class MyThreadSafe implements Runnable {
- private int ticketAccount = 50;
-
- @Override
- public void run() {
- //同步方法
- saleTicket();
-
- }
-
- //同步方法
- public synchronized void saleTicket() {
- while (ticketAccount > 0) {
- //为了提高线程出现安全的问题,睡眠一下,提高概率
- try {
- Thread.sleep(200);
- } catch (Exception e) {
- e.printStackTrace();
- }
- System.out.println(Thread.currentThread().getName() + "正在卖第" + (50 - ticketAccount + 1) + "张票");
- ticketAccount--;
-
- }
- }
-
- }
备注:main方法的代码同之前不变
结果
同步方法也会把方法内部的代码锁住,只让一个线程执行.
同步方法的锁对象是谁?是new的实现类对象:new MyThreadSafe(),就是this
静态同步方法的锁对象是谁?是本类的class文件对象 (不能是this,this是创建对象之后产生的,静态方法优先于对象)
提供了比 synchronized 更广泛的锁定操作
Lock接口中的方法:
lock()
unlock()
使用步骤:
1 在成员位置创建一个ReentrantLock对象
2 在可能会出现安全问题的代码前,调用Lock接口中的方法lock():加同步锁
3 在可能会出现安全问题的代码后,调用Lock接口中的方法unlock():释放锁
- package threadSafe3;
-
- import java.util.concurrent.locks.Lock;
- import java.util.concurrent.locks.ReentrantLock;
-
- /**
- * 实现卖票安全
- */
- public class MyThreadSafe implements Runnable {
- private int ticketAccount = 80;
-
- //1 在成员位置创建一个ReentrantLock对象
- Lock lock = new ReentrantLock();
- @Override
- public void run() {
- //2 在可能会出现安全问题的代码前,调用Lock接口中的方法lock:加同步锁
- lock.lock();
- while (ticketAccount > 0) {
- //为了提高线程出现安全的问题,睡眠一下,提高概率
- try {
- Thread.sleep(200);
-
- } catch (Exception e) {
- e.printStackTrace();
- }
- System.out.println(Thread.currentThread().getName() + "正在卖第" + (80 - ticketAccount + 1) + "张票");
- ticketAccount--;
- }
- //3 在可能会出现安全问题的代码后,调用Lock接口中的方法unlock():释放锁
- lock.unlock();
-
- }
-
-
-
- }
备注:main方法的代码同之前不变
结果
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。