有了synchronized关键字,多线程程序的运行结果将变得可以控制。synchronized关键字用于保护共享数据。请大家注意"共享数据",
你一定要分清哪些数据是共享数据,JAVA是面向对象的程序设计语言,所以初学者在编写多线程程序时,容易分不清哪些数据是共享数据。请看下面的例子:
实例一:
public class FirstThread implements Runnable{
public synchronized void run(){
for(int i=1;i<10;i++){
System.out.println(""+i);
}
}
public FirstThread(){
}
public static void main(String[] args){
Runnable r1=new FirstThread();
Runnable r2=new FirstThread();
Thread t1=new Thread(r1);
Thread t2=new Thread(r2);
t1.start();
t2.start();
}
}
在这个程序中,run()被加上了synchronized关键字。在main方法中创建了两个线程。你可能会认为此程序的运行结果一定为:0123456789 0123456789。答案不是这样子的,
这个程序中synchronized关键字保护的不是共享数据(其实在这个程序中synchronized关
键字没有起到任何作用,此程序的运行结果是不可预先确定的)。这个程序中的t1,t2是两个对象(r1,r2)的线程。JAVA是面向对象的程序设计语
言,不同的对象的数据是不同的,r1,r2有各自的run()方法,而synchronized使同一个对象的多个线程,在某个时刻只有其中的一个线程可
以访问这个对象的synchronized数据。每个对象都有一个"锁标志",当这个对象的一个线程访问这个对象的某个synchronized数据时,
这个对象的所有被synchronized修饰的数据将被上锁(因为"锁标志"被当前线程拿走了),只有当前线程访问完它要访问的
synchronized数据时,当前线程才会释放"锁标志",这样同一个对象的其它线程才有机会访问synchronized数据。
实例二:
public class SecondThread implements Runnable{
public synchronized void run(){
for(int i=1;i<10;i++){
System.out.println(""+i);
}
}
public SecondThread(){
}
public static void main(String[] args){
Runnable r=new SecondThread();
//Runnable r2=new FirstThread();
Thread t1=new Thread(r);
Thread t2=new Thread(r);
t1.start();
t2.start();
}
}
如果你运行1000次这个程序,它的输出结果也一定每次都是:01234567890123456789。因为这里的synchronized保护的是共享数据。t1,t2是同一个对象(r)的两个线程,当其中的一个线程(例如:t1)开始执行run()方法时,由于run()受
synchronized保护,所以同一个对象的其他线程(t2)无法访问synchronized方法(run方法)。只有当t1执行完后t2才有机会
执行。
实例三:
public class ThreeThread implements Runnable{
public void run(){
synchronized(this){
for(int i=1;i<10;i++){
System.out.println(""+i);
}}
}
public ThreeThread(){
}
public static void main(String[] args){
Runnable r=new SecondThree();
//Runnable r2=new FirstThread();
Thread t1=new Thread(r);
Thread t2=new Thread(r);
t1.start();
t2.start();
}
}
这个程序与示例2的运行结果一样。在可能的情况下,应该把保护范围缩到最小,可以用示例3的形式,this代表"这个对象"。没有必要把整个run()保护起来,run()中的代码只有一个for循环,所以只要保护for循环就可以了。
实例四:
public class FourThread implements Runnable{
public void run(){
for(int i=1;i<10;i++){
System.out.println(Thread.currentThread().getName()+"forloop"+i);
}
synchronized(this){
for(int i=1;i<10;i++){
System.out.println(Thread.currentThread().getName()+"synchronized"+i);
}}
}
public SecondThread(){
}
public static void main(String[] args){
Runnable r=new ThreeThread();
//Runnable r2=new FirstThread();
Thread t1=new Thread(r);
Thread t2=new Thread(r);
t1.start();
t2.start();
}
}
t1_name:forloop:0
t1_name:forloop:1
t1_name:forloop:2
t2_name:forloop:0
t1_name:forloop:3
t2_name:forloop:1
t1_name:forloop:4
t2_name:forloop:2
t1_name:synchronizedforloop:0
t2_name:forloop:3
t1_name:synchronizedforloop:1
t2_name:forloop:4
t1_name:synchronizedforloop:2
t1_name:synchronizedforloop:3
t1_name:synchronizedforloop:4
t2_name:synchronizedforloop:0
t2_name:synchronizedforloop:1
t2_name:synchronizedforloop:2
t2_name:synchronizedforloop:3
t2_name:synchronizedforloop:4
第一个for循环没有受synchronized保护。对于第一个for循环,t1,t2可以同时访问。运行结果
表明t1执行到了k=2 时,t2开始执行了。t1首先执行完了第一个for循环,此时还没有执行完第一个
for循环(t2刚执行到k=2)。t1开始执行第二个for循环,当 t1的第二个for循环执行到k=1时,t2
的第一个for循环执行完了。http://bianceng.cn(编程入门)
t2想开始执行第二个for循环,但由于t1首先执行了第二个for循环,这个对象的锁标志自然在
t1手中(synchronized方法的执行权也就落到了t1手中),在t1没执行完第二个for循环的时候,它
是不会释放锁标志的。
所以t2必须等到t1执行完第二个for循环后,它才可以执行第二个for循环