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

本文通过银行服务窗口的实例,探讨了多线程编程中线程同步的重要性,并详细介绍了Java中synchronized同步锁的使用方法及其如何确保同一时间只有一个线程能访问临界区。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

      说到线程编程,我们初步的认识,就是通过编程的方式让系统资源在不同的线程中切换,从而实现系统高效的运行。然后这看似简单明了的问题,实际操作起来,却隐藏了很多复杂的细节,其中线程间的同步和通信问题,就是导致多线程编程出现各种异常的关键。举一个简单的例子,银行的有多个服务窗口,每一个服务窗口通过叫号提供服务。假设系统设计了一个变量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锁。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值