参考网址(写得非常好,珍藏网址之一):http://www.blogjava.net/tscfengkui/archive/2010/11/10/337709.html?opt=admin
举一个在多线程操作当中经典的例子,电影院售票问题。
电影院的窗口可以同时售票,售出的票不能重叠,即不能售出两张11排11号的位置。
eg1(非常经典的一段代码):
class Source implements Runnable {
private int ticket = 10;
@Override
public void run() {
// TODO Auto-generated method stub
for( int i = 0; i < 50; i++){
if( ticket>0){
try {
Thread. sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System. out.println(Thread. currentThread().getName()+ "号窗口售出" + "第" + ticket-- + "张票");
}
}
}
}
public class test9 {
public static void main(String[] args) {
//只能新建一个source,确保三个线程都是对同一个source操作
Source source = new Source();
new Thread(source, "A").start();
new Thread(source, "B").start();
new Thread(source, "C").start();
}
}
可以看到C、B都售出标记为1号的票,因为线程C售出1号的票后,没来得及自减,就被线程B访问了!这明显不符合业务的逻辑。
我们需要这样一套机制,当资源正在被某一线程访问的时候,资源被锁住,其他线程访问不了,直到被访问的线程退出使用该资源。
原理图:
很幸运的是,Java已经帮我们实现了这个机制,只要使用
synchronized
关键字就完美解决了这个问题。程序员不用去管Java内部怎么实现这个机制的,只要使用它就可以了。
synchronized
的用法:
1、放方法名前形成同步方法
eg2:
class Source implements Runnable {
private int ticket = 10;
@Override
public void run() {
// TODO Auto-generated method stub
for( int i = 0; i < 50; i++){
try {
Thread. sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
sale();
}
}
public synchronized void sale() {
if( ticket>0){
System.out.println(Thread.currentThread().getName()+ "号窗口售出" + "第" + ticket -- + "张票" );
}
}
}
结果:
2、构成
同步块
class Source implements Runnable {
private int ticket = 10;
@Override
public void run() {
// TODO Auto-generated method stub
for( int i = 0; i < 50; i++){
try {
Thread. sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
synchronized ( this) {
if( ticket>0){
System. out.println(Thread. currentThread().getName()+ "号窗口售出" + "第" + ticket-- + "张票" );
}
}
}
}
}
结果:
VIP:synchronized 的位置非常关键,在不同的地方有不同的效果,以下几个例子,读者自行分析。
eg4:
class Source implements Runnable {
private int ticket = 10;
@Override
public void run() {
// TODO Auto-generated method stub
for( int i = 0; i < 50; i++){
if( ticket>0){
try {
Thread. sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
synchronized( this){
System. out.println(Thread. currentThread().getName()+ "号窗口售出" + "第" + ticket-- + "张票" );
}
}
}
}
}
结果:
eg5:
class Source implements Runnable {
private int ticket = 10;
@Override
public synchronized void run() {
// TODO Auto-generated method stub
for( int i = 0; i < 50; i++){
if( ticket>0){
try {
Thread. sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System. out.println(Thread. currentThread().getName()+ "号窗口售出" + "第" + ticket-- + "张票" );
}
}
}
}
结果: