JAVA拾遗 - 线程安全之隐式锁

线程安全

当多个线程同时操作一个数据结构的时候,可能会发生一些奇妙的情况(比如相互串行或者相互修改),这种情况发生后就无法保证数据的一致性,这也是不安全的线程,为了保证数据的一致性,我们在这里讨论线程安全。

1.首先我们新建一个线程

package ThreadSecure;

public class Thread_A extends Thread{
    private work work; //这是工作

    public Thread_A(work work)
    {
        this.work = work;
    }
    @Override
    public void run()
    {
        work.add();

    }

}

这是一个不包含任何线程安全写法的线程,这在单例模式中是非常危险的。
这个线程主要实现一个功能就是运行一个“任务”,然后在任务结束后使得num获得++,

2.然后我们编辑工作内容

package ThreadSecure;

public class work {
    public int number = 0;
    public void add()
    {
        try {
            Thread.sleep(500);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        number = number + 1 ;
        System.out.println(Thread.currentThread().getName() + "-" + number);
    }

}

3.然后我们编辑主函数,使得主函数创建三个线程,用来执行work.add()这个功能,然后观察结果

package ThreadSecure;

public class ThreadMain {
    public static void main(String[] args)
    {
        work work = new work();
        for(int i = 0 ; i < 5 ; i++)
        {
        Thread_A p1 = new Thread_A(work);
        p1.start();
        }
        try {
            Thread.sleep(1001);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        System.out.println(work.number);
    }
}

4.输出结果

//第一次
Thread-0-1
Thread-4-2
Thread-1-3
Thread-3-3
Thread-2-2
3
//第二次
Thread-4-3
Thread-0-3
Thread-3-3
Thread-2-3
Thread-1-3
3

可以看到运行几次的结果都是不同的,能不能按照我们所想,用number累计做了多少次work.add()完全是凭借运气!
所以在这里使用JAVA的先进功能隐式锁修改代码

//关键字
synchronized(object);

隐式锁

synchronzied是JAVA的一个关键字,在用其进行修饰代码块或者方法的时候,可以保证同一时刻最多只有一个线程执行该代码段,从此访问需要讲究顺序,凭一个先来后到。

示例1:修饰于方法上,放在public等之后、返回类型之前

public synchronized void synMethod()
{
    //method
}

示例2:修饰于代码块上

public int synMethod(int al){
    synchronized (this){//synchronized(Object)
    }//一次只有一个线程可以进入
    //这里面的object可以为一个对象、参数本身、this,不建议对非对象使用
}

规则

  1. 当两个进程并发地访问一个对象的隐式锁同步代码块时,同一个时间内只能有一个代码块得到执行
  2. 当一个线程访问object的一个隐式锁同步代码块时,另外一个线程可以访问该对象中的非锁条件(所以不建议对非对象使用,因为容易混淆)
  3. 尤其关键的是当一个线程访问object中的synchronized(this)方法块时,其他线程对object中的所有其他synchronized(this)同步代码块的访问将会被阻塞
  4. 也就是说,当一个线程访问被隐式锁锁住的对象中的某个被锁的代码块,其他所有线程对这个对象中的被锁代码的访问都会被堵塞
  5. 以上规则适用于其他对象锁

1.利用隐式锁修改代码

了解了隐式锁后我们就尝试用隐式锁修改代码,以完成我们之前的目的

package ThreadSecure;

public class work {
    public int number = 0;
    public synchronized void  add()
    {
        try {
            Thread.sleep(500);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        number = number + 1 ;
        System.out.println(Thread.currentThread().getName() + "-" + number);
    }

}

可见我们在add方法上加入了一个隐式锁

2.运行结果

Thread-0-1
Thread-1-2
2
Thread-4-3
Thread-3-4
Thread-2-5

程序走到add处得以一步一步地执行

三种隐式锁的写法

1.锁方法体

class A{
    public synchronized void method()
    {
        //method
    }
}

2.锁this

class A{
    publicvoid method()
    {
        synchronized(this){
            //method
        }   
    }
}

3.锁小东西

class A{
    private byte [] lock = new byte[1];
    public  void method()
    {
        synchronized(lock){
        //method
        }
    }
}

三种方法的速度大约是3>2>1
因为锁和解锁一个对象是需要此对象的资源,所以锁的对象越小,那么效率也越高,与此同时,一个线程进入对象时调用资源也是需要一定时间的,在最关键的时候锁当然比把线程“关”在门外面速度更快一点。造一个一字节的byte对象最小,所以在工作中经常这样写。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值