1.产生原因
临界资源:被多个线程同时访问的资源
如果有多个线程同时访问同一份资源,这个资源对应的值有可能会出现值不准确的情况。临界资源产生的原因:在多个线程访问同一份资源的时候,如果一个线程在取值的过程中,时间片又被其他线程抢走了,临界资源问题就产生了
此处给出一个卖票的例子:
1 | package Thread; |
运行结果:
此时的结果明显不是我们想要的, 因此,需要采用同步机制解决这种问题。
2.解决方法
1.加上synchronized关键字
synchronized关键字主要有两种用法(synchronized 方法和synchronized块),此外该关键字还可以作用于静态方法、类或某个实例,但这都对程序的效率有很大的影响。
1) synchronized 方法。
在方法的声明前加人synchronized关键字,示例如下:
1 | public synchronized void mutiThreadAccess( ); |
只要把多个线程对类需要被同步的资源的操作放到mutiThreadAccess( )方法中,就能保这个方法在同一时刻只能被一个线程访问, 从而保证了多线程访问的安全性。然而,当一个法的方法体规模非常大时,把该方法声明为synchronized会大大影响程序的执行效率。为了高程序的效率,Java 提供了synchronized块。
代码:
只修改实现Runnable接口的run方法
1 | public void run() { |
运行结果:
可以看到输出结果大致和我们所需相同但是多出了-1,-2,-3,这个是不满足我们的要求的
因为当你加了synchronized(“ “)后,线程看到锁后需要等待,所以当满足了TicketCenter.restCount > 0,还会有三个线程正在等待执行。只需再加一个判断条件即可。
全部代码:
1 | package Thread; |
2) synchronized 块。
synchronized 块既可以把任意的代码段声明为synchronized, 也可以定上锁的对象,有非常高的灵活性。其用法如下:
1 | synchronized( syncObject) { |
1 | package Thread; |
2. Lock
JDK5里新增一个Lock接口以及它的一个实现类ReentrantLock(重入锁),Lock也可以用来实现多线程的同步。
直接上代码:
1 | package Thread; |