您的位置:首页 > 职场人生

黑马程序员_java多线程的一些总结(二)

2015-10-29 16:48 435 查看
------<a href="http://www.itheima.com" target="blank">Java培训、Android培训、iOS培训、.Net培训</a>、期待与您交流! -------------<a href="http://www.itheima.com" target="blank">Java培训、Android培训、iOS培训、.Net培训</a>、期待与您交流! -------一、多线程的安全问题

1、问题描述:

当多条语句在操作同一个线程共享数据时,一个线程对多条语句只执行了一部分,还没质执行完,另一个线程参与进来执行,导致共享数据的错误。代码示例:(模拟一个买票程序,两个窗口同时卖20张票)
</pre><pre name="code" class="java">class Sale implements Runnable {// 定义线程共享数据private int num = 20;@Overridepublic void run() {// 死循环,暂不处理while (true) {if (num > 0) {//这里为了看到效果,线程休眠100毫秒try{Thread.sleep(100);}catch(InterruptedException e){}//打印票的号码,模拟买票(线程的名称--票的号码)System.out.println("当前线程:" + Thread.currentThread().getName() + "--" + num--);}}}}public class Test0 {public static void main(String[] args) {System.out.println("当前线程:" + Thread.currentThread().getName());Sale sale = new Sale();Thread thread = new Thread(sale);Thread thread2 = new Thread(sale);thread.start();thread2.start();}}
执行结果:当前线程:main当前线程:Thread-0---20当前线程:Thread-1---19当前线程:Thread-0---18当前线程:Thread-1---17当前线程:Thread-0---16当前线程:Thread-1---15当前线程:Thread-0---14当前线程:Thread-1---13当前线程:Thread-0---12当前线程:Thread-1---11当前线程:Thread-0---10当前线程:Thread-1---9当前线程:Thread-0---8当前线程:Thread-1---7当前线程:Thread-0---6当前线程:Thread-1---5当前线程:Thread-0---4当前线程:Thread-1---3当前线程:Thread-0---2当前线程:Thread-1---1当前线程:Thread-0---0程序中的打印条件是票的号码大于0(Num>0),但是运行结果中我们看到线程0卖了一张票号为0的不存在的票。在这里,票的号码num所以共享数据,线程0在卖票时看到还有最后一张1号的票,但是正准备卖时,CPU执行权被线程1抢走了,线程1也看到还有最后一张票(线程0没有卖,没有更改num的值),所以线程1把票号为1的票卖了出去。当线程0再次得到CPU执行权时,会继续往下执行代码,所以继续打印Num--,(它以为Num还是1,其实在它等待CPU时线程1已经把Num改为了0)。这就是多线程可能会产生的安全问题。

2、 解决思路:

对多条操作共享数据的语句,只能让一个线程都执行完。在执行过程中欧,其他线程不可以参与执行。

3、解决方式:

java对于多线程的安全问题提供了专业的解决方式,就是同步:同步代码块、同步函数代码示例:
//方法一、同步代码块synchronized (对象) {//同步的代码} 
//方法二、同步函数public synchronized void methodA(){//同步的代码}
①、这里括号里的对象相当于一个锁,只有持有锁的线程可以在同步中执行,没有持有锁的线程即使获得了CPU的执行权,是不能进入同步代码块中执行的,只能等待,这样就解决了共享数据的安全性。
②、同步代码块中对象可以是任意非Null的对象,但是我们看到同步函数中没有指定锁,其实同步函数中的锁就是 this(也就是调用该函数的对象)③、静态函数的锁不是this,因为静态函数是没有this的( 不需要对象调用),静态函数是有类直接调用的,静态函数进入内存是没有本类对象,但是有该类的字节码文件对象(类名.class),所以静态函数的锁是该方法所在类的字节码文件对象。

4、同步的条件:

①、必须要有两个或另个以上的线程。②、必须是多个线程使用通一个锁。这里需要提的是,同步虽然解决了多线程的安全问题,但是每个线程要进入同步中执行,都必须判断锁,这样会影响程序执行的效率。

二、同步解决单例设计模式在多线程下的安全问题

1、饿汉式:饿汉式的特点是一开始就加载了,所以每次用到的时就可以马上返回就好了,可以说是用空间换时间。代码示例:
class Single{private static final Single s = new Single();private Single(){}private static Single getInstance(){return s;}}
2、懒汉式:懒汉式的特点是示例的延迟加载,在用到时才创建实力对象,节省了空间,可以说是用时间换空间。代码示例:
class Single{private static Single s = null;//初始指向null,用到时才创建实例化对象private Single(){}public static Single getInstance(){if(s=null)    //多线程时,出问题点s = new Single();return s ;}}
3、懒汉式在多线程下的问题上述代码中,有的情况下会发生安全问题。比如:在判断s是否为空时,一个线程在判断为空后,如果还未创建实例对象就失去执行权,而另一线程进入,判断s还是null,这时它可以创建一个对象。但是当前面的线程重新获得执行权,它会继续往下执行代码(也会常见一个对象),这样的话就违反了单例设计描述,该类拥有了两个实例对象。为了解决这样的问题,就可以用到同步的知识来解决,这里不管用同步函数还是同步代码块,都会比较影响,懒汉式中可以用双重判断来降低同步带来的效率的影响。具体如代码所示:示例代码:
class Single{private static Single s = null;//初始指向null,用到时才创建实例化对象private Single(){}public static Single getInstance(){//当s为空时才会创建对象 ,才会受判断锁而影响效率,s不为空,则不会受再同步影响效率if(s==null){synchronized (Single.class) {    //静态函数的锁是函数所在类的字节码文件if(s==null)        //加同步,s = new Single();}}return s ;}}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: