工作之余补充一下线程方面的知识。直接上源码:
package cn.qxp.test;
/**
* 此线程会出现脏读的情况。即输出结果有可能是两个800,或者两个100.
*
* 原因:th和th2是SyncThread两个不同的对象实例,
* 在todo中的synchronized同步检查的目标是不一样的。
*
* 要特别注意synchronized检查是目标
* @author Administrator
*
*/
public class SyncThread implements Runnable{
private Integer value;
private static Integer NOWVALUE;
public SyncThread(Integer value) {
this.value = value;
}
//---------------------------------------------------------------------------------------------------
/* 脏读写法1。
* 这是同步方法,检查的目标是当前对象实例,由于new了两个对象实例,当后面的线程执行到这里时,并不会进行检查。
*
* 当线程1执行到NOWVALUE = this.value,value=100时,在执行打印语句之前,
* 线程2也刚好执行到NOWVALUE = this.value,这是就会打印出两个100。脏读出现
*
* */
/*private synchronized void todo() {
NOWVALUE = this.value;
System.out.println("NOWVALUE==="+NOWVALUE);
}
*/
//---------------------------------------------------------------------------------------------------
/*
* 脏读写法2。
* 这是同步方法加同步代码快,this检查的也是当前的对象实例
* 出现脏读的原因与上基本一致
*
*/
/*private synchronized void todo() {
synchronized (this) {
NOWVALUE = this.value;
System.out.println("NOWVALUE==="+NOWVALUE);
}
}*/
//---------------------------------------------------------------------------------------------------
/*
* 不会出现脏读的写法。
* 同步代码块中,检查的是SyncThread.class。
* 表示对SyncThread这个类的所有实例进行检查。
* 只要是SyncThread的实例。在执行到synchronized都会进行检查,
* 若没有其他线程在synchronized里面,则执行。若有则等待正在执行的释放对象锁,抢占执行权
*
*/
private void todo() {
synchronized (SyncThread.class) {
NOWVALUE = this.value;
System.out.println("NOWVALUE==="+NOWVALUE);
}
}
//---------------------------------------------------------------------------------------------------
@Override
public void run() {
this.todo();
}
public static void main(String[] args) {
Thread th = new Thread(new SyncThread(100));
Thread th2 = new Thread(new SyncThread(800));
th.start();
th2.start();
}
}
总结:
判断一个对象是否线程安全,除了synchronized 之外,更重要的是synchronized锁的目标。
在脏读写法1中,非静态方法上加锁。synchronized锁的目标是:当前对象的todo方法。若非当前对象,则锁无效。使用new创建了一个全新的对象实例,非当前对象。
脏读写法2中,synchronized(this){},锁的目标也是当前对象(this指代的是当前对象),
非脏读写法中,synchronized(SyncThread.class){},锁的目标是SyncThread类是class对象,SyncThread的所有实例对象进行同步锁检查。
静态方法上加锁,private static synchronized void todo(){}这种写法,与synchronized(SyncThread.class){}写法非常类似。