java复习第6天---多线程----6.3--多线程同步

java复习第6天---多线程----6.3--多线程同步

目录




内容

1、线程同步

1.1、多线程安全问题的产生

  • 示例1.1-1:电影院售票问题

  现在电影院正在出售流浪地球电影票,100张。

  • 只有1个售票窗口(单线程):此时不会产生安全问题
  • 有多个售票窗口(多线程):当每个窗口售票都不一样时,比如100张票分开,每个窗口分别销售一叠票。此时,也不会出现安全问题
  • 多个售票窗口(多线程):每个售票窗口都销售者100张票,不分开。这时候,可能产生安全问题:
  • 图示1.1-1:[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-A6mdhWOB-1590502296256)(./images/sealTicket.bmp)]

1.2、多线程安全问题产生代码实现

package thread.create;

public class Ticket implements Runnable{

	private int ticket = 10;

	@Override
	public void run() {
		while(true) {
			if(this.ticket > 0) {
				try {
					Thread.sleep(100);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				System.out.println(Thread.currentThread().getName() + "正在售卖" + this.ticket + "号票");
				ticket--;
			}
		}
	}
}

package thread.create;

public class TestTicket1 {
	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, "三号窗口");

		t1.start();
		t2.start();
		t3.start();
	}

}
测试结果:
一号窗口正在售卖10号票
三号窗口正在售卖9号票
二号窗口正在售卖9号票
二号窗口正在售卖7号票
一号窗口正在售卖7号票
三号窗口正在售卖7号票
一号窗口正在售卖4号票
三号窗口正在售卖4号票
二号窗口正在售卖4号票
三号窗口正在售卖1号票
二号窗口正在售卖0号票
一号窗口正在售卖-1号票

1.3、多线程安全问题分析

  某一时刻在打印语句输出之前,3个线程ticket值可能相同,所有会打印相同的票,当某一个线程线程执行tickent = 0时,另外要给线程刚好执行tickent–那么可能打印-号票。归根结底,还是多线程共享同一资源,并且对共享资源进行操作引起的安全问题,怎么解决呢?
  为解决多线程安全问题,java引入同步机制。同步机制有三种方式:

  • 同步代码块
  • 同步方法
  • 锁机制

下面分别介绍使用方法。

1.4、同步代码

  synchronized 关键字可以作用于方法中的某一块区域,表示只对这个区域内的资源进行互斥访问。

  • 格式

      synchronized(同步锁) {
      	需要同步的代码
      }
    
  • 同步锁:对象的同步锁只是一个概念,可以想象在对象上标记了一个锁

    1. 所对象可以是任意类型
    2. 多个线程共享同一资源,需要使用相同的锁
    3. 在任何时刻,最多有一个线程获取同步锁,进入代码块执行,其他线程对象只能在外等着(BOLOCKED)
  • 同步代码块解决多线程安全问题:

      package thread.create;
    
      public class SynTicket implements Runnable{
      private int ticket = 10;
    
      	@Override
      	public void run() {
      		while(true) {
      			synchronized(this) {
      				if(this.ticket > 0) {
      					try {
      						Thread.sleep(100);
      					} catch (InterruptedException e) {
      						// TODO Auto-generated catch block
      						e.printStackTrace();
      					}
      					System.out.println(Thread.currentThread().getName() + "正在售卖" + this.ticket + "号票");
      					ticket--;
      				}
      			}
      		}
      	}
      }
      
      package thread.create;
    
      public class TestSynTicket1 {
      	public static void main(String[] args) {
      		SynTicket t = new SynTicket();
      		Thread t1 = new Thread(t, "一号窗口");
      		Thread t2 = new Thread(t, "二号窗口");
      		Thread t3 = new Thread(t, "三号窗口");
    
      		t1.start();
      		t2.start();
      		t3.start();
      	}
      }
      测试结果:
      一号窗口正在售卖10号票
      一号窗口正在售卖9号票
      一号窗口正在售卖8号票
      二号窗口正在售卖7号票
      三号窗口正在售卖6号票
      二号窗口正在售卖5号票
      一号窗口正在售卖4号票
      二号窗口正在售卖3号票
      三号窗口正在售卖2号票
      三号窗口正在售卖1号票
    

1.5、同步方法

  • 同步方法:用synchronized 修饰的方法为同步方法,能保证一个线程在访问该方法的时候,其他线程在方法外等待

  • 格式

      修饰符 synchronized 返回值类型 方法名(参数列表) {需要同步的代码块}
    
  • 所对象:

    • 非static方法,所对象为this
    • static方法,锁对象为方法所在类的字节码(.class)
  • 用同步方法解决卖票完全问题:

      package thread.create;
    
      public class SynMethodTicket implements Runnable{
      private int ticket = 10;
    
      	@Override
      	public synchronized void run() {
      		while(true) {
      				if(this.ticket > 0) {
      					try {
      						Thread.sleep(100);
      					} catch (InterruptedException e) {
      						// TODO Auto-generated catch block
      						e.printStackTrace();
      					}
      					System.out.println(Thread.currentThread().getName() + "正在售卖" + this.ticket + "号票");
      					ticket--;
      				}
      		}
      	}
    
      }
      
      package thread.create;
    
      public class TestSynMethodTicket {
      	public static void main(String[] args) {
      		SynMethodTicket t = new SynMethodTicket();
      		Thread t1 = new Thread(t, "一号窗口");
      		Thread t2 = new Thread(t, "二号窗口");
      		Thread t3 = new Thread(t, "三号窗口");
    
      		t1.start();
      		t2.start();
      		t3.start();
      	}
      }
      测试结果:
      一号窗口正在售卖10号票
      一号窗口正在售卖9号票
      一号窗口正在售卖8号票
      一号窗口正在售卖7号票
      一号窗口正在售卖6号票
      一号窗口正在售卖5号票
      一号窗口正在售卖4号票
      一号窗口正在售卖3号票
      一号窗口正在售卖2号票
      一号窗口正在售卖1号票
    
  • 总结:

    • 锁作用代码块越少,效率相对越高
    • 能用同步代码块尽量不要用同步方法

1.6、lock锁

  java.util.concurrent.locks.Lock 锁提供了比同步代码块和同步方法更广泛的锁定操作,具有同步代码块和同步方法全部功能,更加体现面向对象。
  Lock锁也称同步锁,加锁和释放锁方法化 了,如下

  • public void lock:加锁

  • public void unlock:释放锁

  • 使用步骤:

    1. 成员位置创建ReentrantLock对象
    2. 在可能出现安全问题的代码前 使用lock.lock()方法加锁
    3. 在可能出现安全问题的代码后使用lock.unlock()方法释放锁
  • lock锁解决买票安全问题:

      package thread.create;
    
      import java.util.concurrent.locks.Lock;
      import java.util.concurrent.locks.ReentrantLock;
    
      public class TicketLock implements Runnable{
      private int ticket = 10;
      Lock lock = new ReentrantLock();	
      	@Override
      	public void run() {
      		while(true) {
      				lock.lock();
      				if(this.ticket > 0) {
      					try {
      						Thread.sleep(100);
      					} catch (InterruptedException e) {
      						// TODO Auto-generated catch block
      						e.printStackTrace();
      					}
      					System.out.println(Thread.currentThread().getName() + "正在售卖" + this.ticket + "号票");
      					ticket--;
      				}
      				lock.unlock();
      		}
      	}
    
      }
      
      package thread.create;
    
      public class TestTicketLock {
      	public static void main(String[] args) {
      		TicketLock t = new TicketLock();
      		Thread t1 = new Thread(t, "一号窗口");
      		Thread t2 = new Thread(t, "二号窗口");
      		Thread t3 = new Thread(t, "三号窗口");
    
      		t1.start();
      		t2.start();
      		t3.start();
      	}
      }
      测试结果:
      一号窗口正在售卖10号票
      一号窗口正在售卖9号票
      一号窗口正在售卖8号票
      一号窗口正在售卖7号票
      一号窗口正在售卖6号票
      三号窗口正在售卖5号票
      三号窗口正在售卖4号票
      三号窗口正在售卖3号票
      二号窗口正在售卖2号票
      二号窗口正在售卖1号票
    

1.7、静态同步方法

  静态方法的锁对象是所在类的class属性,即所在类的字节码类。

后记

本项目为参考某马视频开发,相关视频及配套资料可自行度娘或者联系本人。上面为自己编写的开发文档,持续更新。欢迎交流,本人QQ:806797785

前端项目源代码地址:https://gitee.com/gaogzhen/vue-leyou
    后端JAVA源代码地址:https://gitee.com/gaogzhen/JAVA

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

gaog2zh

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值