你用或者不用:线程中synchronized关键字使用总结

时间:2021-11-03 18:30:29


感谢张龙老师!

Synchronized关键字,同步的, synchronized关键字可以修饰方法,还有静态代码块(static block)

Java中的每一个对象都有一把锁(lock),只是平时状态下,锁是开着的,当被synchronized关键字所修饰时,就将这把锁激活,当一个对象被多个线程所调用时,由于线程的行进度的不确定性,可能会引发方法内很多问题,而synchronized关键字就是只允许方法每一次只能允许一个线程访问,只有当线程访问结束,或者抛出异常时,才允许下一个线程的介入:

(张龙老师的文档说的很清楚了,这点也是非常重要的)

模拟银行取钱系统:

/*

 * 在这个程序中我遇到了一个问题

 * 就是为什么要在类中定义一个其他类的成员变量

 * 我一开始是new出来的,发现这样做很不应该

 * 特别是在线程中,这样做是一点都体现不了线程的作用;

 * 这样做的话,每一次调用启用类都会生成调用类的对象;相当于是线程之间不会影响;

 * 而定义成员类,不管生成多少个启用类,都可以只传入一个调用类;

 */

 

package thread;

 

publicclass FetchMoneyTest

{

   publicstaticvoid main(String[] args)

   {

      Bank bank = new Bank();

      ThreadDemo test1 = new ThreadDemo(bank);

      ThreadDemo test2 = new ThreadDemo(bank);

      test1.start();

      test2.start();

   }

}

 

class Bank

{

   publicintmoney = 1000;

 

   public int getMoney(int number)throwsInterruptedException

   {

      if (number < 0)

      {

        return -1;

 

      }

      elseif (number > 1000)

      {

        return -2;

      }

      else

      {

        Thread.sleep(1200);

        money -= number;

        System.out.println(money);

       

        return number;

      }

 

   }

 

}

 

class ThreadDemoextends Thread

{

   private Bankbank;

   public ThreadDemo(Bank bank)

   {

      this.bank = bank;

   }

   @Override

   publicvoid run()

   {

     

      try

      {

        System.out.println(bank.getMoney(800));

      }

      catch (InterruptedException e)

      {

        // TODO Auto-generatedcatch block

        e.printStackTrace();

      }

   }

}

 

下面的程序只改变一点,就是只加上synchronized关键字:

/*

 * 在这个程序中我遇到了一个问题

 * 就是为什么要在类中定义一个其他类的成员变量

 * 我一开始是new出来的,发现这样做很不应该

 * 特别是在线程中,这样做是一点都体现不了线程的作用;

 * 这样做的话,每一次调用启用类都会生成调用类的对象;相当于是线程之间不会影响;

 * 而定义成员类,不管生成多少个启用类,都可以只传入一个调用类;

 */

 

package thread;

 

publicclass FetchMoneyTest

{

   publicstaticvoid main(String[] args)

   {

      Bank bank = new Bank();

      ThreadDemo test1 = new ThreadDemo(bank);

      ThreadDemo test2 = new ThreadDemo(bank);

      test1.start();

      test2.start();

   }

}

 

class Bank

{

   publicintmoney = 1000;

 

   publicsynchronizedint getMoney(int number)throws InterruptedException

   //加上了synchronized关键字后,线程将会卓一的通过这个方法,第二次的时候显然不符合方法的第三个约定,返回-3

   {

      if (number < 0)

      {

        return -1;

 

      }

      elseif (number > 1000)

      {

        return -2;

      }

      elseif(money < number)

      {

        return -3;

      }

      else

      {

        Thread.sleep(1200);

        money -= number;

        System.out.println(money);

       

        return number;

      }

 

   }

 

}

 

class ThreadDemoextends Thread

{

   private Bankbank;

   public ThreadDemo(Bank bank)

   {

      this.bank = bank;

   }

   @Override

   publicvoid run()

   {

     

      try

      {

        System.out.println(bank.getMoney(800));

      }

      catch (InterruptedException e)

      {

        // TODO Auto-generatedcatch block

        e.printStackTrace();

      }

   }

}

 

  

25.

人是不能够懒得,我应该有自己的一套,下面来深度的理解张龙老师说的一句话,被synchronized修饰方法的对象会上锁,如果一个类的多个方法都被synchronized修饰时,当一个线程未访问完毕其中的一个synchronized方法时,其他的线程是不能访问任何该对象的任何一个synchronized方法的,除非你生成了两个对象!

下面我们来一步步的分析:

首先 当一个类中的两个方法都没有被synchronized修饰时,这两个方法的打印将是随机进行的:

package thread;

 

publicclass ThreadTest10

{

   publicstaticvoid main(String[] args)

   {

      Demo demo1 = new Demo();

      ThreadTest11 test1 = new ThreadTest11(demo1);

      ThreadTest12 test2 = new ThreadTest12(demo1);

      test1.start();

      test2.start();

   }

}

class Demo

{

   publicvoid method1()throws InterruptedException

   {

      for(int i = 0 ; i < 15 ; i ++)

      {

        Thread.sleep((long)(Math.random()*1000));

        System.out.println("hello:"+ i );

      }

   }

   publicvoid method2()throws InterruptedException

   {

     

      for(int i = 0 ; i < 15; i ++)

      {

        Thread.sleep((long)(Math.random()*1000));

        System.out.println("world:"+i);

       

      }

   }

}

class ThreadTest11extends Thread

{

   private Demodemo;

   public ThreadTest11 (Demo demo)

   {

      this.demo = demo;

   }

   @Override

   publicvoid run()

   {

      try

      {

        demo.method1();

      }

      catch (InterruptedException e)

      {

        // TODO Auto-generatedcatch block

        e.printStackTrace();

      }

   }

  

  

}

class ThreadTest12extends Thread

{

   private Demodemo;

   public ThreadTest12 (Demo demo)

   {

      this.demo = demo;

   }

   @Override

   publicvoid run()

   {

      try

      {

        demo.method2();

      }

      catch (InterruptedException e)

      {

        // TODO Auto-generatedcatch block

         e.printStackTrace();

      }

   }

  

  

}

 

 

 

 

现在我要为上面的两个方法加上synchronized关键字,使之变得有序,如果只加上一个的话,是没有意义的,还是乱序,因为同步方法,可以和非同步方法同时运行;

加上后将会输出有序;先输出hello 或者 world;

 

 

下面

说过 synchronized只修饰一个对象,如果我传入的是两个对象,即使加上了synchronized关键字,它们也不会互相影响;

更改如下:

publicstaticvoid main(String[] args)

   {

      Demodemo1 = new Demo();

      ThreadTest11 test1 = new ThreadTest11(demo1);

      demo1 = newDemo();

      ThreadTest12 test2 = new ThreadTest12(demo1);

      test1.start();

      test2.start();

   }

 

 

下面将是讲述的重点,就是关键字synchronized 与static合用的效果,我们知道,不管一个类生成多少个对象,这些对象实例都只有一个共同的类,synchronized只能锁住一个类的对象,而被 synchronizedstatic修饰的方法,将会锁住对象的类!!,即不管你生成多少个类的对象,我们都相当于你调用的是同一个对象;所以上面的输出将会变得有序!

package thread;

 

publicclass ThreadTest10

{

   publicstaticvoid main(String[] args)

   {

      Demo demo1 = new Demo();

      ThreadTest11 test1 = new ThreadTest11(demo1);

      demo1 = new Demo();

      ThreadTest12 test2 = new ThreadTest12(demo1);

      test1.start();

      test2.start();

   }

}

class Demo

{

   publicsynchronizedstaticvoid method1()throws InterruptedException

   {

      for(int i = 0 ; i < 15 ; i ++)

      {

        Thread.sleep((long)(Math.random()*1000));

        System.out.println("hello:"+ i );

      }

   }

   publicsynchronizedstaticvoid method2()throwsInterruptedException

   {

     

      for(int i = 0 ; i < 15; i ++)

      {

        Thread.sleep((long)(Math.random()*1000));

        System.out.println("world:"+i);

       

      }

   }

}

class ThreadTest11extends Thread

{

   private Demodemo;

   public ThreadTest11 (Demo demo)

   {

      this.demo = demo;

   }

   @Override

   publicvoid run()

   {

      try

      {

        demo.method1();

      }

      catch (InterruptedException e)

      {

        // TODO Auto-generatedcatch block

        e.printStackTrace();

      }

   }

  

  

}

class ThreadTest12extends Thread

{

   private Demodemo;

   public ThreadTest12 (Demo demo)

   {

      this.demo = demo;

   }

   @Override

   publicvoid run()

   {

      try

      {

        demo.method2();

      }

      catch (InterruptedException e)

      {

        // TODO Auto-generatedcatch block

        e.printStackTrace();

      }

   }

  

}

 

 

 

 

26.

下面介绍synchronized代码块,个人以为,重要度远远高于单纯的修饰synchronized方法:

一方面:高效率!

另一方面:后面涉及到的wait和notify方法,都要涉及

Synchronized要修饰一个对象

即:synchronized(Object);

表示将对象object上锁,这里的object对象其实是没有用的,只是说明被上锁,个人建议使用this关键字,表示将当前对象上锁!

看下面的synchronized代码块代码同样实现了两个方法的顺序执行:

 

 

 

package thread;

 

publicclass BlockTest

{

   publicstaticvoid main(String[] args)

   {

      Block block = new Block();

      ThreadTestX test1 = new ThreadTestX(block);

      ThreadTestXX test2 = new ThreadTestXX(block);

      test1.start();

      test2.start();

   }

}

class Block

{

   publicvoid method1()

   {

      synchronized (this)

      {

        for(int i = 0; i < 15; i++)

        {

           System.out.println("hello :"+ i);

          

          

        }

      }

   }

   publicvoid method2()

   {

      synchronized(this)

      {

        for(int i = 0 ; i <15; i ++)

        {

           System.out.println("world!"+ i);

          

        }

      }

   }

}

class ThreadTestXextends Thread

{

   private Blockblock;

   public ThreadTestX(Block block)

   {

      this.block = block;

   }

   @Override

   publicvoid run()

   {

      block.method1();

   }

  

}

class ThreadTestXXextends Thread

{

   private Blockblock;

   public ThreadTestXX(Block block)

   {

      this.block = block;

   }

   @Override

   publicvoid run()

   {

      block.method2();

   }

  

}