public class TraditionalThreadSynchronized {
 
    /**
     * @param args
     */
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        new TraditionalThreadSynchronized().init();
    }
 
    private void init(){
        /**内部类不能访问局部变量,所以这里要用final*/
        final Outputer outputer = new Outputer();
        new Thread(new Runnable() {
            @Override
            public void run() {
                while(true){    
                    try {
                        Thread.sleep(5);
                    } catch (InterruptedException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                    outputer.output("GavinlinHere");
                }   
                 
            }
        }).start();
         
        new Thread(new Runnable() {
            @Override
            public void run() {
                while(true){    
                    try {
                        Thread.sleep(5);
                    } catch (InterruptedException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                    outputer.output("JiaoZhaoHere");
                }   
                 
            }
        }).start();
    }
     
    static class Outputer{
        private String name;
         
        /**
         * 可以与outputsync同步,不可与static的同步
         * @param name
         */
        public void output(String name){
            this.name = name;
            int len = this.name.length();
            synchronized(this){
                for(int i = 0;i < len; i++){
                    System.out.print(this.name.charAt(i));
                }
                System.out.println();
            }   
        }
         
        public synchronized void outputsync(String name){
            this.name = name;
            int len = this.name.length();
            for(int i = 0;i < len; i++){
                System.out.print(this.name.charAt(i));
            } 
             
            System.out.println();
        }
         
        /**
         * 若想与output同步,必须output的锁为Outputer.class
         * @param name
         */
        public static synchronized void sOutputSync(String name){
     
            int len = name.length();
            for(int i = 0;i < len; i++){
                System.out.print(name.charAt(i));
            }
            System.out.println();
        }
    }
}
output,outputsync  现在这2个方法互斥吗?如果有一个线程在执行output,另外一个线程不能执行outputsync对吧?当:当前线程执行完毕了是怎么通知另外一个线程去执行outputsync的?

解决方案 »

  1.   

    我的浅见:
    1、现在这两个方法不是互斥的,因为你output方法的前两行没有在同步块儿里。
    既然不适合互斥的,就不能保证有一个线程在执行output,另外一个线程不能执行outputsync。
    2、当前线程执行完毕了是怎么通知另外一个线程,建议楼主看一下Object的wait和notify方法。
      

  2.   

    贴下自己写的一些总结:1.若不加static,而单一使用synchronized修饰,则只是对方法进行加锁。
    可以防止多个线程对"同一个对象"的同一个方法进行同时访问。
    当运行到此类或此类中的相应synchronized方法,就会进入“同步”状态,其他人要调用此类必须等待。2.对静态方法加synchronize
    给动态方法加锁与给静态方法加锁的机制是不一样的。
    对静态方法加synchronize的效果:
    相当于对“类”进行加锁。使用静态方法时,并不需要建立实例。因此其他地方要调用同一个类中的两个不同synchronize的静态方法时,将会依次等待执行。也就是说,是在同一个类的几个静态方法中存在线程互斥作用。
      

  3.   

    肯定有的……我还在编辑ing……
      

  4.   

    1. 不是互斥的。output 方法加锁是从进入同步块开始的,因此有可能发生一种情况,output 执行了前两条语句,设置了 name;但这是outputsync 开始执行,重新设置name等,知道outputsync 执行结束。而后再执行output 的输出,输出的实际上是outputsync 所设置的内容。
    2. Google "wait notify".
      

  5.   

    动态方法与static静态方法,在程序运行时,存放的位置是不同的。
    首先看下有这么个类:public class A {
    public synchronized static void methodA() {
    System.out.println("methodA");
    }

    public synchronized static void methodB() {
    System.out.println("methodB");
    }

    public synchronized void methodC() {
    System.out.println("methodC");
    }
    public synchronized void methodD() {
    System.out.println("methodD");
    }

    public void methodE() {
    System.out.println("1");
    System.out.println("2");
    System.out.println("3");
    synchronized {
    System.out.println("methodE,synchronized程序块");
    }
    } public void methodF() {
    System.out.println("methodE");
    synchronized {
    }
    }
    }
    动态方法的synchronized,是指对同一个对象的方法进行加锁,这个锁是加在对象上的。
    比如,在另一个类class B中,A temp = new A();
    methodC和methodD都有锁,这两个方法是动态方法,存在互斥作用。例1:线程1“正在执行”temp.methodC();线程2要执行temp.methodC(),线程3要执temp.methodD(),那么线程2、线程3都需要需要等待线程1执行完temp.methodC()。线程1执行完后,线程2、线程3“竞争”执行权利(也就是要争夺“同步锁”),因为都是要运行加锁的动态方法,所以只有1个线程能运行。
    总而言之,不管线程有多少个,“同一个对象”一次只能执行一个“加了锁的动态方法”。注意这里的措辞,是“同一个对象”,“加了锁的动态方法”。例2:线程1“正在执行”temp.methodC();线程2要执行temp.methodC(),线程3要执temp.methodD();线程4要执行temp.methodE();线程5要执行temp.methodF();
    因为methodC、methodD都是加锁的动态方法,所以线程2、线程3必须等待线程1释放同步锁;
    methodE、methodF没有加锁,所以线程4、线程5能直接运行;
    所以同时是线程1、线程4、线程5在运行,线程2、线程3在等待线程1释放同步锁。
      

  6.   

    例3:线程1、线程2、线程3同时执行temp.methodE();
    methodE()没有加锁,所以三个线程能同时运行。
    但methodE()当中有同步块,假设线程2先运行到同步块,那么线程2先执行,而线程1、线程3需要等待。例4:线程1要执行temp.methodC();而线程2要执行temp.methodD()
    因为都是同一个对象“temp”,而methodC、methodD都是加了锁的动态方法,因此只有一个方法能执行。由线程1、线程2竞争同步锁,这个竞争结果是随机的,但不管如何,结果都只有其中一个能执行。这就是楼主所说的“线程互斥”情况。例5:线程1要执行temp.methodC();而线程2执行A.methodA();
    methodC是加锁的“动态”方法,而methodA是加锁的“静态”方法;动态方法与静态方法运行时在内存中是存放在两个不同的区域,因此两者并不冲突。所以线程1、线程2能同时运行。加锁动态方法与另外的加锁动态方法互斥,而加锁静态方法也只与静态方法互斥。例6:线程1执行A.methodA();线程2执行A.methodB();
    两者都是加锁的静态方法,所以只有其中之一能执行,另一个必须等待。
      

  7.   

    例7:线程1执行temp.methodC();线程2执行temp.methodF();
    methodC加了锁,但是methodF没有加锁,这两者不存在冲突,所以线程1、线程2能同时执行。例8:线程1执行A.methodA();线程2执行temp.methodF();
    methodA加了锁,但是methodF没有加锁,这两者也不存在冲突,所以线程1、线程2也能同时执行。接下来是wait()、notify()的一个参考程序。
    是两个线程轮流执行的程序。写这个程序费了我不少时间……
    public class Test {
    //int money = 0;
    //为了保证能线程同步操作money对象,此处用了一个内部类Money。
    //该内部类中有加了同步锁的plusOrMinus方法。
    Money money = new Money(0);

    MyThread1 m1;
    MyThread2 m2;

    // 构造方法一般只赋值,不调用具有效果的方法。
    public Test() {
    m1 = new MyThread1();
    m2 = new MyThread2();
    } public static void main(String[] args) {
    // 实例化一个对象
    Test w = new Test();
    // 通过实例对象,再调用具有效果的方法
    w.startProgram();
    } public void startProgram() {
    //启动线程1
    //线程1可以视为主线程。一般而言都必须有一个主线程
    new Thread(m1).start();
    //启动线程2
    new Thread(m2).start();
    } // 提供一个Thread的包装类
    // class myThread1 extends Thread
    private class MyThread1 implements Runnable {
    boolean overFlag;
    public void run() {
    // 线程尽量不要死循环
    // while(true) {
    while (!overFlag) {
    try {
    //money++;
    //注意,此处money是线程1、线程2都会操作的对象,两个线程可能同时进行操作。
    //若不同步,将可能遗漏某次冲突操作,因此对money的操作才是同步的关键。
    //这里实现同步的原理,就是使用set、get器。
    //将money作为一个内部类包装起来,然后使用一个synchronize的方法操纵money增减,这样对money的操作就是同步的了。
    money.plusOrMinus(1);

    //因受m2影响,此处打印出来的数据会有误差。
    //但是,若getMoney()加了synchronized修饰,就会没有误差。
    //注意看连着两次增加或连着两次减少的值。这里减少是一次性-2,但连着两次都只是-1。
    //实际money的数值增减是没问题的,是打印的时候getMoney方法没有同步。
    //可以将plusOrMinus中打印语句的注释去掉,对比误差数据。
    System.out.println("(存在误差)m1增加money,money值为---" + money.getMoney());

    //该线程是一直增加的,从不中断。
    //当money > 20的时候,唤醒线程2,允许减少money。
    //注意此处的this传递的对象。
    //因为此句是在内部类MyThread1范围内,因此传递的是MyThread1的实例对象。
    //如果要在内部类调用外部类的对象,需使用:Test.this,也就是在this前加外部类的名字。
    countmoney(this);
    Thread.sleep(60);
    } catch (Exception e) {
    e.printStackTrace();
    }
    }
    }
    }

    private class MyThread2 implements Runnable {
    boolean overFlag;
    public void run() {
    while (!overFlag) {
    try {
    //将自身对象传到countmoney方法,进行判断,看是否需要挂起线程m2。
    //money < 10的时候,挂起线程m2。
    //唤醒线程则应该分开,必须在另一个“一直运行”的线程中执行。
    countmoney(this);

    //判断了之后再减少money。
    //money -= 2;
    //注意,此处money是线程1、线程2都会操作的对象,两个线程可能同时进行操作。
    //若不同步,将可能遗漏某次冲突操作,因此对money的操作才是同步的关键。
    //这里实现同步的原理,就是使用set、get器。
    //将money作为一个内部类包装起来,然后使用一个synchronize的方法操纵money增减,这样对money的操作就是同步的了。
    money.plusOrMinus(-2);

    //因受m1影响,此处打印出来的数据会有误差。
    //但是,若getMoney()加了synchronized修饰,就会没有误差。
    //注意看连着两次增加或连着两次减少的值。有些减少是一次性-2,但有些连着两次都只是-1。
    //实际money的数值增减是没问题的,是打印的时候getMoney方法没有同步。
    //可以将plusOrMinus中打印语句的注释去掉,对比误差数据。
    System.out.println("(存在误差)!!m2减少money,money值为---" + money.getMoney());
    Thread.sleep(60);
    } catch (Exception e) {
    e.printStackTrace();
    }
    }
    }
    }

    public synchronized void countmoney(Object temp) {
    //判断是否为MyThread2的实例
    if (temp instanceof MyThread2) {
    //money < 10的时候,挂起线程m2。
    if (money.getMoney() < 10) {
    try {
    //必须先打印,否则wait()执行后就不会执行后面的语句了。
    System.out.println("------------------------m2挂起------------------------");
    //此处为什么wait前面不用加对象?
    //注意main方法,生成了一个实例w。
    //线程1、线程2,都是在实例w的范围内执行,因此默认直接指向实例w。
    //前面实际省略了this.
    wait();
    //下面这句与上面这句含义相同,效果一样,打印this就知道意思了。
    //this.wait();
    //System.out.println(this);
    } catch (InterruptedException e) {
    e.printStackTrace();
    }
    }
    }
    //判断是否为MyThread1的实例
    if (temp instanceof MyThread1) {
    //money>20时,唤醒m2,允许减少money。
    if (money.getMoney() > 20) {
    //同理,此处也省略了this.
    //this指向main中的实例w。
    notify();
    System.out.println("------------------------m2唤醒------------------------");
    }
    }
    }

    private class Money {
    int money;
    public Money(int x) {
    this.money = x;
    }
    //该方法没有同步,因此在打印的时候就会出现数值误差。
    //public synchronized int getMoney() {
    //之后再加同步锁,就可以发现没有误差了。
    public int getMoney() {
    return money;
    }
    //该方法加了同步,就能保证money对象能被同步操作。
    public synchronized void plusOrMinus(int x) {
    this.money +=x;
    //该数据是在同步方法内的,因此打印的是正确的。
    //System.out.println("(正确数据)money值被修改为---" + this.getMoney());
    }
    }
    }//对上面程序的一些总结:
    //1.线程尽量使用implements方法。
    //使用implements的线程,赋值、启动方式如下:
    //①线程构造语句为:
    //private class MyThread implements Runnable 
    //②常用的启动语句如下:
    //放在一个动态方法里包装起来
    //public void startProgram() {
    // new Thread(new MyThread()).start();
    // //中间new MyThread1()是一个匿名的线程对象。
    // //可以放进已经实例化了的线程对象,因此可以变化为:
    // //MyThread m1 = new MyThread();
    // //new Thread(m1).start();
    // //在下面的程序里,也是采用的这种语句方案。
    //}
    //
    //2.main方法里面的程序,一般都是生成一个实例,然后再通过实例调用一个动态方法。
    //public static void main(String[] args) {
    // // 实例化一个对象
    // Test w = new Test();
    // // 通过实例对象,再调用具有效果的方法
    // w.startProgram();
    //}
    //
    //3.wait、notify,需要通过实例才能调用。
    //注意其“对象实例”不是指线程实例,因为线程实例只是内部类(private class或者不加修饰符的class就是内部类)。
    //此处的“对象实例”是指public类的实例,比如Test w = new Test();这里Test就是一个public类。
    //此public类生成的实例,可以再创建线程。因此,一定要注意搞清楚“public类实例”与“线程实例”的关系。
    //也就是说,“public类实例”可以包含若干个线程实例,也就是多个线程。
    //下面程序中的m1、m2线程对象,都是包含在“public类实例”w对象里面的。
    //必须通过“public类实例”来调用wait、notify。这个在程序中也做了注释。
    //
    //4.在countmoney方法中,使用了instanceof方法。
    //instanceof方法是判断对象之间的“类型”是否相同。如,Object instanceof int、Object instanceof char等。
    //用法和==符相似,==符用于判断对象的“引用”是否相同。
    //此处就是为了判断传进来的对象是否是目标线程的实例。
    //
    //5.money才是真正要被同步的对象。具体做法见程序中的注释。
    //在money的plusOrMinus方法中有正确数据的打印,注意去掉注释查看打印结果,和误差数据进行比较。
    //
    //如果money是一个自定义类,比如有个专门的public类Money,那么,是否需要修改Money.java的方法,添加synchronized修饰符呢?
    //在实际开发中,这样是必须避免的。可以在自己的程序里写个内部类,然后extends原始类,然后再覆写需要添加synchronized的方法。
    //例:
    //已经存在一个Money类:
    // public class Money {
    // int money;
    // public Money(int x) {
    // this.money = x;
    // }
    // public int getMoney() {
    // return money;
    // }
    // public void plusOrMinus(int x) {
    // this.money +=x;
    // }
    // }
    //注意,plusOrMinus没加synchronized。
    //在自己程序里,就写个内部类,继承Money,然后覆写plusOrMinus方法:
    // private class MyMoney {
    // public synchronized void plusOrMinus(int x) {
    // this.money +=x;
    // }
    // }
    //这样就不用修改Money的原始类。因为Money原始类,可能在其他地方也被用到了,其他地方没必要synchronized,如果在原始类上面修改,就会影响到其他地方的运行效率。
    //
    //6.一般都必须提供一个主线程。“从不中断”的线程,即可以选为主线程。主线程一般都必须有个监听方法,用于控制其他线程。
    //该程序中,我就把线程1设置为主线程,然后countmoney作为监听方法,控制线程2的挂起/唤醒。
    //假如线程1不是“从不中断”的线程,比如增加到20就挂起,而线程2的条件也修改为减少到20就挂起,那么,有可能出现两个线程同时挂起的情况,这就会导致程序“假死”。
    //因此,一般都是再提供一个线程,使用while(1){}进行“死循环”,保持“从不中断”的状态,然后在程序中不断监听其他线程情况,并执行相应操作。
    //如,提供线程1、线程2、线程3,其中线程3使用while(1){}进行死循环监视线程1、线程2,防止出现所有线程都被挂起的情况。