关于java 多线程编程的同步问题

      说到线程编程,我们初步的认识,就是通过编程的方式让系统资源在不同的线程中切换,从而实现系统高效的运行。然后这看似简单明了的问题,实际操作起来,却隐藏了很多复杂的细节,其中线程间的同步和通信问题,就是导致多线程编程出现各种异常的关键。举一个简单的例子,银行的有多个服务窗口,每一个服务窗口通过叫号提供服务。假设系统设计了一个变量number,代表当前的的号数,当number大于10的时候,就不再叫号。现在假设有三个窗口线程1,2,3.线程1执行到①的时候,判断number的值正好等于10,准备执行提供服务②的时候,系统分配的时间资源用完切换到了线程2,此时线程1所保存的断点是②处的地址信息。线程2运行到判断语句①,发现number仍然是10满足条件,进入提供服务②,并且将number加一,当系统又切换到线程1的时候,从断点②处开始,并不经过判断,就执行了提供服务,而实际的number值是11,不应该被提供服务。这就是一个线程的同步问题,我们应该通过一种机制保证这种现象不发生,在java中有一个叫同步锁synchronized的机制保证了对于临界区域在同一时间段只允许一个线程的操作。

if(number<=10) .......①

{

       提供服务;     .........②

       number++;  .........③

}

     现在我们看一个加入了同步锁的方式所写的一个银行服务窗口程序,该程序例子,是我从一本书上选取的,我自己修改扩展了一下。

class TicketWindow3 implements Runnable
{
private static int max_value = 0;
private boolean flag = true;
public void run()
{
if(flag)
{
while(true)
{
synchronized(TicketWindow3.class)
{
if(max_value > 500)
{
break;
}
try
{
Thread.sleep(10);
}catch (InterruptedException e)
{

}
System.out.println(Thread.currentThread().getName()+":lock..."+max_value++);
}
}
}
else
{
while(true)
if(ticket())
break;
}
}
private synchronized static boolean ticket()
{
if (max_value > 500)
{
return true;
}
try
{
Thread.sleep(10);
}catch(InterruptedException e)
{

}
System.out.println(Thread.currentThread().getName()+":method.."+max_value++);
return false;
}
public void change() throws InterruptedException
{
Thread.sleep(30);
this.flag = false;
}
}
public class bank3 {
public static void main(String[] args) throws InterruptedException {
TicketWindow3 tw3 = new TicketWindow3();
Thread t1 = new Thread(tw3);
Thread t2 = new Thread(tw3);
t1.start();
tw3.change();
t2.start();
}
}

      在该例子中,对临界区域进行了加锁,而TicketWindow3类中,通过了两种方式加锁,在测试这两种方式加锁的区域是同一个,能够实现在一个线程占用临界区域,其他线程不能再访问。第一种加锁方式,是通过synchronized锁住类,即所谓的this锁,而第二种方式是通过synchronized锁住一个类方法ticket(),单看这个程序,我们并不能一定可以确定第二种方式锁住类方法也是this锁,因为还有一种假设的解释,锁住类方法只是锁住该类中的一个部分,而第一种锁住this的范围更大,这两种方式是包含的关系,虽然最后结果的确实现了线程的同步,保证了最后结果的正确,但并不能说明问题。因此我对该程序进行了一个修改,使得类中具有两个类方法的的锁,ticket1()和ticket2(),而不加this锁。

class TicketWindow3 implements Runnable
{
private static int max_value = 0;
private boolean flag1 = true;
private boolean flag2 = true;
public void run()
{
if(flag1)
{
while(true)
{
synchronized(TicketWindow3.class)
{
if(max_value > 500)
{
break;
}
try
{
Thread.sleep(10);
}catch (InterruptedException e)
{

}
System.out.println(Thread.currentThread().getName()+":lock..."+max_value++);
}
}
}
else
{
if(flag2)
{
while(true)
{
if(ticket1())
break;
}
}
else
{
while(true)
{
if(ticket2())
break;
}
}
   }
}
private synchronized static boolean ticket1()
{
if (max_value > 500)
{
return true;
}
try
{
Thread.sleep(10);
}catch(InterruptedException e)
{

}
System.out.println(Thread.currentThread().getName()+":method.."+max_value++);
return false;
}
private synchronized static boolean ticket2()
{
if (max_value > 500)
{
return true;
}
try
{
Thread.sleep(10);
}catch(InterruptedException e)
{

}
System.out.println(Thread.currentThread().getName()+":method.."+max_value++);
return false;
}
public void change() throws InterruptedException
{
Thread.sleep(30);
if(flag1)
   flag1 = false;
else
flag2 = false;
}
}
public class bank3 {
public static void main(String[] args) throws InterruptedException {
TicketWindow3 tw3 = new TicketWindow3();
tw3.change();
Thread t1 = new Thread(tw3);
Thread t2 = new Thread(tw3);
Thread t3 = new Thread(tw3);
t1.start();
t2.start();
tw3.change();
t3.start();
}
}


从结果中我们可以看到,通过对两个不同的类方法加锁,依然可以保证线程的同步。这就说明上述的假设类方法是类中更小区域的锁,是不对的,类方法的锁也是this锁。

发布了28 篇原创文章 · 获赞 35 · 访问量 14万+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章