精华内容
下载资源
问答
  • 主要介绍了Java多线程编程之Lock用法实例,本文直接给出代码实例,需要的朋友可以参考下
  • java多线程Lock使用

    千次阅读 2017-05-06 00:44:26
    在Java多线程中,可以使用synchronized关键字来实现线程之间的同步互斥,也就是上锁。在JDK1.5之中,新增了ReentrantLock类也能够达到相同的效果。并且在扩展功能上更强、更加灵活。LockLock是一个接口,也就是...

    在Java多线程中,可以使用synchronized关键字来实现线程之间的同步互斥,也就是上锁。在JDK1.5之中,新增了ReentrantLock类也能够达到相同的效果。并且在扩展功能上更强、更加灵活。


    Lock。

    Lock是一个接口,也就是ReentrantLock以及ReentrantReadWriteLock.ReadLock、ReentrantReadWriteLock.WriteLock的的接口。
    这里写图片描述

    Lock接口的三个实现类能够实现锁的一些操作,下面是官方的一些介绍:
    这里写图片描述

    以及其基本的使用方法:

    Lock l = ...;
     l.lock();
     try {
       // access the resource protected by this lock
     } finally {
       l.unlock();
     }

    并且Lock接口中有几个方法可供使用:
    这里写图片描述


    ReentrantLock。

    使用ReentrantLock可以进行上锁操,保证了部分的线程安全以及防止出现脏读。

    package testThread;
    
    import java.util.concurrent.locks.Lock;
    import java.util.concurrent.locks.ReentrantLock;
    
    class ThreadA extends Thread {
        private TestReentrantLock testReentrantLock;
    
        public ThreadA(TestReentrantLock testReentrantLock) {
            // TODO Auto-generated constructor stub
            this.testReentrantLock = testReentrantLock;
        }
    
        @Override
        public void run() {
            // TODO Auto-generated method stub
            testReentrantLock.testA();
        }
    
    }
    
    
    class TestReentrantLock {
        private Lock lock = new ReentrantLock();//得到ReentrantLock对象
    
        public void testA() {
            try {
                //lock.lock();//进行上锁
                System.out.println("方法A拿到了锁!");
                System.out.println("---begin----这里是方法A,当前线程的名字为:" + Thread.currentThread().getName());;
                System.out.println("进入方法A的时间为:" + System.currentTimeMillis());
                Thread.sleep(3000);//当前线程睡眠3秒
                System.out.println("---end-----退出方法A的时间为:" + System.currentTimeMillis());
            } catch (Exception e) {
                // TODO: handle exception
                e.printStackTrace();
            } finally {
                //lock.unlock();//释放锁
                //System.out.println("方法A释放了锁!");
            }
        }
    
    }
    
    public class ReentrantLockTest {
    
        public static void main(String[] args) {
            TestReentrantLock testReentrantLock = new TestReentrantLock();
            ThreadA a1 = new ThreadA(testReentrantLock);
            a1.setName("A1");
            a1.start();
    
            ThreadA a2 = new ThreadA(testReentrantLock);
            a2.setName("A2");
            a2.start();
    
            ThreadA a3 = new ThreadA(testReentrantLock);
            a3.setName("A3");
            a3.start();
    
        }
    
    }
    

    这里将上锁和释放锁的代码都给注释掉了,输出结果如下:

    这里写图片描述

    可以看到现在线程随机调用方法A。

    当去掉注释之后,也就是进行上锁操作,输出结果如下:

    这里写图片描述

    如果有多个锁的话,必须都要释放掉才能被其他线程所使用:

    这里写图片描述

    这里新添加了一个锁,不进行释放,那么其他的线程将永远无法得到执行的机会,输出结果如下:

    这里写图片描述

    因为lock2没有释放锁,所以一直线程一直在等待。


    ReentrantReadWriteLock。

    类ReentrantLock可以完全的互斥排他,这也就说明了同一个时间内只能有一个线程可以执行ReentrantLock.lock之后的内容。这样虽然保证了线程的安全性。但是如果有时候想要在上锁的时候进行其他的读取操作就不可以了,这时候ReentrantReadWriteLock这个类就可以帮助实现读取的操作。

    读写锁顾名思义有两种锁,一种是读锁,一种是写锁。读锁也称为共享锁,在一个线程进入读锁的时候,即使不进行释放锁,其他的线程也可以进入读锁的内容中进行操作。写锁也成为排他锁,也就是互斥的,当一个线程上了写锁之后,其他的线程就不能进行操作了。

    如果读个线程都是用读锁的话:

    package testThread;
    
    import java.util.concurrent.locks.ReentrantReadWriteLock;
    
    class ReentrantReadWrite {
        private ReentrantReadWriteLock.ReadLock readLock = new ReentrantReadWriteLock().readLock();
    
        public void read() throws InterruptedException {
            try {
                readLock.lock();//上读锁
                System.out.println("获取了读锁!,当前线程名称为:" + Thread.currentThread().getName());
                System.out.println("现在的时间为:" + System.currentTimeMillis());
                Thread.sleep(5000);
            } finally {
                readLock.unlock();//释放读锁
            }
        }
    }
    
    class ThreadB extends Thread {
    
        private ReentrantReadWrite readWrite;
    
        public ThreadB(ReentrantReadWrite readWrite) {
            this.readWrite = readWrite;
        }
        @Override
        public void run() {
            try {
                this.readWrite.read();
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }
    
    
    public class ReentrantReadWriteLockTest {
    
        public static void main(String[] args) {
            ReentrantReadWrite readWrite = new ReentrantReadWrite();
    
            ThreadB b1 = new ThreadB(readWrite);
            b1.setName("b1");
            ThreadB b2 = new ThreadB(readWrite);
            b2.setName("b2");
            ThreadB b3 = new ThreadB(readWrite);
            b3.setName("b3");
            b1.start();
            b2.start();
            b3.start();
        }
    
    }
    

    其他的线程也都可以进行操作,输出结果:

    这里写图片描述

    但如果将其换成写锁的话:

    这里写图片描述

    其输出结果又成同步的了,也就是必须需要等待锁的释放:

    这里写图片描述

    除了读读锁可以共享之外,其他的写写、读写、写读都不能够实现共享操作,均需要等待锁的释放然后争抢到锁之后进行操作。


    Condition。

    关键字synchronized与wait和notify/notifyAll方法结合可以实现等待/通知模式,ReentrantLock类也可以实现相同的功能,但是需要借助Condition对象。也就是通过Lock中定义的newCondition方法得到的Condition对象。而且使用Condition更加灵活,可以实现多路通知功能,也就是在一个Lock对象里面创建多个Condition实例,线程对象可以注册在指定的Condition中,从而有选择性的进行线程通知。

    ···

    import java.util.concurrent.locks.Condition;
    import java.util.concurrent.locks.Lock;
    import java.util.concurrent.locks.ReentrantLock;
    
    class MyService {
    private Lock lock = new ReentrantLock();
    private Condition conditionA = lock.newCondition();
    private Condition conditionB = lock.newCondition();
    
    public void awaitA() {
        try {
            lock.lock();//在使用Condition前必须调用
            System.out.println("Condition调用await");
            conditionA.await();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
    
    public void awaitB() {
        try {
            lock.lock();//在使用Condition前必须调用
            System.out.println("Condition调用await");
            conditionA.await();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
    
    public void signalAll_A() {
        try {
            lock.lock();
            conditionA.signalAll();
            System.out.println("Condition调用signalAll");
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
    
    public void signalAll_B() {
        try {
            lock.lock();
            conditionA.signalAll();
            System.out.println("Condition调用signalAll");
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
    
    }
    
    class ThreadC extends Thread {
    private MyService myService;
    
    public ThreadC(MyService myService) {
        this.myService = myService;
    }
    
    @Override
    public void run() {
        myService.awaitA();
    }
    }
    
    class ThreadD extends Thread {
    private MyService myService;
    
    public ThreadD(MyService myService) {
        this.myService = myService;
    }
    
    @Override
    public void run() {
        myService.awaitB();
    }
    }
    
    
    public class ConditionTest {
    
    public static void main(String[] args) {
        MyService myService = new MyService();
        ThreadC c = new ThreadC(myService);
        ThreadD d = new ThreadD(myService);
        c.start();
        d.start();
        myService.signalAll_A();
    }
    
    }
    

    ···

    输出:

    这里写图片描述

    这里两个线程都进行了await操作,然后其中一个线程执行了signalAll操作所以该线程结束了等待,但是另一组线程由于没有进行通知所以还在等待中,Condition的等待通知模式十分灵活。


    公平锁与非公平锁。

    可以在创建ReentrantLock的时候传入参数:true或者false。如果是true则锁定为公平锁,公平锁也就是表示线程获取锁的顺序是按照线程加锁的顺序来分配的,相反如果是false,这就是非公平锁,也就是随机获取锁的。说白了就是哪个线程先start,然后哪个线程就先拿到锁,这个就是公平锁。反之,即是哪个线程先start,它并不一定能够先拿到锁,这就是非公平锁。

    公平锁/非公平锁测试:

    package testThread;
    
    import java.util.concurrent.locks.ReentrantLock;
    
    class MyFairService {
         private ReentrantLock lock ;  
    
            public MyFairService(boolean isFair) {  
                lock = new ReentrantLock(isFair);  
            }  
    
            public void serviceMethod() {  
                try {  
                    lock.lock();  
                    System.out.println("ThreadName=" + Thread.currentThread().getName()  
                            + " 获得锁定");  
                } finally {  
                    lock.unlock();  
                }  
            }  
    }
    
    
    
    public class FairLock {
    
        public static void main(String[] args) {
            final MyFairService service = new MyFairService(true);  //改为false就为非公平锁了  
            Runnable runnable = new Runnable() {  
                public void run() {  
                    System.out.println("**线程: " + Thread.currentThread().getName()  
                            +  " 运行了 " );  
                    service.serviceMethod();  
                }  
            };  
    
            Thread[] threadArray = new Thread[10];  
    
            for (int i=0; i<10; i++) {  
                threadArray[i] = new Thread(runnable);  
            }  
            for (int i=0; i<10; i++) {  
                threadArray[i].start();  
            }  
        }  
    
    }
    
    

    运行结果:

    这里写图片描述

    线程的运行是随机的,但是线程获得锁的顺序是与运行顺序一致的。

    如果修改为非公平锁,也就是ReentrantLock参数传入false。
    运行结果为:
    这里写图片描述

    此时线程的运行顺序和获得锁的顺序是不一致的。

    如果不传入参数,默认为非公平锁。可以使用isFair()方法测试该锁定是否为公平锁。


    其他方法。

    Lock的newCondition方法上述已经介绍。
    还有一些其他的方法:

    • int getHoldCount():查询当前线程保持此锁定的个数,也就是调用lock()方法的次数。

    • int getQueueLength():返回正在等待获取此锁定的线程的估计数,比如有5个线程,1个线程首先调用lock()方法,那么在调用getQueueLength()方法返回值就是4,说明4个线程正在等待lock 的释放。

    • int getWaitQueueLength(Condition condition):返回等待与此锁定相关的给定条件Condition的线程估计数,比如有5个线程,每个线程都执行了同一个Condition的await方法,那么调用getWaitQueueLength之后返回的值就是5.

    • boolean hasQueuedThread(Thread thread):查询指定线程是否正在等待获取此锁定。

    • boolean hasQueuedThreads():查询是否有线程正在等待获取此锁定。

    • boolean hasWaiters(Condition condition):查询是否有线程正在等待与此锁定有关的Condition条件,比如有5个线程调用了Condition的await方法,那么调用该方法也就是反悔了true。

    • boolean isHeodByCurrentThread():查询当前线程是否保持此锁定。lock()方法调用之前是false,调用之后是true。

    • boolean isLocked():是否此锁定有任意线程保持。

    • void lockInterruptibly():如果当前线程未被中断,则获取此锁定,如果已经被中断则抛出异常。

    • boolean tryLock():仅在调用时锁定未被另一个线程保持的情况下,才会获取该锁定。也就是说如果有线程调用了lock方法,并且还没有释放,那么该方法返回false。否则如果释放了则返回true。

    • boolean tryLock(long timeout,TimtUnit unit):在给定的时间内,该锁定没有被另一个线程保持,并且当线程未被中断,则获取该锁定。

    • awaitUntil(Date date):Condition调用该方法后,到达传入的时间后自动唤醒,其中如果有其他唤醒操作那么立即唤醒。

    展开全文
  • 线程是操作系统分配处理器时间的基本单元,在进程中可以有线程同时执行代码。进程之间是相对独立的,一个进程无法访问另一个进程的数据(除非利用分布式计算方式),一个进程运行的失败也不会影响其他进程的运行...

    进程(Process)是Windows系统中的一个基本概念,它包含着一个运行程序所需要的资源。一个正在运行的应用程序在操作系统中被视为一个进程,进程可以包括一个或多个线程。线程是操作系统分配处理器时间的基本单元,在进程中可以有多个线程同时执行代码。进程之间是相对独立的,一个进程无法访问另一个进程的数据(除非利用分布式计算方式),一个进程运行的失败也不会影响其他进程的运行,Windows系统就是利用进程把工作划分为多个独立的区域的。进程可以理解为一个程序的基本边界。是应用程序的一个运行例程,是应用程序的一次动态执行过程。

    线程(Thread)是进程中的基本执行单元,是操作系统分配CPU时间的基本单位,一个进程可以包含若干个线程,在进程入口执行的第一个线程被视为这个进程的主线程。在.NET应用程序中,都是以Main()方法作为入口的,当调用此方法时系统就会自动创建一个主线程。线程主要是由CPU寄存器、调用栈和线程本地存储器(Thread Local Storage,TLS)组成的。CPU寄存器主要记录当前所执行线程的状态,调用栈主要用于维护线程所调用到的内存与数据,TLS主要用于存放线程的状态信息。

    二、多线程

    多线程的优点:可以同时完成多个任务;可以使程序的响应速度更快;可以让占用大量处理时间的任务或当前没有进行处理的任务定期将处理时间让给别的任务;可以随时停止任务;可以设置每个任务的优先级以优化程序性能。

    为什么可以多线程执行呢?总结起来有下面两方面的原因:

    1、CPU运行速度太快,硬件处理速度跟不上,所以操作系统进行分时间片管理。这样,从宏观角度来说是多线程并发的,因为CPU速度太快,察觉不到,看起来是同一时刻执行了不同的操作。但是从微观角度来讲,同一时刻只能有一个线程在处理。

    2、目前电脑都是多核多CPU的,一个CPU在同一时刻只能运行一个线程,但是多个CPU在同一时刻就可以运行多个线程

    C#程序具有一个线程,此线程执行程序中以Main方法开始和结束的代码,Main()方法直接或间接执行的每一个命令都有默认线程(主线程)执行,当Main()方法返回时此线程也将终止。

    一个进程可以创建一个或多个线程以执行与该进程关联的部分程序代码。在C#中,线程是使用Thread类处理的,该类在System.Threading命名空间中。使用Thread类创建线程时,只需要提供线程入口,线程入口告诉程序让这个线程做什么。通过实例化一个Thread类的对象就可以创建一个线程。创建新的Thread对象时,将创建新的托管线程。Thread类接收一个ThreadStart委托或ParameterizedThreadStart委托的构造函数,该委托包装了调用Start方法时由新线程调用的方法,示例代码如下:

    Thread thread=new Thread(new ThreadStart(method));//创建线程

    thread.Start();                                                           //启动线程

     

    一、Lock定义

        lock 关键字可以用来确保代码块完成运行,而不会被其他线程中断。它可以把一段代码定义为互斥段(critical section),互斥段在一个时刻内只允许一个线程进入执行,而其他线程必须等待。这是通过在代码块运行期间为给定对象获取互斥锁来实现的。

    在多线程中,每个线程都有自己的资源,但是代码区是共享的,即每个线程都可以执行相同的函数。这可能带来的问题就是几个线程同时执行一个函数,导致数据的混乱,产生不可预料的结果,因此我们必须避免这种情况的发生。

    在C# lock关键字定义如下:

        lock(expression) statement_block,其中expression代表你希望跟踪的对象,通常是对象引用。

        如果你想保护一个类的实例,一般地,你可以使用this;如果你想保护一个静态变量(如互斥代码段在一个静态方法内部),一般使用类名就可以了。

    而statement_block就是互斥段的代码,这段代码在一个时刻内只可能被一个线程执行。

    二、简单解释一下执行过程

    先来看看执行过程,代码示例如下:

            private static object  ojb = new object();

            lock(obj)

            {

                     //锁定运行的代码段

            }   假设线程A先执行,线程B稍微慢一点。线程A执行到lock语句,判断obj是否已申请了互斥锁,判断依据是逐个与已存在的锁进行object.ReferenceEquals比较(此处未加证实),如果不存在,则申请一个新的互斥锁,这时线程A进入lock里面了。

    这时假设线程B启动了,而线程A还未执行完lock里面的代码。线程B执行到lock语句,检查到obj已经申请了互斥锁,于是等待;直到线程A执行完毕,释放互斥锁,线程B才能申请新的互斥锁并执行lock里面的代码。

     

    三、注意事项

    . lock(this)请不要使用,this指的是整个外层方法,当锁住后导致别的进程也无法访问该方法,应lock一个不影响其他操作的私有对象

     

     

    展开全文
  • 多线程中,为了防止多个线程对一个变量赋值的更改或者一段代码段的逻辑的修改,需要对这个赋值动作或者代码段加锁。 private AutoResetEvent _autoSetEvent = new AutoResetEvent(false); 在启动一个线程后但是...

    在多线程中,为了防止多个线程对一个变量赋值的更改或者一段代码段的逻辑的修改,需要对这个赋值动作或者代码段加锁。

    private AutoResetEvent _autoSetEvent = new AutoResetEvent(false);

    在启动一个线程后但是需等待时:

                    if (!isStart)
                    {
                        Logger.Info("侦听线程进入等待!");
                        _autoSetEvent.WaitOne();
                        Logger.Info("侦听线程再次运行!");
                    }

    需要启动时:

    _autoSetEvent.Set();

    private object _startLock = new object();               

    lock (_startLock)
     {
              isStart = _boolStarted;
     }

    关于lock对一个对象加锁要注意,可以在递归中使用,lock只会生效于多线程,对于单线程,自身已经是锁的所有者,并不会出现为了继续加锁等待原有锁释放,递归中不会发生锁死。

    展开全文
  • 一、Lock定义 lock 关键字可以用来确保代码块完成运行,而不会被其他线程中断。... 在多线程中,每个线程都有自己的资源,但是代码区是共享的,即每个线程都可以执行相同的函数。这可能带来的问题就是几...

    一、Lock定义

        lock 关键字可以用来确保代码块完成运行,而不会被其他线程中断。它可以把一段代码定义为互斥段(critical section),互斥段在一个时刻内只允许一个线程进入执行,而其他线程必须等待。这是通过在代码块运行期间为给定对象获取互斥锁来实现的。

         在多线程中,每个线程都有自己的资源,但是代码区是共享的,即每个线程都可以执行相同的函数。这可能带来的问题就是几个线程同时执行一个函数,导致数据的混乱,产生不可预料的结果,因此我们必须避免这种情况的发生。

        而在.NET中最好了解一下进程、应用域和线程的概念,因为Lock是针对线程一级的,而在.NET中应用域是否会对Lock起隔离作用,我的猜想是,即不在同一应用域中的线程无法通过Lock来中断;另外也最好能了解一下数据段、代码段、堆、栈等概念。

        在C# lock关键字定义如下:

        lock(expression) statement_block,其中expression代表你希望跟踪的对象,通常是对象引用。

        如果你想保护一个类的实例,一般地,你可以使用this;如果你想保护一个静态变量(如互斥代码段在一个静态方法内部),一般使用类名就可以了。

    而statement_block就是互斥段的代码,这段代码在一个时刻内只可能被一个线程执行。

    二、简单解释一下执行过程

    先来看看执行过程,代码示例如下:      

      private static object  ojb = new object(); 
            lock(obj) 
            { 
                     //锁定运行的代码段 
            }   

    假设线程A先执行,线程B稍微慢一点。线程A执行到lock语句,判断obj是否已申请了互斥锁,判断依据是逐个与已存在的锁进行object.ReferenceEquals比较(此处未加证实),如果不存在,则申请一个新的互斥锁,这时线程A进入lock里面了。

    这时假设线程B启动了,而线程A还未执行完lock里面的代码。线程B执行到lock语句,检查到obj已经申请了互斥锁,于是等待;直到线程A执行完毕,释放互斥锁,线程B才能申请新的互斥锁并执行lock里面的代码。

    三、Lock的对象选择问题

        接下来说一些lock应该锁定什么对象。

        1、为什么不能lock值类型

        比如lock(1)呢?lock本质上Monitor.Enter,Monitor.Enter会使值类型装箱,每次lock的是装箱后的对象。lock其实是类似编译器的语法糖,因此编译器直接限制住不能lock值类型。退一万步说,就算能编译器允许你lock(1),但是object.ReferenceEquals(1,1)始终返回false(因为每次装箱后都是不同对象),也就是说每次都会判断成未申请互斥锁,这样在同一时间,别的线程照样能够访问里面的代码,达不到同步的效果。同理lock((object)1)也不行。

        2、Lock字符串

        那么lock("xxx")字符串呢?MSDN上的原话是:

    锁定字符串尤其危险,因为字符串被公共语言运行库 (CLR)“暂留”。 这意味着整个程序中任何给定字符串都只有一个实例,就是这同一个对象表示了所有运行的应用程序域的所有线程中的该文本。因此,只要在应用程序进程中的任何位置处具有相同内容的字符串上放置了锁,就将锁定应用程序中该字符串的所有实例。

        3、MSDN推荐的Lock对象

        通常,最好避免锁定 public 类型或锁定不受应用程序控制的对象实例。例如,如果该实例可以被公开访问,则 lock(this) 可能会有问题,因为不受控制的代码也可能会锁定该对象。这可能导致死锁,即两个或更多个线程等待释放同一对象。出于同样的原因,锁定公共数据类型(相比于对象)也可能导致问题。

        而且lock(this)只对当前对象有效,如果多个对象之间就达不到同步的效果。

        而自定义类推荐用私有的只读静态对象,比如:

    private static readonly object obj = new object();

    为什么要设置成只读的呢?这时因为如果在lock代码段中改变obj的值,其它线程就畅通无阻了,因为互斥锁的对象变了,object.ReferenceEquals必然返回false。

    4、lock(typeof(Class))

        与锁定字符串一样,范围太广了。

    五、特殊问题:Lock(this)等的详细解释

        在以前编程中遇到lock问题总是使用lock(this)一锁了之,出问题后翻看MSDN突然发现下面几行字:通常,应避免锁定 public 类型,否则实例将超出代码的控制范围。常见的结构 lock (this)、lock (typeof (MyType)) 和 lock ("myLock") 违反此准则:如果实例可以被公共访问,将出现C# lock this问题。如果 MyType 可以被公共访问,将出现 lock (typeof (MyType)) 问题。由于进程中使用同一字符串的任何其他代码将共享同一个锁,所以出现 lock(“myLock”) 问题。

        来看看C# lock this问题:如果有一个类Class1,该类有一个方法用lock(this)来实现互斥:

    publicvoidMethod2()
    {
    lock(this)
    {
    System.Windows.Forms.MessageBox.Show("Method2End");
    }
    }

    如果在同一个Class1的实例中,该Method2能够互斥的执行。但是如果是2个Class1的实例分别来执行Method2,是没有互斥效果的。因为这里的lock,只是对当前的实例对象进行了加锁。

    Lock(typeof(MyType))锁定住的对象范围更为广泛,由于一个类的所有实例都只有一个类型对象(该对象是typeof的返回结果),锁定它,就锁定了该对象的所有实例,微软现在建议,不要使用lock(typeof(MyType)),因为锁定类型对象是个很缓慢的过程,并且类中的其他线程、甚至在同一个应用程序域中运行的其他程序都可以访问该类型对象,因此,它们就有可能代替您锁定类型对象,完全阻止您的执行,从而导致你自己的代码的挂起。

    锁住一个字符串更为神奇,只要字符串内容相同,就能引起程序挂起。原因是在.NET中,字符串会被暂时存放,如果两个变量的字符串内容相同的话,.NET会把暂存的字符串对象分配给该变量。所以如果有两个地方都在使用lock(“my lock”)的话,它们实际锁住的是同一个对象。到此,微软给出了个lock的建议用法:锁定一个私有的static 成员变量。

    .NET在一些集合类中(比如ArrayList,HashTable,Queue,Stack)已经提供了一个供lock使用的对象SyncRoot,用Reflector工具查看了SyncRoot属性的代码,在Array中,该属性只有一句话:return this,这样和lock array的当前实例是一样的。ArrayList中的SyncRoot有所不同

    get
    {
    if(this._syncRoot==null)
    {
    Interlocked.CompareExchange(refthis._syncRoot,newobject(),null);
    }
    returnthis._syncRoot;

    其中Interlocked类是专门为多个线程共享的变量提供原子操作(如果你想锁定的对象是基本数据类型,那么请使用这个类),CompareExchange方法将当前syncRoot和null做比较,如果相等,就替换成new object(),这样做是为了保证多个线程在使用syncRoot时是线程安全的。集合类中还有一个方法是和同步相关的:Synchronized,该方法返回一个对应的集合类的wrapper类,该类是线程安全的,因为他的大部分方法都用lock来进行了同步处理,比如Add方法:

    publicoverridevoidAdd(objectkey,objectvalue)
    {
    lock(this._table.SyncRoot)
    {
    this._table.Add(key,value);
    }
    }

    这里要特别注意的是MSDN提到:从头到尾对一个集合进行枚举本质上并不是一个线程安全的过程。即使一个集合已进行同步,其他线程仍可以修改该集合,这将导致枚举数引发异常。若要在枚举过程中保证线程安全,可以在整个枚举过程中锁定集合:

    QueuemyCollection=newQueue();
    lock(myCollection.SyncRoot){
    foreach(ObjectiteminmyCollection){
    //Insertyourcodehere.
    }
    }

    最后

        注意:应避免锁定 public 类型,否则实例将超出代码的控制范围。常见的结构 lock (this)、lock (typeof (MyType)) 和 lock ("myLock") 违反此准则:     1)如果实例可以被公共访问,将出现 lock (this) 问题;     2)如果 MyType 可以被公共访问,将出现 lock (typeof (MyType)) 问题;     3)由于进程中使用同一字符串的任何其他代码将共享同一个锁,所以出现 lock("myLock") 问题;     最佳做法是定义 private 对象来锁定, 或 private static 对象变量来保护所有实例所共有的数据。

    六、参考资料

        由于参考的资料都保存在本地,只能先列出标题,无法提供原文地址,深表歉意!

        1)描述C#多线程中Lock关键字

        2)解决C# lock this问题

        3)基于C#中的lock关键字的总结

        4)C# lock关键字  

    关于lock网上说法一大堆,但是关于实际用法的实例还是比较多的,但是多而不精,没说的很透彻,但是这个例子是对多线程中使用lock关键字是一个相当好的实例。很郁闷现在网上找到像样的文章都没有了,抄来抄去!!又不注明网址,还当自己的是原创!找个例子都找不到,还不如自己来~

    下面引入lock关键字的理论:

    在应用程序中使用多个线程的一个好处是每个线程都可以异步执行。对于 Windows 应用程序,耗时的任务可以在后台执行,而使应用程序窗口和控件保持响应。对于服务器应用程序,多线程处理提供了用不同线程处理每个传入请求的能力。否则,在完全满足前一个请求之前,将无法处理每个新请求。 然而,线程的异步特性意味着必须协调对资源(如文件句柄、网络连接和内存)的访问。否则,两个或更多的线程可能在同一时间访问相同的资源,而每个线程都不知道其他线程的操作。结果将产生不可预知的数据损坏。 对于整数数据类型的简单操作,可以用 Interlocked 类的成员来实现线程同步。对于其他所有数据类型和非线程安全的资源,只有使用本主题中的结构才能安全地执行多线程处理。

       lock 关键字可以用来确保代码块完成运行,而不会被其他线程中断。这是通过在代码块运行期间为给定对象获取互斥锁来实现的。

       提供给 lock 关键字的参数必须为基于引用类型的对象,该对象用来定义锁的范围。在上例中,锁的范围限定为此函数,因为函数外不存在任何对该对象的引用。如果确实存在此类引用,锁的范围将扩展到该对象。严格地说,提供给 lock 的对象只是用来唯一地标识由多个线程共享的资源,所以它可以是任意类实例。然而,实际上,此对象通常表示需要进行线程同步的资源。例如,如果一个容器对象将被多个线程使用,则可以将该容器传递给 lock,而 lock 后面的同步代码块将访问该容器。只要其他线程在访问该容器前先锁定该容器,则对该对象的访问将是安全同步的。

    通常,最好避免锁定 public 类型或锁定不受应用程序控制的对象实例。例如,如果该实例可以被公开访问,则 lock(this) 可能会有问题,因为不受控制的代码也可能会锁定该对象。这可能导致死锁,即两个或更多个线程等待释放同一对象。出于同样的原因,锁定公共数据类型(相比于对象)也可能导致问题。锁定字符串尤其危险,因为字符串被公共语言运行库 (CLR)“暂留”。 这意味着整个程序中任何给定字符串都只有一个实例,就是这同一个对象表示了所有运行的应用程序域的所有线程中的该文本。因此,只要在应用程序进程中的任何位置处具有相同内容的字符串上放置了锁,就将锁定应用程序中该字符串的所有实例。因此,最好锁定不会被暂留的私有或受保护成员。某些类提供专门用于锁定的成员。例如,Array 类型提供 SyncRoot。许多集合类型也提供 SyncRoot。

    下面有注释,有一定线程基础都是可以看懂的。【VS2008 .NET3.5】

    /*
    该实例是一个线程中lock用法的经典实例,使得到的balance不会为负数
    同时初始化十个线程,启动十个,但由于加锁,能够启动调用WithDraw方法的可能只能是其中几个
    作者:http://hi.baidu.com/jiang_yy_jiang
    */
    using System;
    
    namespace ThreadTest29
    {
        class Account
        {
            private Object thisLock = new object();
            int balance;
            Random r = new Random();
    
            public Account(int initial)
            {
                balance = initial;
            }
    
            int WithDraw(int amount)
            {
                if (balance < 0)
                {
                    throw new Exception("负的Balance.");
                }
                //确保只有一个线程使用资源,一个进入临界状态,使用对象互斥锁,10个启动了的线程不能全部执行该方法
                lock (thisLock)
                {
                    if (balance >= amount)
                    {
                        Console.WriteLine("----------------------------:" + System.Threading.Thread.CurrentThread.Name + "---------------");
                        
                        Console.WriteLine("调用Withdrawal之前的Balance:" + balance);
                        Console.WriteLine("把Amount输入 Withdrawal     :-" + amount);
                        //如果没有加对象互斥锁,则可能10个线程都执行下面的减法,加减法所耗时间片段非常小,可能多个线程同时执行,出现负数。
                        balance = balance - amount;
                        Console.WriteLine("调用Withdrawal之后的Balance :" + balance);
                        return amount;
                    }
                    else
                    {
                        //最终结果
                        return 0;
                    }
                }
            }
            public void DoTransactions()
            {
                for (int i = 0; i < 100; i++)
                {
                    //生成balance的被减数amount的随机数
                    WithDraw(r.Next(1, 100));
                }
            }
        }
    
        class Test
        {
            static void Main(string[] args)
            {
                //初始化10个线程
                System.Threading.Thread[] threads = new System.Threading.Thread[10];
                //把balance初始化设定为1000
                Account acc = new Account(1000);
                for (int i = 0; i < 10; i++)
                {
                    System.Threading.Thread t = new System.Threading.Thread(new System.Threading.ThreadStart(acc.DoTransactions));
                    threads[i] = t;
                    threads[i].Name = "Thread" + i.ToString();
                }
                for (int i = 0; i < 10; i++)
                {
                    threads[i].Start();
                }
                Console.ReadKey();
            }
        }
    }

    效果如下:

    展开全文
  • 在存取的过程中 可能 A线程取了100 而B线程那边还看见账户上没少掉那100快,所以导致数据不统一,赋值出现问题。 下面代码则可以测试出加上Lock锁定 与 不加的区别。   先上两个图。 第一个(加了Lock...
  • 一、Lock定义  lock 关键字可以用来确保代码块完成运行,而不会被其他线程中断。... 在多线程中,每个线程都有自己的资源,但是代码区是共享的,即每个线程都可以执行相同的函数。这可能带来的问
  • 本文实例讲述了python多线程threading.Lock锁的用法实例,分享给大家供大家参考。具体分析如下: python的锁可以独立提取出来 复制代码 代码如下:mutex = threading.Lock() #锁的使用 #创建锁 mutex = threading....
  • 本篇文章主要是对c#多线程Lock()关键字的用法进行了详细的总结介绍,需要的朋友可以过来参考下,希望对大家有所帮助
  • 多线程lock用法使用细节的经典实例 一、Lock定义  lock 关键字可以用来确保代码块完成运行,而不会被其他线程中断。它可以把一段代码定义为互斥段(critical section),互斥段在一个时刻内只允许一个线程...
  • 多线程Lock

    2020-06-07 21:27:38
    java.util.concurrent.locks.Lock 接口时控制线程对共享资源进行访问的工具。锁提供了对共享资源的独占访问。每次只能有一个线程Lock 对象加锁,线程开始访问共享资源前都应先获得Lock对象。 ReentrantLock ...
  • lock 多线程中的用法

    2012-03-06 21:44:18
    private static object syncRoot = new object(); //关键:使用静态变量,在多线程中才可以真正实现锁定 lock (syncRoot) { //要锁定的方法 }
  • lock是什么. 1 public class ThreadTest 2 { 3 private int i = 0; 4 public void Test() 5 { 6 Thread t1 = new Thread(Thread1); 7 Thread t2 = new Thread(Thread2); 8 t1.Start(); 9 t2.Start...
  • 多线程——Lock使用

    千次阅读 2019-04-12 11:27:43
    Lock是一个接口,其中常用的方法有: 尝试获取锁,获取成功则返回,否则阻塞当前线程 void lock(); 尝试获取锁,线程在成功获取锁之前被中断,则放弃获取锁,抛出异常 void lockInterruptib...
  • Java多线程-Lock锁的使用

    千次阅读 2019-03-15 13:42:08
    文章目录Lock锁的使用Lock接口1.1 Lock接口简介1.2 Lock的简单使用1.3 Lock接口的特性和常见方法Lock接口的实现类:ReentrantLock2.1 第一个ReentrantLock程序2.2 Condition接口简介2.3 使用Condition实现等待...
  • 多线程-lock 锁的使用

    千次阅读 2016-11-13 13:34:07
    所以,Lock 实现提供了比使用 synchronized 方法和语句可获得的更广泛的锁定操作。此实现允许更灵活的结构,可以具有差别很大的属性,可以支持个相关的 Condition 对象。锁是控制线程对共享资源
  • 多线程4-Lock使用

    千次阅读 2018-05-18 10:20:23
    除此之外,通过Lock对象来实现同步效果。 知识点 1- ReentrantLock类的使用 2- ReentrantReadWriteLock类的使用 ReentrantLock类 案例说明ReentrantLock可实现线程同步效果。 运行类: package ...
  • c# 中多线程同步Lock用法

    千次阅读 2011-07-08 11:13:29
    临界区(Critical Section) 是一段在同一时候只被一个线程进入/执行的代码。为啥要有这个东西?是因为这段代码访问了“临界资源”,而这种资源只能同时被互斥地访问。举个例子来说,你的银行账户就是一个互斥资源...
  • JAVA多线程——锁Lock

    2020-09-03 14:31:58
    JAVA多线程——锁Lock Lock是一个接口,是在JUC包下的,Lock接口是控制多个线程对共享资源进行访问的工具。每次只能有一个线程拿到Lock对象的锁,线程开始访问共享资源前应先获得Lock对象。 不加锁,线程不安全 ...
  • Java中在使用多线程时,调用的方法计算比较复杂。为了避免线程之间数据混乱,使用Lock,但是这就导致失去了多线程应该有效果,有什么方法能够解决吗?
  • 查询当前线程保持此锁锁定的个数,也就是调用lock方法的次数 2、int getQueueLength 返回正等待此锁定的线程估计数,例如有5个线程,1个线程首先执行await,那么调用getQueueLength返回4 3、int ...
  • Lockers在多线程编程里面一个重要的概念是锁定,如果一个资源是多个线程共享的,为了保证数据的完整性,在进行事务性操作时需要将共享资源锁定,这样可以保证在做事务性操作时只有一个线程能对资源进行操作。...
  • 在前面一篇博文《Python多线程编程(一):threading 模块 Thread 类的用法详解 》 我有简单介绍怎么利用 threading 模块进行多线程的编码。 但那只是多线程编码最简单的部分,真正难的其实是多个线程之间的通信和...
  • Java多线程Lock使用

    万次阅读 多人点赞 2011-12-21 09:48:37
    import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future;...import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReadWr
  • Java 多线程Lock 关键字

    千次阅读 2016-03-14 21:53:27
    sleep() & interrupt()线程A正在使用sleep()暂停着: Thread.sleep(100000); 如果要取消他的等待状态,可以在正在执行的线程里(比如这里是B)调用 a.interrupt();令线程A放弃睡眠操作,这里a是线程A对应到的Thread实例...
  • 相对于synchronized机制,Lock的锁机制更加的灵活,并且能够实现有选择性地进行线程通知。因此相比于重量级的synchronized机制,...1. Lock使用 常规用法,利用Condition类与ReentrantLock类,实现代码的同步 publ
  • 线程各自占有一些共享资源,并且互相等待其他线程占有的资源才能运行,而导致两个或者线程都在等待对方释放资源,都会停止执行的情形,某一个同步块同时拥有“两个以上对象的锁”时,就可能会发生“死锁”的...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 244,624
精华内容 97,849
关键字:

多线程lock的用法