多线程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();
    }
        
    }

优质内容筛选与推荐>>
1、Mac中设置唯一输入法(搜狗)
2、第04组 Alpha冲刺(5/6)
3、面向对象---异常处理,约束,MD5加密,日志处理,
4、7月7日,整整一年了。
5、C# Arcengine 设置默认数据库


长按二维码向我转账

受苹果公司新规定影响,微信 iOS 版的赞赏功能被关闭,可通过二维码转账支持公众号。

    阅读
    好看
    已推荐到看一看
    你的朋友可以在“发现”-“看一看”看到你认为好看的文章。
    已取消,“好看”想法已同步删除
    已推荐到看一看 和朋友分享想法
    最多200字,当前共 发送

    已发送

    朋友将在看一看看到

    确定
    分享你的想法...
    取消

    分享想法到看一看

    确定
    最多200字,当前共

    发送中

    网络异常,请稍后重试

    微信扫一扫
    关注该公众号





    联系我们

    欢迎来到TinyMind。

    关于TinyMind的内容或商务合作、网站建议,举报不良信息等均可联系我们。

    TinyMind客服邮箱:support@tinymind.net.cn