02____线程的同步(Synchronized)

来源:互联网 发布:sql 表别名无法使用 编辑:程序博客网 时间:2024/06/08 03:14

java语言的关键字,当它用来修饰一个方法或者一个代码块的时候,能够保证在同一时刻最多只有一个线程执行该段代码。


模拟账户存取款的小例子:

public class Test1 implements Runnable {    public int cash = 100;    public void m() {        System.out.println("m查看账户,余额为"+cash);        try {            Thread.sleep(1000);        } catch (InterruptedException e) {            e.printStackTrace();        }        cash = cash - 100;        System.out.println("cash1 = " + cash);    }    public  void run() {        System.out.println("run查看账户,余额为"+cash);        cash += 1000;        try {            Thread.sleep(5000);        } catch (InterruptedException e) {            e.printStackTrace();        }        System.out.println("cash2 = " + cash);    }    public static void main(String[] args) {        Test1 test = new Test1();        Thread thrad = new Thread(test);        thrad.start();        test.m();    }}
打印结果:

m查看账户,余额为100
run查看账户,余额为100
cash1 = 1000
cash2 = 1000

如果这是一个模拟银行的小程序,run方法和m为两个人操作同一个账户,run方法存入了1000,m方法取了100。

首先,m看账户余额,有100,于是就取走了100,再次查询余额时,发现余额,是1000。他并不知道其他人还在操作,发现账户多了1000块。

而run也是一样,存入了1000,然后查看余额的时候发现少了100块。

这样的话,两个人都会感到很诧异。

要解决这种情况,就要让账户进行锁定,在同一时间段只能让一个人进行操作。

加入synchronized:

public class Test implements Runnable {    public int cash = 100;    public synchronized void m() {        System.out.println("m查看账户,余额为"+cash);        try {             Thread.sleep(2000);        } catch (InterruptedException e) {             e.printStackTrace();        }        cash = cash - 100;        System.out.println("cash1 = " + cash);    }    public synchronized void run() {        System.out.println("run查看账户,余额为"+cash);        cash += 1000;        try {            Thread.sleep(5000);         } catch (InterruptedException e) {             e.printStackTrace();         }        System.out.println("cash2 = " + cash);    }    public static void main(String[] args) {        Test test = new Test();        Thread thrad = new Thread(test);        thrad.start();        test.m();    }}
打印结果:

m查看账户,余额为100
cash1 = 0
run查看账户,余额为0
cash2 = 1000

添加synchronized关键字后可以看出。只要m或者run进行对账户进行操作,不论中途多长时间,或者睡眠多长时间,线程都要执行完这个方法以后才会执行其他的方法。这样这种方法就解决了。

但是要注意的是,两个方法都必须加synchronized关键字,并且两者锁定同一对象(此处锁定的对象是test对象)。

也就是说,只要有一个线程进入test对象的任意一个加了锁的方法,其他线程就不能访问这个对象里加了相同锁的方法了。

所以如果只在其中一个方法内加synchronized是没有用的(两者没有锁定同一对象)。


可以这样理解:一个方法是一栋大楼,楼中有很多房子(方法),很多房子锁子都是一样的(加了相同的锁),但是只有一把
钥匙。你拿了钥匙走进一个房子,其他人就进不了相同锁的房子了。直到你出来。但是别人可以进入其他的不同锁子的房
间。

再看一个例子:

public class Test {public static int cash = 100; public static void main(String[] args) { Thread thread2 = new Thread(new Runnable() { //public synchronized void run() { public void run() { synchronized(this){System.out.println("run查看账户,余额为" + cash);cash += 1000;try { Thread.sleep(5000); } catch (InterruptedException e) {e.printStackTrace();}System.out.println("cash2 = " + cash);}}});thread2.start();Thread thread1 = new Thread(new Runnable() {//public synchronized void run() {public  void run() {synchronized(this){System.out.println("m查看账户,余额为" + cash);try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}cash = cash - 100;System.out.println("cash1 = " + cash);}}});thread1.start();}}

但是上面的这种方法也是不能形成互斥的。这种情况下不论run方法前加synchronized还是在逻辑块中加synchronized
(this),形成的锁是各自new出Thread实例(一个是thread1,一个是threa2)。所以两个this不一样。不能形成互斥。要形
成互斥,可以使用Test的字节码。或者自己指定一把锁synchronized("1");

打印名字的例子:

public class TraditionalThreadSynchronized {/** * @param args */public static void main(String[] args) {TraditionalThreadSynchronized t = new TraditionalThreadSynchronized();t.init();}public void init(){final Output output = new Output();new Thread(new Runnable() {public void run() {while(true){try {Thread.sleep(10);} catch (InterruptedException e) {e.printStackTrace();}output.output("xuyang");}}}).start();new Thread(new Runnable() {public void run() {while(true){try {Thread.sleep(10);} catch (InterruptedException e) {e.printStackTrace();}output.output("aaaa");}}}).start();}class Output{//public synchronized void output(String name)//如果要给方法内的所有代码加互斥,只需在方法名上加入关键字就可以。public void output(String name){if(name!=null){/* * 添加线程互斥,只让一个线程进入这个方法。 * 添加的锁子是name时,没有起到互斥效果,原因就是name传过来的值不同。不是同一把锁。 * 要互斥,必须让锁子是同一把。 * 两个线程都用的是同一个new出来的output,所以output就是同一个对象。 * 这里this指的就是output对象。 *///synchronized (name)synchronized (this) {for(int i = 0;i<name.length();i++){System.out.print(name.charAt(i));}System.out.println();}}}/* *public void output2(String id){} *如过类中有多个方法,要形成互斥 *互斥的栓子就可以是这个类的字节码,Output.class,字节码在全局中只有一份 */}}

总结synchronized关键字:


一、当两个并发线程访问同一个对象object中的这个synchronized(this)同步代码块时,一个时间内只能有一个线程得到
执行。另一个线程必须等待当前线程执行完这个代码块以后才能执行该代码块。
二、然而,当一个线程访问object的一个synchronized(this)同步代码块时,另一个线程仍然可以访问该object中的非
synchronized(this)同步代码块。
三、尤其关键的是,当一个线程访问object的一个synchronized(this)同步代码块时,其他线程对object中所有其它
synchronized(this)同步代码块的访问将被阻塞。
四、第三个例子同样适用其它同步代码块。也就是说,当一个线程访问object的一个synchronized(this)同步代码块时,
它就获得了这个object的对象锁。结果,其它线程对该object对象所有同步代码部分的访问都被暂时阻塞。
五、以上规则对其它对象锁同样适用.
所以synchronized(this)关键的是锁的粒度大小。



0 0