在多线程编程中,为了避免资源访问冲突,需要线程同步。在Java中用synchronized关键字来锁住当前线程访问对象。使用synchronized关键来修饰一个方法或者一个代码块的时候,能够保证在同一时刻最多只有一个线程执行该段代码。
一. 当两个并发线程访问同一个对象object中的这个synchronized(this)同步代码块时,一个时间内只能有一个线程得到执行。另一个线程必须等待当前线程执行完这个代码块以后才能执行该代码块。
二. 当一个线程访问object的一个synchronized(this)同步代码块时,另一个线程仍然可以访问该object中的非synchronized(this)同步代码块。
三. 当一个线程访问object的一个synchronized(this)同步代码块时,其他线程对object中所有其它synchronized(this)同步代码块的访问将被阻塞。
四. 当一个线程访问object的一个synchronized(this)同步代码块时,它就获得了这个object的对象锁。结果,其它线程对该object对象所有同步代码部分的访问都被暂时阻塞。
以上规则对其它对象锁同样适用.
下面就通过具体例子来解释程序运行过程。
(1) 在Class中使用synchronized声明某一方法:可参照上面的规则二
public class ThreadSync implements Runnable{
int b = 100;
public synchronized void fun1() throws Exception {
b = 1000;
Thread.sleep(1000);
System.out.println("b = " + b);
}
public void fun2() throws Exception {
Thread.sleep(2500);
b = 2000;
}
public void run() {
try {
fun1();
} catch(Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) throws Exception{
// TODO Auto-generated method stub
ThreadSync_2 t = new ThreadSync_2();
Thread thread = new Thread(t);
thread.start(); //启动线程
t.fun2();
System.out.println(t.b);
}
}
运行结果:
上面程序的运行过程是这样的:main线程 --- 启动thread线程 --- t.fun()执行 --- sleep(主线程休眠2.5s) ----------- 主线程被唤醒继续执行,b=2000, 打印 2000,结束主线程
--- thread线程运行 --- b = 1000, thread线程休眠1s --- 打印 1000,thread线程结束
(2) 多线程访问对象的Synchronized声明的方法(代码块)
public class ThreadSync_2 implements Runnable{
int b = 100;
public synchronized void fun1() throws Exception {
b = 1000;
Thread.sleep(1000);
System.out.println("b = " + b);
}
public synchronized void fun2() throws Exception {
Thread.sleep(2500);
b = 2000;
}
public void run() {
try {
fun1();
} catch(Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) throws Exception{
// TODO Auto-generated method stub
ThreadSync_2 t = new ThreadSync_2();
Thread thread = new Thread(t);
thread.start(); //启动线程
t.fun2();
System.out.println(t.b);
}
}
运行结果:
上面程序中使用了synchronized关键字同时声明了fun1、fun2方法,因此当一个线程访问到Object的fun1或fun2时,其他线程如果要访问Object的fun1或fun2,,就会被阻塞。程序执行过程如下:(一般线程启动后并不是立即执行,要等待CPU的调度)
main线程 --- 启动thread线程--- 主线程运行fun2(主线程获得对象t的锁) --- 主线程休眠2.5s --- 主线程被唤醒,b = 2000,释放锁 --- 打印 1000
thread线程运行 ---- thread线程访问t对象被阻塞 --- 获得锁 b=1000, 休眠1s ---------------打印 b=1000
最后总结一下:
在一个类中,当多个方法都会修改类的同一个属性值时,为了线程同步,最好使用synchronzed关键字声明类中所有会修改该属性的方法。