线程安全的生活例子解析
电影院买票:
public class RunnableImpl implements Runnable{ //定义一个多个共享的票源 private int ticket = 100; //设置线程买票 @Override public void run() { //买票重复炒作 while (true){ //判断是否存在票 if (ticket > 0){ //飘出在,买票 try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"正在买弟"+ticket+"张票"); ticket--; } } } }
我们去买票:
public class DemoTicket { public static void main(String[] args) { RunnableImpl runn = new RunnableImpl(); Thread t0 = new Thread(runn); Thread t1 = new Thread(runn); Thread t2 = new Thread(runn); t0.start(); t1.start(); t2.start(); } }
Thread-0正在买弟5张票 Thread-1正在买弟5张票 Thread-2正在买弟3张票 Thread-0正在买弟2张票 Thread-1正在买弟2张票 Thread-2正在买弟0张票 Thread-0正在买弟-1张票
线程安全问题出现的原理:
线程同步解决线程安全问题
同步代码块
/* 格式: synchronized(锁对象){ 可能会出现线程安全问题的代码(访问共享数据的代码块) } 注意: 1通过代码中的锁对象,可以使用任意的对象 2但是必须保证多个线程的锁对象是同一个 3锁对象作用: 吧同步代码块锁住,只让一个线程再同步代码块中执行 */
锁对象可以是本类this,不需要新建
代码李诗
public class RunnableImpl implements Runnable{ private int ticket = 100; // 创建一个锁对象 Object object = new Object(); @Override public void run() { //买票重复炒作 while (true){ // 创建同步代码块 synchronized (object){ if (ticket > 0){ try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"正在买弟"+ticket+"张票"); ticket--; } } } } }
同步的原理:
同步方法解决线程安全问题
/* 使用步骤: 1把访问了共享数据的代码抽取出来,放到一个方法中 2再方法中添加一个修饰符synchroize 修饰符 格式: 修饰符 synchronize 放回值类型 方法名(参数列表){ } */
列子
public class RunnableImpl implements Runnable{ private int ticket = 100; public synchronized void payticket(){ if (ticket > 0){ try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"正在买弟"+ticket+"张票"); ticket--; } } @Override public void run() { //买票重复炒作 while (true){ payticket(); } } }
/*
同步方法也会把方法内部的代码锁住
只让一个先执行,同步方法的锁对象是谁?==就是实现类对象 new RunnableImpl()
就是this,代表本实现类,
就是谁调用方法就谁是锁对象
*/
静态同步方法
就是再同步方法里面加上static修饰符,注意,静态方法访问的属性也要是静态属性
区别:
Lock锁解决线程安全问题
/* 使用Lock锁: Lock实现提供比使用synchronized方法和语句可以获得的更广泛的锁定操作。 它们允许更灵活的结构化 java.util.concurrent.locks.ReentrantLock implements Lock接口 接口中的方法: void lock() 获得锁。 void unlock() 释放锁。 步骤: 1在成员位置创建一个Reentrantion对象 2在可能会出现安全问题的代码前调用Lock接口里面的方法lock 3在可以会出现安全问题的代码后调用Locka接口里面的方法unlock */
例子:
public class RunnableImpl implements Runnable{ private int ticket = 100; Lock l = new ReentrantLock(); @Override public void run() { //买票重复炒作 while (true){ l.lock(); if (ticket > 0){ try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"正在买弟"+ticket+"张票"); ticket--; } l.unlock(); } } }
优化例子:保证lock能关闭
while (true){ l.lock(); if (ticket > 0){ try { Thread.sleep(10); System.out.println(Thread.currentThread().getName()+"正在买弟"+ticket+"张票"); ticket--; } catch (InterruptedException e) { e.printStackTrace(); }finally {//加入finally 保证锁能够关闭 l.unlock(); }
原文:https://www.cnblogs.com/java-quan/p/13191961.html