黑马程序员_多线程安全问题

来源:互联网 发布:java zip压缩 编辑:程序博客网 时间:2024/05/17 06:15

------- android培训、java培训、期待与您交流! ----------

多线程安全问题

第一部分

1、多线程安全问题的描述

在卖票程序中多线程安全问题的描述:当4个线程在分别执行程序的时候,0线程进入程序进行判断tick>0;刚判断完条件,这时候0线程就卧倒下了,卧倒就是0线程具备执行的资格但是执行权被其他的线程给强走了,换句话说就是cpu切换到其他人那边去了;这是1线程就获得了执行权,1线程判断条件tick>0,是满足条件的,然后依次类推都是有可能出现这些情况,也就是说4个线程都已经卧倒了,但是这是cpu这是切换到了0线程身上了,这是0线程就直接向下执行了,这时0线程输出的票是1号票,但是这时1线程输出的是0号票,2线程输出的是-1号票,3线程输出的是-2号票,按照人们生活常理是没有0号票还负数的票的;-->这时程序就有可能会出现安全隐患;

2、用代码体现多线程安全问题

//实现Runnableclass Ticket implements Runnable{private int tick =100;//Object obj = new Object();public void run(){while(true){//synchronized(obj)//{if(tick>0){//sleep方法抛出了异常try{Thread.sleep(10);}catch (Exception e){System.out.println(e.toString());}System.out.println(Thread.currentThread().getName()+"sale:"+tick--);//}}}}}class TicketDemo2{public static void main(String[] args) {Ticket t = new Ticket();//创建线程;需要传一个Ticket对象;要指定run方法所属对象;//Thread(Runnable target) Thread t1 = new Thread(t);Thread t2 = new Thread(t);Thread t3 = new Thread(t);Thread t4 = new Thread(t);//分别要开启线程;t1.start();t2.start();t3.start();t4.start();}}

3、代码运行的结果


4、问题的描述

多线程最恐慌的就是安全问题,通过分析,发现出现了0和-1号票,因此就是多线程的运行出现了安全问题,

5、问题的原因

|--当多条语句在操作同一个线程共享数据时,一个线程对多条语句只执行了一部分,还没执行完,另一个线程参与进来执行,导致共享数据的错误。
|--如果一个线程进来把所有的语句都执行完,然后下一个线程在进来把程序执行完,那么就没有问题了。

6、解决办法

|--对多条操作共享数据的语句,只能让一个线程都执行完,在执行过程中,其他线程不可以参与执行;换句话说就是0线程在程序里面执行,1线程即使拿到了执行权也不让1线程执行;那么这样就靠谱了;
|--java对于多线程的安全问题提供了专业的解决方式;就是同步代码块;
|--如何判断哪些代码需要同步:就要看哪些代码在操作共享数据;-->tick就是共享数据;
//同步代码块synchronized(对象){需要被同步的代码}

7、代码体现

//实现Runnableclass Ticket implements Runnable{private int tick =500;//需要放一个对象;Object obj = new Object();public void run(){while(true){//同步代码块synchronized(obj){if(tick>0){//sleep方法抛出了异常try{Thread.sleep(10);}catch (Exception e){System.out.println(e.toString());}System.out.println(Thread.currentThread().getName()+"sale:"+tick--);}}}}}class TicketDemo2{public static void main(String[] args) {Ticket t = new Ticket();//创建线程;需要传一个Ticket对象;要指定run方法所属对象;//Thread(Runnable target) Thread t1 = new Thread(t);Thread t2 = new Thread(t);Thread t3 = new Thread(t);Thread t4 = new Thread(t);//分别要开启线程;t1.start();t2.start();t3.start();t4.start();/*Ticket t1 = new Ticket();Ticket t2 = new Ticket();Ticket t3 = new Ticket();Ticket t4 = new Ticket();t1.start();t2.start();t3.start();t4.start();*/}}

8、运行结果


现在就是4个线程共同操作一个共享数据,并且没有出现0和负数的情况;

9、同步原理

|--四个线程,这时有两个标志位,一个是0,一个是1,当0线程获取到cpu执行权时,判断标志位是1,进入同步代码快中,0线程进入以后,就把标志位恩恩1变成了0;把锁给关上了,然后0线程就判断if语句,满足条件,就开始读取,这时读取到了sleep语句,这是0线程就处于卧倒状态,这时1线程就进入懂啊了语句中,1线程就判断标志位是0,-->但是1线程是进不来的,这时0线程进醒了,就继续执行语句,出了同步,这是0线程就又做了一件事,就是把标志位的0置成了1;这时3线程抢到了执行权,这时3线程就把标志位改成了0;synchronized(obj)-->这个是一个锁,只有拿到了这个锁才可以进入到程序中执行,否则就一直等;
|--同步经典例子:火车上卫生间
|--对象如同锁,持有锁的线程可以在同步中执行;没有持有锁的线程即使获取cpu的执行权,也进不去,因为没有获取锁;

10、同步前提

|--必须要有两个或者两个以上的线程;
|--必须是多个线程使用同一个锁;

11、同步好处和弊端

好处:解决了多线程的安全问题;
弊端:多个线程需要判断锁,较为消耗资源;

第二部分

1、同步函数的描述

需求:银行有个金库,有两个储户分别往里面存钱,每次存100,分别存3次;
问题的描述:当储户在存钱的时候,第一个储户出了100元,但是没有立即执行输出语句,而是卧倒了,这是又来了一个储户,来存钱,存了100后,变成了200元,这时就执行了输出语句,打印了200元,而没有打印100;这就是出现了安全问题;
如何找问题:
|--明确哪些代码是多线程运行代码;
|--明确共享数据;
|--明确多线程运行代码中,哪些语句是操作的是共享数据;

2、同步函数

同步有两种表现形式:
|--同步代码:把同步放在代码中;
|--同步函数:把同步作为修饰符放在函数上;

3、同步函数的锁是this

|--如果卖票的程序在函数上使用同步的话,那么是不OK的,这样输出的是只有0线程一直在运行,把锁就一直锁起来了,其他的线程就没有运行;
|--同步函数用的是哪一个锁呢?
|--函数需要被对象调用,那么函数都有一个所属对象应用,就是this,所以同步函数使用的锁是this;

验证:

|--当同步代码块里面的锁的对象是obj的时候,同步函数里面的锁是this的时候,运行的结果是线程0和线程1交替运行,而且还出现了不同步的现象;

|--当同步代码块里面的锁的对象是this的时候,同步函数里面的锁是this的时候,运行的结果只有线程1运行;

|--由此可见,同步函数里面的锁是this锁;

4、代码体现

//卖票的程序//线程0是同步代码块,线程1是同步函数;class Ticket implements Runnable{private int tick =100;Object obj = new Object();//boolean型的标记boolean flag = true;public void run(){//是true的话if(flag){while(true){//同步代码块synchronized(this){if(tick>0){try{Thread.sleep(10);}catch (Exception e){System.out.println(e.toString());}//同步代码快System.out.println(Thread.currentThread().getName()+"--code--"+tick--);}}}}//如果是false的话elsewhile(true)show();}//同步函数public synchronized void show()//这个锁是this;{if(tick>0){try{Thread.sleep(10);}catch (Exception e){System.out.println(e.toString());}//同步函数System.out.println(Thread.currentThread().getName()+"--show--"+tick--);}}}class ThisLockDemo{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);//Thread t4 = new Thread(t);t1.start();//让主线程停10毫秒try{Thread.sleep(10);}catch (Exception e){System.out.println(e.toString());}//把flag改为false,并开启t2t.flag =false;t2.start();//t3.start();//t4.start();}}

5、运行结果


由此可见,同步函数用的是this锁;

第三部分

1、同步静态函数描述

|--通过验证法发现同步静态函数出现了不同步的现象;也就是说静态函数用的锁不是this锁;其实静态方法中是没有this的,因为静态中是没有对象的;

|--静态静内存后,内存中没有本类对象,但是一定有该类所属的字节码文件对象;

|--格式是:类名.class;该对象的类型是Class;

2、通过代码展现

class Ticket implements Runnable{private static int tick =100;Object obj = new Object();boolean flag = true;public void run(){if(flag)while(true){//该类所属的字节码文件对象;synchronized(Ticket.class){if(tick>0){try{Thread.sleep(10);}catch (Exception e){System.out.println(e.toString());}System.out.println(Thread.currentThread().getName()+"--code--"+tick--);}}}elsewhile(true)show();}public static synchronized void show(){if(tick>0){try{Thread.sleep(10);}catch (Exception e){System.out.println(e.toString());}System.out.println(Thread.currentThread().getName()+"--show--"+tick--);}}}class StaticMethodDemo{public static void main(String[] args) {Ticket t = new Ticket();Thread t1 = new Thread(t);Thread t2 = new Thread(t);t1.start();try{Thread.sleep(10);}catch (Exception e){System.out.println(e.toString());}t.flag =false;t2.start();}}

3、运行结果


当在静态同步函数,发现其的对象不是this锁了,而是该类所属的字节码文件对象,类名.class

第四部分

我的总结

|--在同步代码块里面用的锁是任意对象,
|--在同步函数里面用的锁是this;
|--在静态同步函数上用的锁是该类所属的字节码文件对象,格式:类名.class;
|--想要解决多线程的安全问题,就要先明确同步的两个前提:是不是有多个线程,是不是用的是同一把锁;
0 0
原创粉丝点击