黑马程序员——线程安全

            ------<a href="http://www.itheima.com" target="blank">Java培训、Android培训、iOS培训、.Net培训</a>、期待与您交流! -------

  线程安全问题在理想状态下,不容易出现,但一旦出现对软件的影响是非常大的。

  导致安全问题的出现的原因:

  1.线程随机性。

  2.多个线程访问出现延迟。

  那么如何解决线程安全问题呢?我们可以通过线程同步来解决这个问题。这就要涉及到同步代码块、同步方法方面的知识。

  同步代码块,指的是,在同一个时间内只能有一个线程操作这个代码块,只有这个线程执行完这个代码块后别的线程才可以执行该代码块。说到同步代码那么还要涉及到对象锁,每个对象都有一个对象锁,只要一个线程获得了对象的对象锁,那么另外的线程就不能再操作该对象了,直到拥有对象的对象锁的线程释放对象锁后,别的线程才可以有机会获得该对象锁从而对对象进行操作。其实同步代码块使用的就是这个原理,java里同过关键字synchronized即可获得对象锁。

  如下是同步代码块的例子:

package bokedemo;
 
public class SychronizedBlock {
 public void testSychronized()
 {
 //this是当前类的对象,通过synchronized(this)获得该对象的锁
 synchronized(this){
 System.out.println("tongbu");
 }
 }
}

  同步方法是指用synchronized修饰方法,然后让该方法同步起来,每次只让一个线程操作。要注意的是synchronized一定要放在方法返回类型之前,如下是一个简单例子:

package bokedemo;
 
public class SychronizedBlock {
//用synchronized修饰方法,然后让该方法同步起来,每次只让一个线程操作。
 public  synchronized void  testSychronized()
 {
 System.out.println("tongbufangfa");
 }
}

  另外,若要在静态方法中使用同步代码块,由于在static修饰的方法中不能使用this,但是我们可以用类.class来代替,如下所示:

package bokedemo;
 
public class SychronizedBlock {
 
 public  static void  testSychronized()
 {
    //这样同样可以
synchronized(SychronizedBlock.class){
}
 }
}

  另外jkd1.5后的另一种同步机制:

  通过显式定义同步锁对象来实现同步,这种机制,同步锁应该使用Lock对象充当。在实现线程安全控制中,通常使用ReentrantLock(可重入锁)。使用该对象可以显示地加锁和解锁。它具有与使用 synchronized 方法和语句所访问的隐式监视器锁相同的一些基本行为和语义,但功能更强大。

  下面是使用ReentrantLock的一个简单例子:

package bokedemo;
 
import java.util.concurrent.locks.ReentrantLock;
 
public class ReentrantLockDemo {
public void testReentrantLock()
{
ReentrantLock rlock=new ReentrantLock();
try{
//获得同步锁
rlock.lock();
}finally{
//释放同步锁
   rlock.unlock();
}
}
}

  回到线程安全问题,上一篇线程文章中,使用了一个例子,就是同时开放3个窗口买总数为50的票,当时是没有使用到同步代码的,这是不安全的。因为有可能会有两个或多个线程同时获得票号相同的票,这回造成严重的错误,实现中是绝对不允许的。

  修改前代码如下:

package bokedemo;
 
public class SaleTicket {
public static void main(String[] args) {
//创建一个Runnable实例
Runnable runnable=new MyRunnable();
//开启3个线程卖票
new Thread(runnable,"1窗口").start();
new Thread(runnable,"2窗口").start();
new Thread(runnable,"3窗口").start();
}
}
/**
 * 实现Runnable接口,覆写run方法。
 * @author Cam
 *
 */
class MyRunnable implements  Runnable
{
int sum=50;
 
@Override
public void run() {
while(sum!=0)
{
System.out.println(Thread.currentThread().getName()+"卖了第"+sum--+"票");
}
}
}

  修改后代码如下:

package bokedemo;
 
public class SaleTicket {
public static void main(String[] args) {
//创建一个Runnable实例
Runnable runnable=new MyRunnable();
//开启3个线程卖票
new Thread(runnable,"1窗口").start();
new Thread(runnable,"2窗口").start();
new Thread(runnable,"3窗口").start();
}
}
/**
 * 实现Runnable接口,覆写run方法。
 * @author Cam
 *
 */
class MyRunnable implements  Runnable
{
int sum=50;
 
@Override
public void run() {
while(true){
//同步代码
synchronized(this){
if(sum>0)
{
try {
Thread.currentThread().sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"卖了第"+(sum--)+"票");
}
if(sum==0)
break;
}
}
}
}


 

 

 

 


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值