Java线程间的资源互斥共享

互斥问题

在计算机中很多资源都是有限的,这种有限资源叫做临界资源。多个进程争抢同一个临界资源,抢到了可以运行,没抢到就无法运行。互斥就是争夺临界资源进程的间接制约关系。 例如多个打印线程争夺一台打印机资源,进程间就形成了互斥。

在一些同时运行的线程需要共享数据时,每个线程就必须要考虑与他一起共享数据的线程的状态与行为,否则的话就不能保证共享数据的一致性,从而也就不能保证程序的正确性

我们观察以下Share类

public class Share{
    int index = 0;
    char[] data = new char[6];
    public void push(char c)
    {
        data[index] = c;
        index++;
    }
    public char pop()
    {
        index--;
        return data[index];
    }
}

当有同时有两个线程A和B使用了Share类的一个实例时,在某一时刻,A要往堆栈里push数据,而B则要从堆栈中pop数据:

在该事件发生前,堆栈中有两个字符

data = | a | c |   |   |   |   |     index = 2

A执行到push中的第一条语句data[index] = ‘r’ 时

data = | a | c | r |   |   |   |     index = 2

A还没有执行index++语句,A被B中断,B执行pop()方法,返回了 ‘c’

data = | a | c | r |   |   |   |     index = 1

随后A继续执行index++语句

data = | a | c | r |   |   |   |     index = 2

最后的结果是A再次往堆栈里push数据的时候,r会被覆盖,相当于r最终也没有被添加到堆栈中去

产生这种问题的原因是对共享资源访问的不完整。

解决方法

为了解决这种问题,需要寻找一种机制来保证对共享数据操作的完整性,这种完整性称为共享数据操作的同步,共享数据叫做条件变量

在Java语言中,引入了对象互斥锁的概念(又称为监视器、管程)来实现不同线程对共享数据操作的同步。 “对象互斥锁”阻止多个线程同时访问同一个条件变量

在Java语言中,有两种方法可以实现“对象互斥锁”:      

(1)用关键字volatile来声明一个共享数据(变量)      

(2)用关键字synchronized来声明一个操作共享数据的方法一段代码

一般情况下,都使用synchronized关键字在方法的层次上实现对共享资源操作的同步,很少使用volatile关键字声明共享变量

每个 synchronized 方法都必须获得调用该方法的类实例的锁方能执行,否则所属线程阻塞,方法一旦执行,就独占该锁,直到从该方法返回时才将锁释放,此后被阻塞的线程方能获得该锁,重新进入可执行状态。如果一个程序内有两个或以上的方法使用synchronized标志,则它们在同一个“对象互斥锁”管理之下

如以下Share类,添加了互斥锁后,就不会出现之前的状况了

public class Share{
    int index = 0;
    char[] data = new char[6];
    public synchronized void push(char c)
    {
        data[index] = c;
        index++;
    }
    public synchronized char pop()
    {
        index--;
        return data[index];
    }
}

猜你喜欢

转载自blog.csdn.net/BYZY1314/article/details/127833805