定义卖票,当一个线程去买票时他会按照顺序进行,但是当两个线程去买票时,可能出现线程安全问题
定义一个卖票类:
其中让线程睡100毫秒,是为了使其出现线程问题,以为cpu的运行速度非常快,所以需要让拿到票的线程睡一下
public class ThreadSafe implements Runnable{ private int ticket=100; @Override public void run() { while(ticket<=100&&ticket>0){ try{ Thread.sleep(100); } catch (Exception e){ e.printStackTrace(); } System.out.println("线程:"+Thread.currentThread().getName()+"取得票号为-----"+ticket--); } } }
定义两个线程的测试类:
import com.one.ThreadSafe; public class ThreadSafeTest { public static void main(String[] args) { ThreadSafe threadSafe=new ThreadSafe(); Thread thread1=new Thread(threadSafe); Thread thread2=new Thread(threadSafe); thread1.start(); thread2.start(); } }
结果:
线程:Thread-0取得票号为-----99 线程:Thread-1取得票号为-----100 线程:Thread-0取得票号为-----98 线程:Thread-1取得票号为-----97 线程:Thread-0取得票号为-----96 线程:Thread-1取得票号为-----96 线程:Thread-0取得票号为-----95 线程:Thread-1取得票号为-----95 线程:Thread-1取得票号为-----94 线程:Thread-0取得票号为-----94 线程:Thread-0取得票号为-----93 线程:Thread-1取得票号为-----93 线程:Thread-0取得票号为-----91 线程:Thread-1取得票号为-----92 线程:Thread-0取得票号为-----89 线程:Thread-1取得票号为-----90 线程:Thread-0取得票号为-----88 线程:Thread-1取得票号为-----87 线程:Thread-0取得票号为-----85 线程:Thread-1取得票号为-----86 线程:Thread-0取得票号为-----83 线程:Thread-1取得票号为-----84 线程:Thread-0取得票号为-----81 线程:Thread-1取得票号为-----82 线程:Thread-1取得票号为-----80 线程:Thread-0取得票号为-----79 线程:Thread-0取得票号为-----77 线程:Thread-1取得票号为-----78 线程:Thread-0取得票号为-----76 线程:Thread-1取得票号为-----75 线程:Thread-1取得票号为-----74 线程:Thread-0取得票号为-----73 线程:Thread-0取得票号为-----72 线程:Thread-1取得票号为-----72 线程:Thread-1取得票号为-----71 线程:Thread-0取得票号为-----70 线程:Thread-1取得票号为-----69 线程:Thread-0取得票号为-----68 线程:Thread-0取得票号为-----67 线程:Thread-1取得票号为-----66 线程:Thread-1取得票号为-----65 线程:Thread-0取得票号为-----64 线程:Thread-0取得票号为-----63 线程:Thread-1取得票号为-----63 线程:Thread-1取得票号为-----62 线程:Thread-0取得票号为-----61 线程:Thread-1取得票号为-----60 线程:Thread-0取得票号为-----59 线程:Thread-1取得票号为-----57 线程:Thread-0取得票号为-----58 线程:Thread-0取得票号为-----56 线程:Thread-1取得票号为-----55 线程:Thread-1取得票号为-----54 线程:Thread-0取得票号为-----53 线程:Thread-0取得票号为-----52 线程:Thread-1取得票号为-----52 线程:Thread-0取得票号为-----50 线程:Thread-1取得票号为-----51 线程:Thread-1取得票号为-----49 线程:Thread-0取得票号为-----49 线程:Thread-0取得票号为-----48 线程:Thread-1取得票号为-----47 线程:Thread-1取得票号为-----45 线程:Thread-0取得票号为-----46 线程:Thread-0取得票号为-----44 线程:Thread-1取得票号为-----44 线程:Thread-0取得票号为-----43 线程:Thread-1取得票号为-----43 线程:Thread-0取得票号为-----42 线程:Thread-1取得票号为-----42 线程:Thread-1取得票号为-----40 线程:Thread-0取得票号为-----41 线程:Thread-1取得票号为-----39 线程:Thread-0取得票号为-----38 线程:Thread-1取得票号为-----37 线程:Thread-0取得票号为-----36 线程:Thread-1取得票号为-----35 线程:Thread-0取得票号为-----34 线程:Thread-0取得票号为-----33 线程:Thread-1取得票号为-----33 线程:Thread-1取得票号为-----31 线程:Thread-0取得票号为-----32 线程:Thread-1取得票号为-----30 线程:Thread-0取得票号为-----30 线程:Thread-0取得票号为-----28 线程:Thread-1取得票号为-----29 线程:Thread-1取得票号为-----27 线程:Thread-0取得票号为-----27 线程:Thread-0取得票号为-----26 线程:Thread-1取得票号为-----25 线程:Thread-1取得票号为-----24 线程:Thread-0取得票号为-----23 线程:Thread-0取得票号为-----22 线程:Thread-1取得票号为-----21 线程:Thread-0取得票号为-----20 线程:Thread-1取得票号为-----20 线程:Thread-0取得票号为-----19 线程:Thread-1取得票号为-----19 线程:Thread-1取得票号为-----18 线程:Thread-0取得票号为-----18 线程:Thread-0取得票号为-----17 线程:Thread-1取得票号为-----16 线程:Thread-1取得票号为-----15 线程:Thread-0取得票号为-----14 线程:Thread-1取得票号为-----13 线程:Thread-0取得票号为-----12 线程:Thread-0取得票号为-----11 线程:Thread-1取得票号为-----11 线程:Thread-1取得票号为-----10 线程:Thread-0取得票号为-----10 线程:Thread-0取得票号为-----9 线程:Thread-1取得票号为-----9 线程:Thread-0取得票号为-----7 线程:Thread-1取得票号为-----8 线程:Thread-0取得票号为-----6 线程:Thread-1取得票号为-----5 线程:Thread-1取得票号为-----4 线程:Thread-0取得票号为-----4 线程:Thread-0取得票号为-----2 线程:Thread-1取得票号为-----3 线程:Thread-1取得票号为-----1 线程:Thread-0取得票号为-----1
解决办法: synchronized:同步代码块或者同步方法可观察到票号 1 被两个线程同时获得了,产生了线程安全问题
给其业务代码上锁,当一个线程进入后则不允许另一个线程进入,要互斥访问临界资源
1、修饰代码块(同步代码块):synchronized(this) { //业务代码 }
例如:
public class ThreadSafe implements Runnable{ private int ticket=100; @Override public void run() { while(ticket>0){ synchronized(this){ try { Thread.sleep(100); } catch (Exception e) { e.printStackTrace(); } System.out.println("线程:" + Thread.currentThread().getName() + "取得票号为-----" + ticket--); } } } }
2、修饰在实例方法上(同步方法):public synchronized void run() { //业务代码 }
例如:
public class ThreadSafe implements Runnable{ private int ticket=100; @Override public synchronized void run() { while(ticket>0){ try { Thread.sleep(100); } catch (Exception e) { e.printStackTrace(); } System.out.println("线程:" + Thread.currentThread().getName() + "取得票号为-----" + ticket--); } } }
3、修饰在静态方法上(同步方法):public static synchronized void run() { //业务代码 }
例如:
public class TT { public static synchronized void show(){ } }
ReentrantLock: 同步锁####synchronized修饰代码块和静态方法时,是给当前class上锁,而synchronized修饰实例方法时,是给实例化对象上锁
较 synchronized同步代码块和同步方法的功能,它也可以实现,功能更强大
常需要用到的两种方法:
lock()获得锁
unlock()尝试释放此锁
这两个方法要同时存在
可以借助try{} finally{}
例如:
import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; public class ThreadSafe implements Runnable{ private static int ticket=100; private Lock lock=new ReentrantLock(); public void run() { lock.lock(); try{ while(ticket>0){ try { Thread.sleep(100); } catch (Exception e) { e.printStackTrace(); } System.out.println("线程:" + Thread.currentThread().getName() + "取得票号为-----" + ticket--); } } //无论上面的代码执不执行,释放锁都要执行 finally { lock.unlock(); } } }
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)