在这里先向读者们道歉,由于之前的不严谨,从头认识多线程-2.15 证明使用整数属性域作为多线程监视器是不同步的这一章里面的例子有所偏差,导致结论出现偏差,在这里再次道歉,因此,才有今天的这一个章节出现。
这一章节我们来讨论一下synchronized (new object())同步时各种不同类别的监视器以及其引起的问题.
1.使用整数作为属性域,而且该属性域就是多线程同步的监视器
package com.ray.deepintothread.ch02.topic_19;
/**
*
* @author RayLee
*
*/
public class MonitorOfSynch {
public static void main(String[] args) throws InterruptedException {
MyService myService = new MyService();
ThreadOne threadOne = new ThreadOne(myService);
Thread thread = new Thread(threadOne);
thread.setName("thread A");
thread.start();
ThreadTwo threadTwo = new ThreadTwo(myService);
Thread thread2 = new Thread(threadTwo);
thread2.setName("thread B");
thread2.start();
}
}
class ThreadOne implements Runnable {
private MyService myService;
public ThreadOne(MyService myService) {
this.myService = myService;
}
@Override
public void run() {
try {
myService.updateA();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
class ThreadTwo implements Runnable {
private MyService myService;
public ThreadTwo(MyService myService) {
this.myService = myService;
}
@Override
public void run() {
try {
myService.updateB();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
class MyService {
private Integer id = 0;
public void updateA() throws InterruptedException {
synchronized (id) {
for (int i = 0; i < 5; i++) {
System.out.println("id:" + id);
System.out.println(Thread.currentThread().getName() + " " + id++);
Thread.sleep(50);
System.out.println("-------------------");
}
}
}
public void updateB() throws InterruptedException {
synchronized (id) {
for (int i = 0; i < 5; i++) {
System.out.println("id:" + id);
System.out.println(Thread.currentThread().getName() + " " + id++);
Thread.sleep(100);
System.out.println("-------------------");
}
}
}
}
输出:
id:0
thread A 0
id:1
thread B 1
-------------------
id:2
thread A 2
-------------------
-------------------
//注意下面的输出
id:3
thread B 3
id:3
thread A 4
-------------------
id:5
thread A 5
-------------------
-------------------
id:6
thread B 6
id:6
thread A 7
-------------------
-------------------
id:8
thread B 8
-------------------
id:9
thread B 9
-------------------
从输出我们可以看见,毫无疑问的是,上面出现了脏读,两个方法之间不同步。
2.使用字符串作为属性域,而且该属性域就是多线程同步的监视器
package com.ray.deepintothread.ch02.topic_19;
/**
*
* @author RayLee
*
*/
public class MonitorOfSynch3 {
public static void main(String[] args) throws InterruptedException {
MyService3 myService = new MyService3();
ThreadFive threadFive = new ThreadFive(myService);
Thread thread = new Thread(threadFive);
thread.setName("thread A");
thread.start();
ThreadSix threadSix = new ThreadSix(myService);
Thread thread2 = new Thread(threadSix);
thread2.setName("thread B");
thread2.start();
}
}
class ThreadFive implements Runnable {
private MyService3 myService;
public ThreadFive(MyService3 myService) {
this.myService = myService;
}
@Override
public void run() {
try {
myService.updateA();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
class ThreadSix implements Runnable {
private MyService3 myService;
public ThreadSix(MyService3 myService) {
this.myService = myService;
}
@Override
public void run() {
try {
myService.updateB();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
class MyService3 {
private String name = "ray";
public void updateA() throws InterruptedException {
synchronized (name) {
for (int i = 0; i < 5; i++) {
System.out.println("name:" + name);
// 修改监视器
name += "-" + i;
System.out.println(Thread.currentThread().getName() + " " + name);
Thread.sleep(50);
// 恢复原形
name = "ray";
System.out.println("-------------------");
}
}
}
public void updateB() throws InterruptedException {
synchronized (name) {
for (int i = 0; i < 5; i++) {
System.out.println("name:" + name);
// 修改监视器
name += "-" + i;
System.out.println(Thread.currentThread().getName() + " " + name);
Thread.sleep(100);
// 恢复原形
name = "ray";
System.out.println("-------------------");
}
}
}
}
输出:
//注意第一个的输出
name:ray
thread A ray-0
name:ray-0
thread B ray-0-0
-------------------
name:ray
thread A ray-1
-------------------
name:ray
thread A ray-2
-------------------
name:ray
thread B ray-1
-------------------
name:ray
thread A ray-3
-------------------
-------------------
name:ray
name:ray
thread A ray-2-4
thread B ray-2
-------------------
-------------------
name:ray
thread B ray-3
-------------------
name:ray
thread B ray-4
-------------------
从输出我们可以看见,毫无疑问的是,上面出现了脏读,两个方法之间不同步。
3.使用自有对象作为属性域,而且该属性域就是多线程同步的监视器
package com.ray.deepintothread.ch02.topic_19;
/**
*
* @author RayLee
*
*/
public class MonitorOfSynch2 {
public static void main(String[] args) throws InterruptedException {
MyService2 myService = new MyService2();
ThreadThree threadThree = new ThreadThree(myService);
Thread thread = new Thread(threadThree);
thread.setName("thread A");
thread.start();
ThreadFour threadFour = new ThreadFour(myService);
Thread thread2 = new Thread(threadFour);
thread2.setName("thread B");
thread2.start();
}
}
class ThreadThree implements Runnable {
private MyService2 myService;
public ThreadThree(MyService2 myService) {
this.myService = myService;
}
@Override
public void run() {
try {
myService.updateA();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
class ThreadFour implements Runnable {
private MyService2 myService;
public ThreadFour(MyService2 myService) {
this.myService = myService;
}
@Override
public void run() {
try {
myService.updateB();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
class MyService2 {
private Person person = new Person();
public void updateA() throws InterruptedException {
synchronized (person) {
for (int i = 0; i < 5; i++) {
System.out.println("person name:" + person.getName());
// 设置新的属性
person.setId(i);
person.setName(person.getName() + "-" + i);
System.out.println(Thread.currentThread().getName() + " " + person.getName());
Thread.sleep(50);
// 复原对象
person.setId(0);
person.setName("ray");
System.out.println("-------------------");
}
}
}
public void updateB() throws InterruptedException {
synchronized (person) {
for (int i = 0; i < 5; i++) {
System.out.println("person name:" + person.getName());
// 设置新的属性
person.setId(i);
person.setName(person.getName() + "-" + i);
System.out.println(Thread.currentThread().getName() + " " + person.getName());
Thread.sleep(100);
// 复原对象
person.setId(0);
person.setName("ray");
System.out.println("-------------------");
}
}
}
}
/**
* 自有对象
*
* @author R
*
*/
class Person {
private int id = 0;
private String name = "ray";
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
输出:
person name:ray
thread A ray-0
-------------------
person name:ray
thread A ray-1
-------------------
person name:ray
thread A ray-2
-------------------
person name:ray
thread A ray-3
-------------------
person name:ray
thread A ray-4
-------------------
person name:ray
thread B ray-0
-------------------
person name:ray
thread B ray-1
-------------------
person name:ray
thread B ray-2
-------------------
person name:ray
thread B ray-3
-------------------
person name:ray
thread B ray-4
-------------------
从输出我们可以看见,虽然我们改变了监视器,但是,上面没有出现了脏读,两个方法之间是同步的。
4.使用时间作为属性域,而且该属性域就是多线程同步的监视器
package com.ray.deepintothread.ch02.topic_19;
import java.util.Date;
/**
*
* @author RayLee
*
*/
public class MonitorOfSynch4 {
public static void main(String[] args) throws InterruptedException {
MyService4 myService = new MyService4();
ThreadSeven threadSeven = new ThreadSeven(myService);
Thread thread = new Thread(threadSeven);
thread.setName("thread A");
thread.start();
ThreadEight threadEight = new ThreadEight(myService);
Thread thread2 = new Thread(threadEight);
thread2.setName("thread B");
thread2.start();
}
}
class ThreadSeven implements Runnable {
private MyService4 myService;
public ThreadSeven(MyService4 myService) {
this.myService = myService;
}
@Override
public void run() {
try {
myService.updateA();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
class ThreadEight implements Runnable {
private MyService4 myService;
public ThreadEight(MyService4 myService) {
this.myService = myService;
}
@Override
public void run() {
try {
myService.updateB();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
class MyService4 {
private Date date = new Date();
public void updateA() throws InterruptedException {
synchronized (date) {
for (int i = 0; i < 5; i++) {
System.out.println("date:" + date.toString());
date = new Date();
Thread.sleep(1000);
System.out.println(Thread.currentThread().getName() + " " + date.toString());
Thread.sleep(500);
System.out.println("-------------------");
}
}
}
public void updateB() throws InterruptedException {
synchronized (date) {
for (int i = 0; i < 5; i++) {
System.out.println("date:" + date.toString());
date = new Date();
Thread.sleep(1000);
System.out.println(Thread.currentThread().getName() + " " + date.toString());
Thread.sleep(1000);
System.out.println("-------------------");
}
}
}
}
输出:
date:Thu May 12 21:13:49 CST 2016
thread A Thu May 12 21:13:49 CST 2016
-------------------
date:Thu May 12 21:13:49 CST 2016
thread A Thu May 12 21:13:50 CST 2016
-------------------
date:Thu May 12 21:13:50 CST 2016
thread A Thu May 12 21:13:52 CST 2016
-------------------
date:Thu May 12 21:13:52 CST 2016
thread A Thu May 12 21:13:53 CST 2016
-------------------
date:Thu May 12 21:13:53 CST 2016
thread A Thu May 12 21:13:55 CST 2016
-------------------
date:Thu May 12 21:13:55 CST 2016
thread B Thu May 12 21:13:56 CST 2016
-------------------
date:Thu May 12 21:13:56 CST 2016
thread B Thu May 12 21:13:58 CST 2016
-------------------
date:Thu May 12 21:13:58 CST 2016
thread B Thu May 12 21:14:00 CST 2016
-------------------
date:Thu May 12 21:14:00 CST 2016
thread B Thu May 12 21:14:02 CST 2016
-------------------
date:Thu May 12 21:14:02 CST 2016
thread B Thu May 12 21:14:04 CST 2016
-------------------
从输出我们可以看见,虽然我们改变了监视器,但是,上面没有出现了脏读,两个方法之间是同步的。
5.使用容器作为属性域,而且该属性域就是多线程同步的监视器
package com.ray.deepintothread.ch02.topic_19;
import java.util.ArrayList;
/**
*
* @author RayLee
*
*/
public class MonitorOfSynch5 {
public static void main(String[] args) throws InterruptedException {
MyService5 myService = new MyService5();
ThreadNine threadNine = new ThreadNine(myService);
Thread thread = new Thread(threadNine);
thread.setName("thread A");
thread.start();
ThreadTen threadTen = new ThreadTen(myService);
Thread thread2 = new Thread(threadTen);
thread2.setName("thread B");
thread2.start();
}
}
class ThreadNine implements Runnable {
private MyService5 myService;
public ThreadNine(MyService5 myService) {
this.myService = myService;
}
@Override
public void run() {
try {
myService.updateA();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
class ThreadTen implements Runnable {
private MyService5 myService;
public ThreadTen(MyService5 myService) {
this.myService = myService;
}
@Override
public void run() {
try {
myService.updateB();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
class MyService5 {
private ArrayList<Integer> list = new ArrayList<>();
public void updateA() throws InterruptedException {
synchronized (list) {
for (int i = 0; i < 5; i++) {
System.out.println("list size:" + list.size());
list.add(i);
System.out.println(Thread.currentThread().getName() + " " + list.size());
Thread.sleep(50);
System.out.println("-------------------");
}
}
}
public void updateB() throws InterruptedException {
synchronized (list) {
for (int i = 0; i < 5; i++) {
System.out.println("list size:" + list.size());
list.add(i);
System.out.println(Thread.currentThread().getName() + " " + list.size());
Thread.sleep(100);
System.out.println("-------------------");
}
}
}
}
输出:
list size:0
thread A 1
-------------------
list size:1
thread A 2
-------------------
list size:2
thread A 3
-------------------
list size:3
thread A 4
-------------------
list size:4
thread A 5
-------------------
list size:5
thread B 6
-------------------
list size:6
thread B 7
-------------------
list size:7
thread B 8
-------------------
list size:8
thread B 9
-------------------
list size:9
thread B 10
-------------------
从输出我们可以看见,虽然我们改变了监视器,但是,上面没有出现了脏读,两个方法之间是同步的。
6.现象结论
使用整数或者字符串作为属性域,并且该属性域作为多线程同步的监视器,这个时候,方法间是不同步的,而其他对象均可同步。
7.为什么?
因为jvm对于某个段的整数或者字符串做了相应的优化
package com.ray.deepintothread.ch02.topic_19;
public class ComparisonInteger {
public static void main(String[] args) {
Integer a = 0;
Integer b = 0;
System.out.println(a == b);
Integer c = 1000;
Integer d = 1000;
System.out.println(c == d);
}
}
输出:
true
false
package com.ray.deepintothread.ch02.topic_19;
public class ComparisonString {
public static void main(String[] args) {
String a = "a";
String b = "a";
System.out.println(a == b);
String c = new String("a");
String d = new String("a");
System.out.println(c == d);
}
}
输出:
true
false
上面的两组代码证明了jvm存在常量池,而整数和字符串都有一部分属于常量池,这只jvm自有的优化,在使用这两种对象作为同步监视器,则会出现不同步的现象。
大家这里必须注意这个地方。
总结:这一章节讨论了各种不同类别的监视器以及所引起的问题。
这一章节就到这里,谢谢
------------------------------------------------------------------------------------
我的github:https://github.com/raylee2015/DeepIntoThread
目录:http://blog.csdn.net/raylee2007/article/details/51204573