精华内容
下载资源
问答
  • 线程锁

    千次阅读 2018-08-27 21:27:29
    一、多线程-同步函数的是this /* 同步函数用的是哪一个呢? 函数需要被对象调用。那么函数都有一个所属对象引用。就是this。 所以同步函数使用的是this。 通过该程序进行验证。 使用两个线程来买票。 一个线程...

    一、多线程-同步函数的锁是this
    /*
    同步函数用的是哪一个锁呢?
    函数需要被对象调用。那么函数都有一个所属对象引用。就是this。
    所以同步函数使用的锁是this。

    通过该程序进行验证。

    使用两个线程来买票。
    一个线程在同步代码块中。    这两个的锁不一样
    一个线程在同步函数中。
    都在执行买票动作。


    */


    class Ticket implements Runnable
    {
        private  int tick = 100;
        Object obj = new Object();
        boolean flag = true;
        public  void run()
        {
            if(flag)
            {
                while(true)
                {
                    synchronized(this)    /*同步函数的锁*/
                    {
                        if(tick>0)
                        {
                            try{Thread.sleep(10);}catch(Exception e){}
                            System.out.println(Thread.currentThread().getName()+"....code : "+ tick--);
                        }
                    }
                }
            }
            else
                while(true)
                    show();           /*使用this锁*/
        }
        public synchronized void show()//this
        {
            if(tick>0)
            {
                try{Thread.sleep(10);}catch(Exception e){}
                System.out.println(Thread.currentThread().getName()+"....show.... : "+ tick--);
            }
        }
    }


    class  ThisLockDemo
    {
        public static void main(String[] args) 
        {

            Ticket t = new Ticket();

            Thread t1 = new Thread(t);
            Thread t2 = new Thread(t);
            t1.start();
            try{Thread.sleep(10);}catch(Exception e){}
            t.flag = false;
            t2.start();


    //        Thread t3 = new Thread(t);
    //        Thread t4 = new Thread(t);
    //        t3.start();
    //        t4.start();


        }
    }

     

    二、多线程-静态同步函数的锁是class对象


    /*
    如果同步函数被静态修饰后,使用的锁是什么呢?

    通过验证,发现不在是this。因为静态方法中也不可以定义this。

    静态进内存是,内存中没有本类对象,但是一定有该类对应的字节码文件对象。
    类名.class  该对象的类型是Class


    静态的同步方法,使用的锁是该方法所在类的字节码文件对象。 类名.class
    */
    class Ticket implements Runnable
    {
        private static  int tick = 100;
        //Object obj = new Object();
        boolean flag = true;
        public  void run()
        {
            if(flag)
            {
                while(true)
                {
                    synchronized(Ticket.class)           /*使用了静态,锁就变了*/
                    {
                        if(tick>0)
                        {
                            try{Thread.sleep(10);}catch(Exception e){}
                            System.out.println(Thread.currentThread().getName()+"....code : "+ tick--);
                        }
                    }
                }
            }
            else
                while(true)
                    show();
        }
        public static synchronized void show()
        {
            if(tick>0)
            {
                try{Thread.sleep(10);}catch(Exception e){}
                System.out.println(Thread.currentThread().getName()+"....show.... : "+ tick--);
            }
        }
    }


    class  StaticMethodDemo
    {
        public static void main(String[] args) 
        {

            Ticket t = new Ticket();

            Thread t1 = new Thread(t);
            Thread t2 = new Thread(t);
            t1.start();
            try{Thread.sleep(10);}catch(Exception e){}
            t.flag = false;
            t2.start();


        }
    }
     

    三、多线程-单例设计模式-懒汉式 出现的安全问题


    /*
    单例设计模式。 必须要记住,面试题
    饿汉式与懒汉式的区别
    1.懒汉式是延时加载的形式
    2.懒汉式由于是延时加载,所以在多线程中会出现安全问题
    3.可以加同步函数或者同步代码块来解决,而同步函数每次都需要判断比较慢
    4.这里采用同步代码块来解决安全问题
    5.由于函数是静态的,所以锁是类名.class

    */
    //饿汉式。
    /*
    class Single
    {
        private static final Single s = new Single();
        private Single(){}
        public static Single getInstance()
        {
            return s;
        }
    }
    */


    //懒汉式           

    class Single
    {
        private static Single s = null;
        private Single(){}


        public static  Single getInstance()
        {
            if(s==null)
            {
                synchronized(Single.class)
                {
                    if(s==null)
                        //--->A;
                        s = new Single();
                }
            }
            return s;
        }
    }

    class SingleDemo 
    {
        public static void main(String[] args) 
        {
            System.out.println("Hello World!");
        }
    }
     

     

     

    四、死锁(要避免出现和熟悉)



    class Test implements Runnable
    {
        private boolean flag;
        Test(boolean flag)
        {
            this.flag = flag;
        }

        public void run()
        {
            if(flag)
            {
                while(true)
                {
                    synchronized(MyLock.locka)
                    {
                        System.out.println(Thread.currentThread().getName()+"...if locka ");
                        synchronized(MyLock.lockb)
                        {
                            System.out.println(Thread.currentThread().getName()+"..if lockb");                    
                        }
                    }
                }
            }
            else
            {
                while(true)
                {
                    synchronized(MyLock.lockb)
                    {
                        System.out.println(Thread.currentThread().getName()+"..else lockb");
                        synchronized(MyLock.locka)
                        {
                            System.out.println(Thread.currentThread().getName()+".....else locka");
                        }
                    }
                }
            }
        }
    }


    class MyLock
    {
        static Object locka = new Object();     /*静态,能用类名直接调用*/
        static Object lockb = new Object();
    }

    class  DeadLockTest
    {
        public static void main(String[] args) 
        {
            Thread t1 = new Thread(new Test(true));
            Thread t2 = new Thread(new Test(false));
            t1.start();
            t2.start();
        }
    }

    多线程(面试中的线程问题)
    线程状态:
    1、新建状态:当用new操作符创建一个线程时,线程还没有开始运行线程中的代码,此时线程处在新建状态。 
    2、就绪状态:当线程对象调用start()方法即启动了线程,等待调度程序(thread scheduler)分配CPU时间的状态。
    3运行状态(Running): 当线程获得CPU时间后,它才进入运行状态,真正开始执行run()方法.
    4.阻塞状态(Blocked):所谓阻塞状态是正在运行的线程没有运行结束,暂时让出CPU,这时其他处于就绪状态的线程就可以获得CPU时间,进入运行状态。
     进入阻塞状态的原因:
           1>线程通过调用sleep方法进入睡眠状态;
           2>线程调用一个在I/O上被阻塞的操作,即该操作在输入输出操作完成之前不会返回到它的调用者;
           3>线程试图得到一个锁,而该锁正被其他线程持有;
           4>线程在等待某个触发条件;
           ......          
    5.死亡状态(Dead): 1) run方法正常退出而自然死亡 2) 一个未捕获的异常终止了run方法而使线程猝死。
    Java中创建线程主要有三种方式:1.继承Thread类2.实现Runnable接口3.创建Callable接口的实现类(有返回值)
     一、继承Thread类创建线程类
    (1)定义Thread类的子类,并重写该类的run方法,该run方法的方法体就代表了线程
         要完成的任务。因此把run()方法称为执行体。
    (2)创建Thread子类的实例,即创建了线程对象。
    (3)调用线程对象的start()方法来启动该线程。
    二、通过Runnable接口创建线程类
    (1)定义runnable接口的实现类,并重写该接口的run()方法,该run()方法的方法体
        同样是该线程的线程执行体。
    (2)创建 Runnable实现类的实例,并依此实例作为Thread的target来创建Thread对
             象,该Thread对象才是真正的线程对象。
    (3)调用线程对象的start()方法来启动该线程。
    三、通过Callable和Future创建线程
    (1)创建Callable接口的实现类,并实现call()方法,该call()方法将作为线程执行
             体,并且有返回值。
    (2)创建Callable实现类的实例,使用FutureTask类来包装Callable对象,该FutureTask
             对象封装了该Callable对象的call()方法的返回值。
    (3)使用FutureTask对象作为Thread对象的target创建并启动新线程。
    (4)调用FutureTask对象的get()方法来获得子线程执行结束后的返回值
    线程终止的三种情况:
     1 线程正常执行结束。
     2 线程抛出异常且未被捕获。
     3 线程调用stop()方法终止线程,该方法已经过期。不建议使用
    线程方法:
    sleep():让线程进入睡眠等待一段时间,线程进入阻塞状态,时间过后进入就绪状态。
    interrupt():让当前线程终止等待,进入就绪状态。
     Join():让当前线程等待加入的线程完成后才能继续执行
    setPriority():设置线程优先级,最高10,最低1,默认5.
    setDaemon(true):守护线程必须在调用start()方法之前,设置守护线程,当程序中只有守护程序时,该程序运行结束。
    线程锁关键字:synchronized (同步)  
    定义锁对象:staticfinalObjectlock=newObject();
    对lock对象上锁: synchronized(lock);
    对方法上锁:privatestaticsynchronizedvoiddoTask(inttid)
    解决线程死锁:多个线程上锁的顺序要一致。
    线程协作:
    void wait():当前线程等待,等待其他线程调用对象的notify()或notifyAll()方法  
    void notify():唤醒在此对象锁上等待的单个线程
    void notifyAll():唤醒在此对象锁上等待的所有线程
    线程池的作用:
    1.提高程序的执行效率
    2.控制线程的数量,防止程序崩溃
    创建线程池的常用方法:
    ForkJoinPool:作用充分挖掘利用多核CPU的能力,把一个大任务分解成多个小任务,执行完后再行合并。

    线程池的使用方法:
    1、创建一个线程池
    2、创建一个Runnable对象
    3、将Runnable对象作为线程任务提交到线程池中,调用线程池的submit或excute方法
    4、使用完线程池后,调用线程的shutdown()方法关闭线程池(不会立马关闭 先执行完线程池中的所有已提交任务后 依次关闭线程对象 所有的线程对象进入死亡状态)
    注:shutdownNow();//试图停止正在执行的线程任务,暂停正在等待的任务..但这是试图,也不一定立马关闭。
    线程池的submit和excute方法的区别:
    1、接收的参数不一样
    2、submit有返回值,而execute没有
    3、submit方便Exception处理

     

    展开全文
  • 在分布式集群系统的开发中,线程锁往往并不能支持全部场景的使用,必须引入新的技术方案分布式锁。 线程锁  主要用来给方法、代码块加锁。当某个方法或者代码块使用锁时,那么在同一时刻至多仅有有一个线程在执行...

    在分布式集群系统的开发中,线程锁往往并不能支持全部场景的使用,必须引入新的技术方案分布式锁。

    线程锁
      主要用来给方法、代码块加锁。当某个方法或者代码块使用锁时,那么在同一时刻至多仅有有一个线程在执行该段代码。当有多个线程访问同一对象的加锁方法/代码块时,同一时间只有一个线程在执行,其余线程必须要等待当前线程执行完之后才能执行该代码段。但是,其余线程是可以访问该对象中的非加锁代码块的。

    进程锁
      也是为了控制同一操作系统中多个进程访问一个共享资源,只是因为程序的独立性,各个进程是无法控制其他进程对资源的访问的,但是可以使用本地系统的信号量控制(操作系统基本知识)。

    分布式锁
      当多个进程不在同一个系统之中时,使用分布式锁控制多个进程对资源的访问。

    分布式锁到底是什么,怎么实现?

    实现分布式锁必须要依靠第三方存储介质来存储锁的元数据等信息。比如分布式集群要操作某一行数据时,这个数据的流水号是唯一的,那么我们就把这个流水号作为一把锁的id,当某进程要操作该数据时,先去第三方存储介质中看该锁id是否存在,如果不存在,则将该锁id写入,然后执对该数据的操作;当其他进程要访问这个数据时,会先到第三方存储介质中查看有没有这个数据的锁id,有的话就认为这行数据目前已经有其他进程在使用了,就会不断地轮询第三方存储介质看其他进程是否释放掉该锁;当进程操作完该数据后,该进程就到第三方存储介质中把该锁id删除掉,这样其他轮询的进程就能得到对该锁的控制。

    线程锁,进程锁,分布式锁的作用都是一样的,只是作用的范围大小不同。范围大小:分布式锁 > 进程锁 > 线程锁。能用线程锁,进程锁情况下使用分布式锁也是可以的,能用线程锁的情况下使用进程锁也是可以的。只是范围越大技术复杂度就越大。
      
    如果大家对java架构相关感兴趣,可以关注下面公众号,会持续更新java基础面试题, netty, spring boot,spring cloud等系列文章,一系列干货随时送达, 超神之路从此展开, BTAJ不再是梦想!

    架构殿堂

    展开全文
  • Python中线程同步与线程锁

    千次阅读 2019-06-09 21:12:02
    文章目录Python中线程同步与线程锁线程同步threading.Event对象threading.Timer定时器,延迟执行threading.Lock锁可重入锁RLockCondition条件锁,等待通知therading.Semaphore信号量threading.BoundedSemaphore有界...

    线程同步与线程锁

    线程同步

    概念
    * 线程同步,线程间协同,通过某种技术,让一个线程访问某些数据时,其他线程不能访问这些数据,直到该线程完 成对数据的操作。

    1.threading.Event对象

    • Event事件,是线程间通信机制中最简单的实现,使用一个内部的标记flag,通过flag的True或False的变化 来进行操作
    名称 含义
    event.set() 标记设置为True
    event.clear() 标记设置为False
    event.is_set() 标记是否为True
    event.wait(timeout=None) 设置等待标记为True的时长,None为无限等待。等到返回True,未等到超时了返回False
    • 老板雇佣了一个工人,让他生产杯子,老板一直等着这个工人,直到生产了10个杯子
    import threading
    import time
    import logging
    
    logging.basicConfig(format="%(asctime)s %(threadName)s %(thread)s %(message)s",level=logging.INFO)
    
    def worker(event:threading.Event,count = 10):
        logging.info("我是worker工作线程")
        cups = []
        while True:
            logging.info("制作了一个 cup")
            time.sleep(0.2)
            cups.append(1)
            if len(cups)>=count:
                event.set()
                break
        logging.info("制作完成:{}".format(cups))
    
    def boss(event:threading.Event):
        logging.info("我是boss")
        event.wait()
        logging.info("Good Job")
    
    event = threading.Event()
    b = threading.Thread(target=boss,args=(event,))
    w = threading.Thread(target=worker,args=(event,))
    b.start()
    w.start()
    

    threading2_001

    • 使用同一个Event对象的标记flag。

    • 谁wait就是等到flag变为True,或等到超时返回False。不限制等待的个数。

    • wait的使用

    from threading import Thread,Event
    import logging
    
    logging.basicConfig(format="%(asctime)s %(threadName)s %(thread)s %(message)s",level=logging.INFO)
    
    def worker(event:Event,interval:int):
        while not event.wait(interval):
            logging.info("没有等到。。")
    
    e = Event()
    Thread(target=worker,args=(e,1)).start()
    
    e.wait(5)
    e.set()
    
    print("======end========")
    

    threading2_002

    2.threading.Timer定时器,延迟执行

    方法 含义
    Timer.cancel() 取消定时器,(定时器为执行函数时可以取消,在函数执行中无法取消)
    Time.start() 启动定时器
    • threading.Timer继承自Thread,这个类用来定义延迟多久后执行一个函数。
    • class threading.Timer(interval, function, args=None, kwargs=None)
      1. interval #多少时间后执行function函数
      2. function #需要执行的函数
    • start方法执行之后,Timer对象会处于等待状态,等待了interval秒之后,开始执行function函数的。
    • Timer是线程Thread的子类,Timer实例内部提供了一个finished属性,该属性是Event对象。
    • cancel方法,本质上 是在worker函数执行前对finished属性set方法操作,从而跳过了worker函数执行,达到了取消的效果。
    from threading import Timer
    import logging
    import time
    
    logging.basicConfig(format="%(asctime)s %(threadName)s %(thread)s %(message)s",level=logging.INFO)
    
    def worker():
        logging.info("in worker")
        time.sleep(5)
        logging.info("end in worker")
    
    t = Timer(2,worker)
    t.setName("timer1") #设置线程名称
    # t.cancel() #取消定时器后,定时器不在执行
    t.start()
    # t.cancel() #取消定时器后,定时器不在执行
    time.sleep(4) #等待4秒后,定时器已经开始执行
    t.cancel() #当定时器执行后,无法取消
    
    print("======end========")
    

    threading2_003

    3.threading.Lock锁

    锁(Lock):一旦线程获得锁,其他试图获取锁的线程将被阻塞等待。
    锁:凡是存在共享支援争抢的地方都可以使用锁,从而保证只有一个使用者可以完全使用这个资源。

    名称 含义
    Lock.acquire(blocking=True,timeout=-1) 获取锁,获取成功返回True,否则返回False
    当获取不到锁时,默认进入阻塞状态,直到获取到锁,后才继续。阻塞可以设置超时时间。非阻塞时,timeout禁止设置。如果超时依旧未获取到锁,返回False。
    Lock.rease() 释放锁,可以从任何线程调用释放。
    已上锁的锁,会被设置为unlocked。如果未上锁调用,会抛出RuntimeError异常。
    import threading
    import sys
    import time
    
    def print(*args):
        sys.stdout.write(" ".join(map(str,args))+"\n")
    
    def worker(lock):
        print("worker start",threading.get_ident(),threading.current_thread().name)
        lock.acquire()
        print("worker over",threading.get_ident(),threading.current_thread().name)
    
    lock = threading.Lock()
    lock.acquire()
    print(" -"*30)
    for i in range(5):
        threading.Thread(target=worker,args=(lock,),name="w{}".format(i)).start()
    
    print("- "* 30)
    while True:
        time.sleep(0.1)
        cmd = input(">>").strip()
        if cmd == "r":
            lock.release()
        elif cmd == "q":
            break
        else:
            print(threading.enumerate())
    

    threading2_004
    上例可以看出不管在哪一个线程中,只要对一个已经上锁的锁阻塞请求,该线程就会阻塞。

    • 加锁,解锁
      一般来说,加锁就需要解锁,但是加锁后解锁前,还要有一些代码执行,就有可能抛异常,一旦出现异常,锁是无 法释放,但是当前线程可能因为这个异常被终止了,这也产生了死锁

    • 加锁、解锁常用语句:

      1. 使用try…finally语句保证锁的释放
      2. with上下文管理,锁对象支持上下文管理
    • 计数器类,可加,可减。

    import threading
    import sys
    import time
    
    def print(*args):
        sys.stdout.write(" ".join(map(str,args))+"\n")
    
    class Counter:
        def __init__(self):
            self._val = 0
            self.lock = threading.Lock()
    
        @property
        def value(self):
            with self.lock:
                return self._val
    
        def inc(self):
            with self.lock:
                self._val += 1
    
        def dec(self):
            with self.lock:
                self._val -= 1
    
    def run(c:Counter,count=100):
        for _ in range(count):
            for i in range(-50,50):
                if i <0:
                    c.dec()
                else:
                    c.inc()
    
    c = Counter()
    c1 = 10 #线程数
    c2 = 1000
    for i in range(c1):
        threading.Thread(target=run,args=(c,c2)).start()
    
    for k in threading.enumerate():
        if k.name != "MainThread":
            k.join()
    
    print(c.value)
    
    • 锁的应用场景
      锁适用于访问和修改同一个共享资源的时候,即读写同一个资源的时候。

    • 使用锁的注意事项:

      1. 少用锁,必要时用锁。使用了锁,多线程访问被锁的资源时,就成了串行,要么排队执行,要么争抢执行
        • 举例,高速公路上车并行跑,可是到了省界只开放了一个收费口,过了这个口,车辆依然可以在多车道 上一起跑。过收费口的时候,如果排队一辆辆过,加不加锁一样效率相当,但是一旦出现争抢,就必须 加锁一辆辆过。注意,不管加不加锁,只要是一辆辆过,效率就下降了。
      2. 加锁时间越短越好,不需要就立即释放锁
      3. 一定要避免死锁
    • 非阻塞锁的使用

    import threading
    import sys
    import time
    
    def print(*args):
        sys.stdout.write(" ".join(map(str,args))+"\n")
    
    def worker(lock:threading.Lock):
        while True:
            if lock.acquire(False):
                print("do something.")
                time.sleep(1)
                lock.release()
                break
            else:
                print("try again")
                time.sleep(1)
    
    lock = threading.Lock()
    for i in range(5):
        threading.Thread(target=worker,name="w{}".format(i),args=(lock,)).start()
    

    threading2_005

    4.可重入锁RLock

    • 可重入锁,是线程相关的锁
    • 线程A获得可重复锁,并可以多次成功获取,不会阻塞。最后要在线程A中做和acquire次数相同的release
    import threading
    import sys
    import time
    
    def print(*args):
        sys.stdout.write(" ".join(map(str,args))+"\n")
    
    def fib(num,rlock:threading.RLock):
        with rlock:
            if num<3:
                return 1
            return fib(num-1,rlock)+fib(num-2,rlock)
    
    def work(num,rlock):
        print(fib(num,rlock))
    
    rlock = threading.RLock()
    for i in range(1,10):
        threading.Thread(target=work,args=(i,rlock)).start()
    

    可重入锁
    * 与线程相关,可在一个线程中获取锁,并可继续在同一线程中不阻塞多次获取锁
    * 当锁未释放完,其它线程获取锁就会阻塞,直到当前持有锁的线程释放完锁
    * 锁都应该使用完后释放。可重入锁也是锁,应该acquire多少次,就release多少次

    5.Condition条件锁,等待通知

    构造方法Condition(lock=None),可以传入一个Lock或RLock对象,默认是RLock。

    名称 含义
    Condition.acquire(self,*args) 获取锁
    Condition.wait(self,timeout=None) 等待通知,timeout设置超时时间
    Condition.notify(self,n=1) 唤醒至多指定数目个数的等待的线程,没有等待的线程就没有任何操作
    Condition.notify_all(self) 唤醒所有等待的线程
    • 每个线程都可以通过Condition获取已把属于自己的锁,在锁中可以等待其他进程的同级锁的通知。当获取到同级锁的通知后,会停止等待。

    • 当使用Condition(lock=Lock())初始化锁时,锁只能一级等待,不能出现多级等待。

    • 简单示例:

    import threading
    import time
    
    def work(cond:threading.Condition):
        with cond:
            print("开始等待")
            cond.wait()
            print("等到了")
    
    cond = threading.Condition()
    # cond = threading.Condition(threading.Lock())
    threading.Thread(target=work,args=(cond,)).start()
    threading.Thread(target=work,args=(cond,)).start()
    
    with cond:
        with cond:
            time.sleep(1)
            print("开始释放二级等待")
            print(cond.notifyAll())
        time.sleep(2)
        print("开始释放一级等待")
        cond.notifyAll()
    

    threading2_006

    • 广播模式示例:
    from threading import Thread,Condition,Lock
    import time
    import logging
    import random
    
    logging.basicConfig(format="%(asctime)s %(threadName)s %(thread)s %(message)s",level=logging.INFO)
    
    class Dispachter:
        def __init__(self):
            self.data = None
            self.cond = Condition(lock=Lock())
    
        #生成者
        def produce(self,total):
            for _ in range(total):
                data = random.randint(1,100)
                with self.cond:
                    logging.info("生产了一个数据:{}".format(data))
                    self.data = data
                    self.cond.notify(1)
                time.sleep(1) #模拟生成数据需要耗时1秒
    
        #消费者
        def consume(self):
            while True:
                with self.cond:
                    self.cond.wait() #等待
                    data = self.data
                    logging.info("消费了一个数据 {}".format(data))
                    self.data = None
    
    d = Dispachter()
    p = Thread(target=d.produce,name="producer",args=(10,))
    
    # 增加消费者
    for i in range(5):
        c = Thread(target=d.consume,name="consumer{}".format(i))
        c.start()
    
    p.start()
    

    threading2_007

    上面例子中演示了生产者生产一个数据,就通知一个消费者消费。

    • Condition总结
      Condition用于生产者消费者模型中,解决生产者消费者速度匹配的问题。采用了通知机制,非常有效率。
    • 使用方式
      使用Condition,必须先acquire,用完了要release,因为内部使用了锁,默认使用RLock锁,最好的方式是使用 with上下文。
      消费者wait,等待通知。
      生产者生产好消息,对消费者发通知,可以使用notify或者notify_all方法。

    6.therading.Semaphore信号量

    和Lock很像,信号量对象内部维护一个倒计数器,每一次acquire都会减1,当acquire方法发现计数为0就阻塞请求 的线程,直到其它线程对信号量release后,计数大于0,恢复阻塞的线程。

    名称 含义
    Semaphore(value=1) 构造方法。value为初始信号量。value小于0,抛出ValueError异常
    Semaphore.acquire(self,blocking=True,timeout=None) 获取信号量,技术器减1,即_value的值减少1。如果_value的值为0会变成阻塞状态。获取成功返回True
    Semaphore.release(self) 释放信号量,计数器加1。即_value的值加1
    Semaphore._value 信号量,当前信号量
    • 注意
      1. 计数器永远不会低于0,因为acquire的时候,发现是0,都会被阻塞。
      2. 信号量没有做超界限制
    from threading import Semaphore
    
    s =Semaphore(3)
    print(s._value)
    s.release() #会增加信号量
    print(s._value) #可以看出没有做信号量上线控制
    print("----------------")
    print(s.acquire())
    print(s._value)
    print(s.acquire())
    print(s._value)
    print(s.acquire())
    print(s._value)
    print(s.acquire())
    print(s._value)
    print(s.acquire()) #当信号量为0再次acquire会被阻塞
    print("~~~~~~阻塞了吗?")
    print(s._value)
    

    threading2_008

    • 跨线程使用演示
    from threading import Thread,Semaphore
    import time
    import logging
    
    logging.basicConfig(format="%(asctime)s %(threadName)s %(thread)s %(message)s",level=logging.INFO)
    
    #定义获取信号量
    def worker(s:Semaphore):
        while s.acquire():
            logging.info("被执行了一次,获取一个信号量 _value={}".format(s._value))
    
    #释放信号量
    def cunn(s:Semaphore):
        while True:
            logging.info("释放一个信号量")
            s.release()
            time.sleep(1)
    
    s = Semaphore(3)
    #创建3个线程获取信号量
    for i in range(3):
        Thread(target=worker,args=(s,),name="w{}".format(i)).start()
    
    #开启一个线程释放信号量
    Thread(target=cunn,args=(s,)).start()
    

    threading2_009

    7.threading.BoundedSemaphore有界信号量

    • 有界信号量,不允许使用release超出初始值的范围,否则,抛出ValueError异常
    名称 含义
    BoundedSemaphore(value=1) 构造方法。value为初始信号量。value小于0,抛出ValueError异常
    BoundedSemaphore.acquire(self,blocking=True,timeout=None) 获取信号量,技术器减1,即_value的值减少1。如果_value的值为0会变成阻塞状态。获取成功返回True
    BoundedSemaphore.release(self) 释放信号量,计数器加1。即_value的值加1,超过初始化值会抛出异常ValueError。
    BoundedSemaphore._value 信号量,当前信号量
    from threading import BoundedSemaphore
    
    bs = BoundedSemaphore(3)
    print(bs._value)
    bs.acquire()
    bs.acquire()
    bs.acquire()
    print(bs._value)
    bs.release()
    bs.release()
    bs.release()
    print(bs._value)
    bs.release()
    

    threading2_010

    • 应用举例
      连接池
      因为资源有限,且开启一个连接成本高,所以,使用连接池。
      一个简单的连接池
      连接池应该有容量(总数),有一个工厂方法可以获取连接,能够把不用的连接返回,供其他调用者使用。
    from threading import Thread,BoundedSemaphore
    import time
    import logging
    import random
    
    logging.basicConfig(format="%(asctime)s %(threadName)s %(thread)s %(message)s",level=logging.INFO)
    
    #链接类
    class Conn:
        def __init__(self,name):
            self.name = name
    
    class Pool:
        def __init__(self,count:int):
            self.count = count
            #池中放着链接备用
            self.pool = [self._connect("conn-{}".format(i)) for i in range(count)]
            self.bsemaphore = BoundedSemaphore(count)
    
        #创建连接方法,返回一个连接对象
        def _connect(self,conn_name):
            return Conn(conn_name)
    
        #获取一个链接
        def get_conn(self):
            self.bsemaphore.acquire()
            self.pool.pop()
            logging.info("从连接池拿走了一个连接~~~~~~~")
    
        #归还一个连接
        def return_conn(self,conn:Conn):
            logging.info("归还了一个连接----------")
            self.pool.append(conn)
            self.bsemaphore.release()
    
    def worker(pool:Pool):
        conn = pool.get_conn()
        logging.info(conn)
        #模拟使用了一段时间
        time.sleep(random.randint(1,5))
        pool.return_conn(conn)
    
    pool = Pool(3)
    for i in range(6):
        Thread(target=worker,name="worker-{}".format(i),args=(pool,)).start()
    

    threading2_011
    上例中,使用信号量解决资源有限的问题。
    如果池中有资源,请求者获取资源时信号量减1,拿走资源。当请求超过资源数,请求者只能等待。当使用者用完 归还资源后信号量加1,等待线程就可以被唤醒拿走资源。
    注意:这个连接池的例子不能用到生成环境,只是为了说明信号量使用的例子,连接池还有很多未完成功能。

    • 问题分析
    1. 边界问题
      • 假设一种极端情况,计数器还差1就归还满了,有三个线程A、B、C都执行了第一句,都没有来得及release,这时 候轮到线程A release,正常的release,然后轮到线程C先release,一定出问题,超界了,直接抛异常。 因此信号量,可以保证,一定不能多归还。
    2. 正常使用分析
      • 正常使用信号量,都会先获取信号量,然后用完归还。
      • 创建很多线程,都去获取信号量,没有获得信号量的线程都阻塞。能归还的线程都是前面获取到信号量的线程,其 他没有获得线程都阻塞着。非阻塞的线程append后才release,这时候等待的线程被唤醒,才能pop,也就是没有 获取信号量就不能pop,这是安全的。
      • 经过上面的分析,信号量比计算列表长度好,线程安全。
    • 信号量和锁
      1. 信号量,可以多个线程访问共享资源,但这个共享资源数量有限。
      2. 锁,可以看做特殊的信号量,即信号量计数器初值为1。只允许同一个时间一个线程独占资源。

    总结

    threading模块中的类

    常用方法 含义
    Event set(self) 将标记设置为True
    clear(self) 将标记设置为False
    is_set() 判断当前标记是否为True,是True返回True,否则返回False
    相当于为返回当前标记
    wait(self,timeout=None) 如果当前标记为True,立即返回True,如果当前标记为False,会产生一个阻塞,直到标记为True时返回True。timeout等待超时时间,默认为None表示无限等待。未等到超时了返回False
    Time定时器,延迟执行 Timer(interval, function, args=None, kwargs=None) 实例化构造方法
    1. interval #多少时间后执行function函数
    2. function #需要执行的函数
    cancel(self) 取消定时器
    (定时器为执行函数时可以取消,在函数执行中无法取消)
    start() 启动定时器
    Lock锁 acquire(self,blocking=True,timeout=-1) 获取锁,获取成功返回True,否则返回False
    当获取不到锁时,默认进入阻塞状态,直到获取到锁,后才继续。
    阻塞可以设置超时时间。
    非阻塞时,timeout禁止设置。如果超时依旧未获取到锁,返回False。
    rease(self) 释放锁,可以从任何线程调用释放。
    已上锁的锁,会被设置为unlocked。如果未上锁调用,会抛出RuntimeError异常。
    RLock可重入锁 和Lock类似但是:
    线程A获得可重复锁,并可以多次成功获取,不会阻塞。最后要在线程A中做和acquire次数相同的release
    Condition Condition(self,lock=None) 构造方法,lock默认值为None表示使用RLock()锁,也可以自己传入为Lock()
    acquire(self,*args) 获取锁
    rease(self) 释放锁
    wait(self,timeout=None) 等待通知,timeout设置超时时间。
    注意:必须获取锁后才能等待通知,notify或notify_all可以发通知
    notify(self,n=1) 唤醒至多指定数目个数的等待的线程,没有等待的线程就没有任何操作
    notify_all(self) 唤醒所有等待的线程
    Semaphore信号量 Semaphore(value=1) 构造方法。value为初始信号量。value小于0,抛出ValueError异常
    acquire(self,blocking=True,timeout=None) 获取信号量,技术器减1,即_value的值减少1。
    如果_value的值为0会变成阻塞状态。获取成功返回True
    release(self) 释放信号量,计数器加1。即_value的值加1
    `_value`属性 信号量,当前信号量
    BoundedSemaphore有界信号量 BoundedSemaphore(value=1) 构造方法。value为初始信号量。value小于0,抛出ValueError异常
    acquire(self,blocking=True,timeout=None) 获取信号量,技术器减1,即_value的值减少1。
    如果_value的值为0会变成阻塞状态。获取成功返回True
    release(self) 释放信号量,计数器加1。即_value的值加1,超过初始化值会抛出异常ValueError。
    _value 信号量,当前信号量
    展开全文
  • Python线程锁

    千次阅读 2018-08-04 14:55:41
    线程锁 当一个数据有多个线程都可以对其进行修改的时候,任何一个线程改变它都会对其他线程造成影响,如果我们某一个线程在使用完之前,其他线程不能对其修改,就需要对这个线程增加一个线程锁。 lock lock注重的...

    线程锁

    当一个数据有多个线程都可以对其进行修改的时候,任何一个线程改变它都会对其他线程造成影响,如果我们某一个线程在使用完之前,其他线程不能对其修改,就需要对这个线程增加一个线程锁。

    lock

    lock注重的是局部,某一个变量没有调用完,其他线程不能调用。

    import threading
    import time
    import random
    count = 0
    def get_money(money):
        global count
        count += money
        count += money
        count -= money
    lock = threading.Lock()
    def lock_thread(money):
        # 加锁
        lock.acquire()
        time.sleep(random.randint(1, 3))
        print('当前线程为',threading.current_thread().name)
        get_money(money)
        time.sleep(random.randint(1,3))
        print('当前线程为', threading.current_thread().name)
        # 解锁
        lock.release()
    thread1 = threading.Thread(target=lock_thread,name='thread1',args=(10000,))
    thread2 = threading.Thread(target=lock_thread,name='thread2',args=(15000,))
    thread1.start()
    thread2.start()
    print('hello world')

    输出结果为:

    hello world
    当前线程为 thread1
    当前线程为 thread1
    当前线程为 thread2
    当前线程为 thread2

    join

    join注重的是整体,线程1没有执行完,线程2不能执行。

    import threading
    import time
    import random
    count = 0
    def get_money(money):
        global count
        count += money
        count += money
        count -= money
    def lock_thread(money):
        time.sleep(random.randint(1, 3))
        print('当前线程为',threading.current_thread().name)
        get_money(money)
        time.sleep(random.randint(1,3))
        print('当前线程为', threading.current_thread().name)
    thread1 = threading.Thread(target=lock_thread,name='thread1',args=(10000,))
    thread2 = threading.Thread(target=lock_thread,name='thread2',args=(15000,))
    thread1.start()
    
    thread1.join()
    
    thread2.start()
    print('hello world')

    执行结果为:

    当前线程为 thread1
    当前线程为 thread1
    hello world
    当前线程为 thread2
    当前线程为 thread2

     

    展开全文
  • 快速理解线程锁

    万次阅读 2018-05-30 17:28:13
    线程锁 线程锁真的好麻烦啊!!! 找了几篇博客发现写的都不一样 相关联内容太多不容易理解 所以现在需要理清 什么是线程锁 应用场景 怎么用 优缺点 1. 什么是线程锁机制 多线程可以同时运行多个任务 ...
  • 线程锁/进程锁/文件锁

    千次阅读 2017-12-19 11:06:38
    线程锁/进程锁/文件锁  1.线程锁是锁线程的,锁住禁用,如果4线程的CPU锁一个线程剩余三个(如果可以锁的话),就像四车道封锁一条车道还剩3个车道可以跑车;  2.进程锁是锁进程的,进程就是正在运行的程序,锁住...
  • 多线程中线程锁的使用

    千次阅读 2018-05-07 13:15:40
    线程锁的5个要素:CRITICAL_SECTION g_cs; //定义线程锁InitializeCriticalSection(&amp;g_cs); //初始化DeleteCriticalSection(&amp;g_cs); //删除EnterCriticalSection(&amp;g_c...
  • Linux 线程锁详解

    千次阅读 2018-11-08 00:50:54
    Linux 线程锁详解
  • 线程锁、进程锁以及分布式锁 1.线程锁 2.进程锁 3.分布式锁 【技术分享篇】线程锁,进程锁以及分布式锁丨锁的实现及原理分析丨高效的使用 更多精彩内容包括:C/C++,Linux,Nginx,ZeroMQ,MySQL,Redis,MongoDB...
  • 二、线程锁(也叫同步锁、互斥锁) 2.1 使用synchronized关键字对方法进行加锁 2.1.1 语法 2.1.2 案例 2.2 使用synchronize关键字对线程方法中的某一部分加锁(同步块的方式) 2.2.1 语法 2.2.2 案例 2.3 静态...
  • 高并发:线程、线程锁与线程池(精华)

    千次阅读 多人点赞 2019-04-18 19:44:24
    单线程——多线程的开启——线程锁——线程同步工具——手写连接池——连接池工具类。 一、线程 1.线程的概念 2.线程与进程的关系 3.定义: 区别:如上!!! 4.wait()和sleep() 5.线程的状态及其他...
  • 线程锁的升级原理是什么?

    万次阅读 多人点赞 2019-05-20 11:04:06
    线程锁的升级原理是什么? 锁的级别从低到高: 无锁 -> 偏向锁 -> 轻量级锁 -> 重量级锁 锁分级别原因: 没有优化以前,sychronized是重量级锁(悲观锁),使用 wait 和 notify、notifyAll 来切换...
  • 线程锁 乐观锁 VS 悲观锁 乐观锁与悲观锁是一种广义上的概念,体现了看待线程同步的不同角度,在Java和数据库中都有此概念对应的实际应用。 1.乐观锁 顾名思义,就是很乐观,每次去拿数据的时候都认...
  • android 线程锁Lock

    千次阅读 2017-09-20 08:41:17
    今天,简单讲讲android的线程锁  Lock的使用。 这个其实和SynchronizedClass 是一样的。我记得我的一篇博客写过这个内容。再次记录一下。 一、同步机制关键字synchronized 对于java来说,最常用的同步...
  • ios多线程学习之GCD线程锁

    千次阅读 2015-11-30 00:03:58
    而在视频开发类项目中,最困难的就是逻辑和多线程处理,这几天没事研究了一下线程锁,我觉的一个简答的代码实例,就能体现出 GCD 线程锁的特征 还是先了解一点,在GCD 中实现锁的操作是通过线程队列来实现的 ...
  • 多线程面试题和答案:线程锁+线程池+线程同步1、并发编程三要素?2、多线程的价值?3、创建线程的有哪些方式?区别是什么?4、创建线程的三种方式的对比?4、线程的生命周期及五种基本状态及转换条件1、Java线程具有...
  • Java多线程锁释放

    千次阅读 2016-06-20 23:19:15
    Java多线程锁释放
  • 线程锁用于进程之间

    千次阅读 2014-09-29 21:01:34
    其实进程之间也可以用线程锁,因为线程锁不仅可以用于线程之间同样可以用于进程之间。 为了在多个进程之间使用线程锁,必须做到(1)互斥锁变量必须存储在为所有进程所共享的内存中;(2)必须通知线程函数库互斥锁...
  • 线程锁的原理是什么

    千次阅读 2015-06-25 15:18:36
    线程锁的使用 本文内容何时该使用线程锁. 线程锁的写法. 以线程锁的例子来理解线程的调度。使用线程锁的场合程序中经常采用多线程处理,这可以充分利用系统资源,缩短程序响应时间,改善用户体验;如果程序中只...
  • 线程锁的基本概念与简单封装

    千次阅读 2016-08-27 20:39:53
    线程锁的基本概念与简单封装 1.在多线程环境中,当我们需要保持线程同步时,通常通过锁来实现。 互斥锁的使用过程中,主要有 第一:线程锁的初始化,pthread_mutex_init 第二:线程锁的释放,pthread_mutex_...
  • python线程锁和进程锁

    千次阅读 2019-06-15 20:54:01
    往往会出现因cpu随机调度而导致结果和我们预期不一致的问题,这时就需要对线程或者进程加锁,以保证一个线程或进程在对共享对象进行修改时,其他的线程或进程无法访问这个对象,直至获取线程的操作执行完毕后...
  • python线程锁实践实例

    千次阅读 2016-07-13 16:38:26
    写这篇文章之前,我想起了一句实践出真知的诗,感觉就在嘴边但就是记不起来,最后百度之。 。 。 。 。...线程锁必须是全局的,这样方能实现在主线程(就是进程)和子线程之间切换。   注意点三
  • python之路 :线程锁

    千次阅读 2018-03-07 20:02:38
    线程锁: 当多个线程同时进行任务时,为保证不会有多个线程对同一个数据进行操作造成不可预料的后果,加个锁,将此时的线 程变为单线程进行操作。threading_Lock()四个状态:1、获取锁、上锁:acquire() 2、等待...
  • python多线程,线程锁

    万次阅读 2019-01-19 23:39:10
    python使用多线程, 不一定运行速度快,这里引入GIL(global interpreter lock) python解释器中任意时刻都只有一个线程在执行; GIL执行过程: 1). 设置一个GIL; 2). 切换线程去准备执行任务(Runnale就绪状态)...
  • Android多线程研究(9)——线程锁Lock

    万次阅读 多人点赞 2014-06-08 21:08:55
    在前面我们在解决线程同步问题的时候使用了synchronized关键字,今天我们来看看Java 5.0以后提供的线程锁Lock.Lock接口的实现类提供了比使用synchronized关键字更加灵活和广泛的锁定对象操作,而且是以面向对象的...
  • 二、什么是线程锁? 2.1 没有加锁时 2.2 锁加在普通方法上时,使用同一个对象调用。 2.3 锁加在普通方法上时,使用两个不同的对象调用。 2.4 锁加在静态方法上时,使用两个不同的对象调用。 三、释放锁是什么样的?...
  • 线程锁:当多个线程几乎同时修改一个共享数据的时候,需要进行同步控制,线程同步能够保证多个线程安全的访问竞争资源(全局内容),最简单的同步机制就是使用互斥锁。 某个线程要更改共享数据时,先将其锁定,此时...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 96,653
精华内容 38,661
关键字:

线程锁