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