感谢张龙老师!
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只能锁住一个类的对象,而被 synchronized和static修饰的方法,将会锁住对象的类!!,即不管你生成多少个类的对象,我们都相当于你调用的是同一个对象;所以上面的输出将会变得有序!
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();
}
}