- notify()、notifyAll()、wait()必须在锁住该对象的同步语句内调用。
摘录Jdoc的说明:
This method should only be called by a thread that is the owner of this object's monitor. A thread becomes the owner of the object's monitor in one of three ways:
By executing a synchronized instance method of that object.
By executing the body of a synchronized statement that synchronizes on the object.
For objects of type Class, by executing a synchronized static method of that class.
Only one thread at a time can own an object's monitor.
但是没有synchronized 同样可以通过编译。
----------------------------------------------------------------------------------------------------------
- synchronzied关键字的使用
public class Two
{
public static void main(String[] args)
{
Abc abc=new Abc();
new Thread(abc).start();
for(int i=0;i<1000;i++);
new Thread(abc).start();
}
}
class Abc implements Runnable
{
public void print()
{
synchronized(this){
int i=0;
while(i<100)
{System.out.println("in print1");
System.out.println("in print2");
i++;
}
}}
public void print1()
{int i=0;
while (i < 100) {System.out.println("anthor");
i++;
}
}
public void run()
{
System.out.println("run");
if(j==0)
{j++;
print();
}
else
{print1();}
}
static int j;
}
注:synchronized(this)并不能阻止非synchronized方法执行,
上面代码会打印出
in print1
anthor
in print2
把synchronized(this)改成 synchronized print()也有相同效果。
代码改成这样也有相同效果
public class Two
{
public static void main(String[] args)
{
Abc abc=new Abc();
new Thread(abc).start();
for(int i=0;i<1000;i++);
new Thread(abc).start();
}
}
class Abc implements Runnable
{
Object lock = new Object();
public void print()
{
synchronized(this){
int i=0;
while(i<100)
{System.out.println("in print1");
System.out.println("in print2");
i++;
}}
}
public void print1()
{synchronized(lock){
int i=0;
while (i < 100) {System.out.println("anthor");
i++;
}}
}
public void run()
{
System.out.println("run");
if(j==0)
{j++;
print1();
}
else
{print();}
}
static int j;
}
注:synchronized(this)和synchronized (lock) 并不会互斥,注意synchronized只作用于对象自身,不对对象里面的成员起作用。
线程的又一个同步。
synchronized(ClassName.class)
static synchronized method()
它们为类级的同步,相对应于synchronized(this)和synchronized method()。
它们是两个级别的访问,synchronized(ClassName.class)和static synchronized method()并不会限制对于对象级的同步访问。可以把它们和起来用:如synchronzied methed(){synchronized(ClassName.class){}},但这么奇怪的写法有什么用,没深究。
----------------------------------------------------------------------------------------------------------
- 内部类的同步问题:
public class outer{
private void printI()
{
synchronized (this)
{while(true)
{
System.out.println(i);
i++;
}
}
}
public class inner implements Runnable
{
public void run()
{
System.out.println("Create");
while(true)
{
System.out.println("inner"+i);
i++;
}
}
}
}
一般在打印“Create”的地方,会表现出同步问题。
虽然在printI()方法中,用synchronized(this)来锁定对象,但内部类的run()不需要外部类的对象锁;所以仍然会同时访问i变量。用下面方法可以锁定:
public void run()
{
System.out.println("Create");
synchronized(outer.this)
{
while(true)
{
System.out.println("inner"+i);
i++;
}
}
}
}
----------------------------------------------------------------------------------------------------------
Java中,object.wait()并不是指object指向的对象进入等待状态。
而是,当前线程(可以是基于object这个对象,也可以是别的对象),把自己加入到object这个对象的等待池中,等待其它线程通过object.notify()唤醒它。
----------------------------------------------------------------------------------------------------------
假设有线程 Thread t
可以调用t.join()来使当前线程等待线程t运行结束后再继续执行。
t.join()相当于一个阻塞方法,要在 t.start()语句之后执行,在 t.start()语句之前执行没有效果。
-------------------------------------------------------------------------------------------------
volatile关键字释义:
首先每个线程都有自己一个工作内存区,多个线程共享一个主内存区。线程中的本地变量存在自己的内存区中,如for(int i=0;i<100;i++){this.i=i;},其中i就存在线程工作内存中,即每个线程都有一个,不用也不能加volatile关键字,this.i就是共享变量。而共享的变量就存在主内存区里,但Java线程为了提高效率,会把共享变量拷贝到自己的工作区中,这就产生了变量一致性的问题。
java提供的一种方法是互斥访问,互斥访问会在加锁和解锁中维持变量的一致性,另一种就是volatile关键字。
java language specification中的一个例子,有类如下:
class Test {
static int i = 0, j = 0;
static void one() { i++; j++; }
static void two() {
System.out.println("i=" + i + " j=" + j);
}
}
有两个线程,一个不停调用 one(),一个不停调用 two(),则有可能出现这种情况,打印出来的j比i还大。因为这时线程对共享变量的更新是无序的。
1.使用同步方法:
class Test {
static int i = 0, j = 0;
static synchronized void one() { i++; j++; }
static synchronized void two() {
System.out.println("i=" + i + " j=" + j);
}
}
这就不用我介绍了,i和j始终一样大。
2.使用volatile
class Test {
static volatile int i = 0, j = 0;
static void one() { i++; j++; }
static void two() {
System.out.println("i=" + i + " j=" + j);
}
}
这样能允许one()和two()并发执行,同时使one()如字面一样执行。这一般能使打印出来的j不会大于i,在更新j之前会先更新i。但有可能打印出来的j比i大很多,因为one()可能在two获取i和j之间执行了很多次。
说明一下:
java language specification中的例子不好,有点晦涩,但我认为volatile的作用就是保证任何时候主内存的i都大于等于j。two()中出现j比i大很多,只是因为它访问的是不同时刻的主内存。
还有一点,就是volatile能防止编译器对变量进行优化,每次共享变量都到主内存。如果线程不到主内存中读,变量的值就会不正确。
强制线程到主存中读,应该也包含上面wulemale所说的,变量被程序外部改变的情况。
而且还有一点要注意,java的这样设计是因为跨平台。为了让更多的java program implement能够在不同的平台利用硬件特性进行优化。不过这样会导致程序的不一致行为。为了保证每次执行都按正确方式。sun 建议使用synchronized来强制在同步块中使用共享变量,这样可以保证程序不会因为使用不同的虚拟机而导致问题。