主要讲synchronized关键字的用法和不同之处
开启多线程
有三个线程ThreadTest1、ThreadTest2、ThreadTest3:
调用一个对象:
启动线程:
先看看非安全线程的实例
即上面代码的输出:
I/System.out: 线程名称为:thread1在1490859330653进入同步块
I/System.out: 线程名称为:thread2在1490859330654进入同步块
I/System.out: 线程名称为:thread3在1490859330657进入同步块
I/System.out: 线程名称为:thread1在1490859333654离开同步块
I/System.out: 线程名称为:thread2在1490859333654离开同步块
I/System.out: 线程名称为:thread3在1490859333657离开同步块
可以看到方法同时被调用,是线程不安全的。
使用synchronized关键字修饰方法
修改如下:
再看看输入:
I/System.out: 线程名称为:thread1在1490859832827进入同步块
I/System.out: 线程名称为:thread1在1490859835827离开同步块
I/System.out: 线程名称为:thread3在1490859835828进入同步块
I/System.out: 线程名称为:thread3在1490859838828离开同步块
I/System.out: 线程名称为:thread2在1490859838828进入同步块
I/System.out: 线程名称为:thread2在1490859841829离开同步块
可以看到线程顺序执行,当一个线程执行完毕下一个线程才会进入,因此是线程安全的。
这里获取的锁是SynchronizedTest的对象实例的锁—对象锁。
多个对象多个锁
threadTest()方法修改如下:
输出:
I/System.out: 线程名称为:thread2在1490860191946进入同步块
I/System.out: 线程名称为:thread1在1490860191950进入同步块
I/System.out: 线程名称为:thread2在1490860194947离开同步块
I/System.out: 线程名称为:thread1在1490860194957离开同步块
I/System.out: 线程名称为:thread3在1490860194957进入同步块
I/System.out: 线程名称为:thread3在1490860197958离开同步块
可以看到线程2和线程1同时进入方法,因为thread1和thread3获取的是synchronizedTest的对象锁,而thread2获取的是synchronizedTest2的对象锁,两者不存在同步问题。
同步块synchronized (this)
将test()方法修改如下:
输入为:
I/System.out: 线程名称为:thread1在1490860807697进入同步块
I/System.out: 线程名称为:thread1在1490860810698离开同步块
I/System.out: 线程名称为:thread2在1490860810699进入同步块
I/System.out: 线程名称为:thread2在1490860813699离开同步块
I/System.out: 线程名称为:thread3在1490860813700进入同步块
I/System.out: 线程名称为:thread3在1490860816700离开同步块
可以看到同样是同步执行的,这里线程获取的是synchronized (this)括号中的对象锁,这里就是synchronizedTest的对象锁。
同步块synchronized (非this对象)
修改test()方法如下:
输出:
I/System.out: 线程名称为:thread1在1490861540167进入同步块
I/System.out: 线程名称为:thread1在1490861543168离开同步块
I/System.out: 线程名称为:thread2在1490861543168进入同步块
I/System.out: 线程名称为:thread2在1490861546169离开同步块
I/System.out: 线程名称为:thread3在1490861546169进入同步块
I/System.out: 线程名称为:thread3在1490861549169离开同步块
可以看到同样是同步执行的,这里线程获取的是synchronized (anyString)括号中的对象锁,这里就是anyString的对象锁。
类中存在两个不同的同步方法会怎么样?
增加一个方法test2():
修改线程2为:
此时运行输出为:
I/System.out: test2 线程名称为:thread2在1490862104089进入同步块
I/System.out: 线程名称为:thread3在1490862104091进入同步块
I/System.out: test2 线程名称为:thread2在1490862107090离开同步块
I/System.out: 线程名称为:thread3在1490862107092离开同步块
I/System.out: 线程名称为:thread1在1490862107092进入同步块
I/System.out: 线程名称为:thread1在1490862110092离开同步块
可以看到线程1和3是顺序执行的,线程2却是同时执行的,因为上面test2()方法被修饰以后,线程2获取到对象锁是synchronizedTest的,而线程1和3调用的是test()方法,获取到的是anyString的对象锁,两者并不冲突。
同步块synchronized (xxx.class)
SynchronizedTest中test()和test2()方法分别如下:
此时线程2调用test2()方法,线程1和3调用test()方法,来看看输出如下:
I/System.out: test2 线程名称为:thread2在1490867370183进入同步块
I/System.out: 线程名称为:thread1在1490867370184进入同步块
I/System.out: test2 线程名称为:thread2在1490867373184离开同步块
I/System.out: 线程名称为:thread1在1490867373185离开同步块
I/System.out: 线程名称为:thread3在1490867373185进入同步块
I/System.out: 线程名称为:thread3在1490867376185离开同步块
可以看到1和3顺序执行,2却同时执行了,为什么呢?原因在于:
直接xxx.class锁是属于直接类的,而this是属于对象锁
这里关系到JVM虚拟机的一些机制,以后再做讨论。
对静态方法使用锁和普通锁的不同之处
SynchronizedTest中test()和test2()方法分别如下:
同样线程2调用test2()方法,线程1和3调用test()方法,来看看输出如下:
I/System.out: test2 线程名称为:thread2在1490866857936进入同步块
I/System.out: 线程名称为:thread3在1490866857938进入同步块
I/System.out: test2 线程名称为:thread2在1490866860936离开同步块
I/System.out: 线程名称为:thread3在1490866860939离开同步块
I/System.out: 线程名称为:thread1在1490866860939进入同步块
I/System.out: 线程名称为:thread1在1490866863939离开同步块
可以看到1和3线程顺序执行,线程2却同时执行了,这里就是锁普通方法和静态方法的区别,如同上面使用直接类和对象的区别:
静态方法的锁和xxx.class锁是一样的直接属于类
到此结束,自己学习使用过程中的一些练习和理解,不对之处还请指出。还有一些更深入的关于获取锁对象的机制等问题,以后再做研究。