首页 > 代码库 > 多线程编程[二]

多线程编程[二]

继 多线程编程[一]:http://www.cnblogs.com/wangfajun/p/6547648.html,我们开始第二篇文章啦。。。

上一篇中结尾,我们了解到,同步函数用的锁是 this ,那么我们接下来,在同步函数上加下个静态标示符static试试:

public class Test {
    public static void main(String[] args) {
        try {
            Ticket one = new Ticket();
            new Thread(one).start();
            Thread.sleep(10);
            one.flag = false;
            new Thread(one).start();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
class Ticket implements Runnable{
    private static int ticket = 1000;
    boolean flag = true;
    @Override
    public void run() {
        if(flag){
            synchronized(this){
                while(true){
                    if(ticket>0){
                        System.out.println(Thread.currentThread().getName()+"同步代码块..."+ ticket--);
                    }
                }
            }
        }else{
            while(true)
                sale();
        }
    }
    public static synchronized void sale(){
        if(ticket>0){
            System.out.println(Thread.currentThread().getName()+"同步方法..."+ ticket--);
        }
    }
}
执行结果:
  ....   
  二号窗口卖票...2
  二号窗口卖票...1
  二号窗口卖票...0

 好吧,又出现了0号票。线程又不安全了。思考线程安全的连个前提:

  1.必须要有两个或以上的线程

  2.必须是多个线程使用同一个锁

肯定是2没满足,那么,静态同步函数的锁对象不是this,是什么呢?

我们知道静态资源的特点:进内存的时候,内存中没有本类的对象,那么有谁?静态方法是不是由类调用的 ?类在进内存的时候,有对象吗? 有,就是那份字节码文件对象(Ticket.class),Ticket进内存,紧跟着,静态资源进内存,OK,我们来试试。。

将上面同步代码块中的this锁换成如下:

synchronized(Ticket.class){
     while(true){
          if(ticket>0){
                System.out.println(Thread.currentThread().getName()+"同步代码块..."+ ticket--);
          }
     }
}
执行结果:
Thread-0同步代码块...5 Thread-0同步代码块...4 Thread-0同步代码块...3 Thread-0同步代码块...2 Thread-0同步代码块...1

最后一张为1号票,线程安全。

结论:静态同步函数使用的锁是该方法所在类的字节码文件对象,也就是 类名.class。

 

看完[一]再到这里,我们穿插一个小的知识点。单例模式,因为这个模式要学会了线程同步,才比较好讲这个知识点。

(好像刚出来的那会,面试中基本会问这个问题,或者笔试中让你写一个单利模式,有记忆吧。哈哈。。。青春一去不复返啊。。)

1饱汉模式:

class Single{
    public static final Single single = new Single();
    public static Single getInstance(){
        return single;
    }
}

初始化时,就给你创建了一个实例化对象,不会出现线程安全问题,再看看懒汉模式:

2..懒汉模式(延迟加载):

class Single{
    public static Single single = null;
    public static Single getInstance(){
        if(single == null){
            single = new Single();
        }
        return single;
    }
}

假如现在有A、B两个线程,同时访问getInstance方法时,分析下执行过程:

1.A线程进入if条件判断,发现single为null,然后线程A挂在这了,

2.B线程进来了,然后也挂这了,

3.A线程活了,继续往下执行,new了一个Single对象出来了

4.B线程也活了,往下执行又创建了一个Single对象

这样是不是线程又不安全了?没错。。。。听明白了否?

如何解决?修改代码如下:

class Single{
    public static Single single = null;
    public static synchronized Single getInstance(){
        if(single == null){
            single = new Single();
        }
        return single;
    }
}

加了一个 synchronized 来修饰,每个线程想要获得这个Single实例的时候,都要判断锁,我们发现,线程安全了。。。

不过虽然这样解决了线程安全问题,但是getInstance()方法效率比较低,如何提高效率?接着修改代码:

class Single{
    public static Single single = null;
    public static Single getInstance(){
        if(single==null){
            synchronized(Single.class){
                if(single == null){
                    single = new Single();
                }
            }
        }
        return single;
    }
}

将同步函数变成同步代码块,然后再在同步代码块外包了一层if条件判断,为什么这么做就提升了效率?假设现在有A、B.等等多个线程,再来分析下执行过程:

1.A线程进入synchronized方法块中,获得了锁,挂这了

2.B线程进入最外层if条件判断,single为null,满足,继续执行,发现锁被A线程拿着了,进不去,B线程挂这了

3.A线程活了,进入内层if条件判断,single为null,满足,new了一个Single对象出来,释放了锁,此时single已经被实例化了

4.B线程活了,进入了synchronized方法块种,内层if条件判断,不满足,执行return single.

后续的其他线程进来,判断最外层if条件判断,single都不为空了,直接返回,这样效率是不是提升了?

 

...回家吃饭了。。。明天继续。。

 

多线程编程[二]