多线程03-线程互斥

1.错误的代码

package org.lkl.thead.sync;

public class ThreadSynchronized {

    class Outputter{
        public void output(String name){
            for(int i = 0 ;i<name.length();i++){
                System.out.print(name.charAt(i));
            }
        }
        
    }

    public static void main(String[] args) {
        new Outputter().output("zhangsan") ; //这里是错误的
    }
}
Outputter是ThreadSynchronized 类的内部类  如果在main 这个static的方法中实例化Outputter是不正确的  
原因分析:内部类实例化以后可以通过内部类去访问外部类中的属性和方法 但是要注意的是static的方法是不需要ThreadSynchronized实例化就可以直接调用的 此时如果在main方法中能实例化Outputter的话 那么在
main方法中就可以调用ThreadSynchronized类中的其他属性和方法 但此时这些属性和方法可能还没有别初始化 因此不能进行访问.


2.问题的引入

看下面的代码
package org.lkl.thead.sync;

public class ThreadSynchronized {


    public static void main(String[] args) {
        /**
         * 调用init方法 两个线程同时打印张三和李四 
         */
        new ThreadSynchronized().init() ;
    }
    
    
    
    private void init(){
        
        final Outputter out = new Outputter();
        //通过一个线程打印张三的名字
        new Thread(new Runnable() {
            @Override
            public void run() {
                while(true){
                    try {
                        Thread.currentThread().sleep(50) ;
                        out.output("zhangsan") ;
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }).start() ;
        
        //通过一个线程打印李四的名字
        new Thread(new Runnable() {
            @Override
            public void run() {
                while(true){
                    try {
                        Thread.currentThread().sleep(50) ;
                        out.output("lisi") ;
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }).start() ;
        
    }
    
    

    class Outputter{
        public void output(String name){
            for(int i = 0 ;i<name.length();i++){
                System.out.print(name.charAt(i));
            }
            System.out.println();
        }
        
    }
}

 

通过两个线程来打印张三和李四的名字  由于他们都是调用同一个Outputter对象的output方法来打印 那么就可能导致打印zhangsan或者lisi字符的时候出现偏差 例如 打印zhangsan的时候只打印了zhang 然后另外一个线程就
调用了for循环打印了lisi的信息 那么导致zhangsan的名字没有打全

3.使用synchronized关键字来解决问题

通过以上可以发现 打印name的这个操作应该是一个整体 在一个人的名字没有全部打印出来以前 是不能让其他的线程来调用for循环的方法 使用synchronized关键字给for循环这段代码加上一把锁,例如
    class Outputter{
        public void output(String name){
            synchronized(this){
                for(int i = 0 ;i<name.length();i++){
                    System.out.print(name.charAt(i));
                }
                System.out.println();
            }
        }
        
    }

 

 注意一个问题  synchronized中的this 表示的是同步这段代码的锁 那么this表示什么呢?表示的是调用这个方法的对象 即前面定义的out对象  

   synchronized中的锁可以是任意的一个对象 只要是一个对象都可以成为同步块的锁 但不是所有的对象都能锁住这个代码块 ,例如 如果把this替换成name 的话 那么就无法锁住for循环的代码 

因为两个线程中的name都是不一样的 一个是zhangsan  一个是lisi ,线程1调用for循环的时候 看的是zhangsan这把锁 但是线程2看的是lisi这把锁 很显然两把锁不一致 不能锁住for循环的代码

 

4.进一步拓展
    

    class Outputter{
        public void output(String name){
            synchronized(this){
                for(int i = 0 ;i<name.length();i++){
                    System.out.print(name.charAt(i));
                }
                System.out.println();
            }
        }
        
        
        public synchronized  void output2(String name){
                for(int i = 0 ;i<name.length();i++){
                    System.out.print(name.charAt(i));
                }
                System.out.println();
        }
    }

 

    增加一个output2方法  此时代码能锁住for循环的输出吗?  

由于我们使用的锁是this 即out对象  那么output方法和output2方法都是out中的方法  那显然是能进行代码的同步的

 

 增加一个静态的output3方法

static class Outputter{
        public void output(String name){
            synchronized(this){
                for(int i = 0 ;i<name.length();i++){
                    System.out.print(name.charAt(i));
                }
                System.out.println();
            }
        }
        
        
        public synchronized  void output2(String name){
                for(int i = 0 ;i<name.length();i++){
                    System.out.print(name.charAt(i));
                }
                System.out.println();
        }
        
        public static  synchronized  void output3(String name){
            for(int i = 0 ;i<name.length();i++){
                System.out.print(name.charAt(i));
            }
            System.out.println();
    }

 

 此时类变成了static的  那么此时能锁住代码吗?

         由于static方法是在类对象还没有产生的时候就可以调用的 那么this很显然是不能锁住代码的  需要使用一个所有方法公共的一个对象  即Outputter.class 在允许的时候会生成一个类的字节码

不管是static还是非static的方法都是在这个类中的 能达到锁住代码的效果 

         正确代码如下: 

    static class Outputter{
        public void output(String name){
            synchronized(Outputter.class){
                for(int i = 0 ;i<name.length();i++){
                    System.out.print(name.charAt(i));
                }
                System.out.println();
            }
        }
        
        
        public synchronized  void output2(String name){
                for(int i = 0 ;i<name.length();i++){
                    System.out.print(name.charAt(i));
                }
                System.out.println();
        }
        
        public static  synchronized  void output3(String name){
            for(int i = 0 ;i<name.length();i++){
                System.out.print(name.charAt(i));
            }
            System.out.println();
    }
        
    }

 

 

posted @ 2014-06-05 18:58 廖凯林 阅读(...) 评论(...) 编辑 收藏