Linux 线程同步都有哪些方法?

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Linux 线程同步都有哪些方法?相关的知识,希望对你有一定的参考价值。

Linux系统中,实现线程同步的方式大致分为六种,其中包括:互斥锁、自旋锁、信号量、条件变量、读写锁、屏障。最常用的线程同步方式就是互斥锁、自旋锁、信号量:

1、互斥锁

互斥锁本质就是一个特殊的全局变量,拥有lock和unlock两种状态,unlock的互斥锁可以由某个线程获得,当互斥锁由某个线程持有后,这个互斥锁会锁上变成lock状态,此后只有该线程有权力打开该锁,其他想要获得该互斥锁的线程都会阻塞,直到互斥锁被解锁。

互斥锁的类型:

①普通锁:互斥锁默认类型。当一个线程对一个普通锁加锁以后,其余请求该锁的线程将形成一个等待队列,并在锁解锁后按照优先级获得它,这种锁类型保证了资源分配的公平性。一个线程如果对一个已经加锁的普通锁再次加锁,将引发死锁;对一个已经被其他线程加锁的普通锁解锁,或者对一个已经解锁的普通锁再次解锁,将导致不可预期的后果。

②检错锁:一个线程如果对一个已经加锁的检错锁再次加锁,则加锁操作返回EDEADLK;对一个已经被其他线程加锁的检错锁解锁或者对一个已经解锁的检错锁再次解锁,则解锁操作返回EPERM。

③嵌套锁:该锁允许一个线程在释放锁之前多次对它加锁而不发生死锁;其他线程要获得这个锁,则当前锁的拥有者必须执行多次解锁操作;对一个已经被其他线程加锁的嵌套锁解锁,或者对一个已经解锁的嵌套锁再次解锁,则解锁操作返回EPERM。

④默认锁:一个线程如果对一个已经解锁的默认锁再次加锁,或者对一个已经被其他线程加锁的默认锁解锁,或者对一个解锁的默认锁解锁,将导致不可预期的后果;这种锁实现的时候可能被映射成上述三种锁之一。

【老男孩教育】Linux运维云计算课程汇集了虚拟化、云计算、安全攻防、Python开发、SRE等技术,课堂效率高、内容丰富全面,由浅入深,循序渐进,帮助学员稳扎稳打,夯实基础,在有限的时间内帮助学员高效提升,成为符合企业需求的技术型人才。

2、自旋锁

自旋锁顾名思义就是一个死循环,不停的轮询,当一个线程未获得自旋锁时,不会像互斥锁一样进入阻塞休眠状态,而是不停的轮询获取锁,如果自旋锁能够很快被释放,那么性能就会很高,如果自旋锁长时间不能够被释放,甚至里面还有大量的IO阻塞,就会导致其他获取锁的线程一直空轮询,导致CPU使用率达到100%,特别CPU时间。

3、信号量

信号量是一个计数器,用于控制访问有限共享资源的线程数。

参考技术A 一、互斥锁(mutex)
1.
初始化锁。在Linux下,线程的互斥量数据类型是pthread_mutex_t。在使用前,要对它进行初始化。
静态分配:pthread_mutex_t
mutex
=
PTHREAD_MUTEX_INITIALIZER;
动态分配:int
pthread_mutex_init(pthread_mutex_t
*mutex,
const
pthread_mutex_attr_t
*mutexattr);
2.
加锁。对共享资源的访问,要对互斥量进行加锁,如果互斥量已经上了锁,调用线程会阻塞,直到互斥量被解锁。
int
pthread_mutex_lock(pthread_mutex
*mutex);
int
pthread_mutex_trylock(pthread_mutex_t
*mutex);
3.
解锁。在完成了对共享资源的访问后,要对互斥量进行解锁。
int
pthread_mutex_unlock(pthread_mutex_t
*mutex);
4.
销毁锁。锁在是使用完成后,需要进行销毁以释放资源。
int
pthread_mutex_destroy(pthread_mutex
*mutex);
二、条件变量(cond)
1.
初始化条件变量。
静态态初始化,pthread_cond_t
cond
=
PTHREAD_COND_INITIALIER;
动态初始化,int
pthread_cond_init(pthread_cond_t
*cond,
pthread_condattr_t
*cond_attr);
2.
等待条件成立。释放锁,同时阻塞等待条件变量为真才行。timewait()设置等待时间,仍未signal,返回ETIMEOUT(加锁保证只有一个线程wait)
int
pthread_cond_wait(pthread_cond_t
*cond,
pthread_mutex_t
*mutex);
int
pthread_cond_timewait(pthread_cond_t
*cond,pthread_mutex
*mutex,const
timespec
*abstime);
3.
激活条件变量。pthread_cond_signal,pthread_cond_broadcast(激活所有等待线程)
int
pthread_cond_signal(pthread_cond_t
*cond);
int
pthread_cond_broadcast(pthread_cond_t
*cond);
//解除所有线程的阻塞
4.
清除条件变量。无线程等待,否则返回EBUSY
int
pthread_cond_destroy(pthread_cond_t
*cond);
三、信号量(sem)
1.
信号量初始化。
int
sem_init
(sem_t
*sem
,
int
pshared,
unsigned
int
value);
这是对由sem指定的信号量进行初始化,设置好它的共享选项(linux
只支持为0,即表示它是当前进程的局部信号量),然后给它一个初始值VALUE。
2.
等待信号量。给信号量减1,然后等待直到信号量的值大于0。
int
sem_wait(sem_t
*sem);
3.
释放信号量。信号量值加1。并通知其他等待线程。
int
sem_post(sem_t
*sem);
4.
销毁信号量。我们用完信号量后都它进行清理。归还占有的一切资源。
int
sem_destroy(sem_t
*sem);

多线程同步有哪些方法?

不同步代码如下:

package com;

public class A2
    public static void main(String[] args) throws InterruptedException 
        final Blank blank = new Blank();
        Thread thread1 = new Thread(new Runnable() 
            @Override
            public void run() 
                try 
                    blank.get(100);
                 catch (InterruptedException e) 
                    e.printStackTrace();
                
            
        );

        Thread thread2 = new Thread(new Runnable() 
            @Override
            public void run() 
                try 
                    blank.get(100);
                 catch (InterruptedException e) 
                    e.printStackTrace();
                
            
        );

        thread1.start();
        thread2.start();
        Thread.sleep(2000);
        System.out.println(blank.look());
    


class Blank
    //    账户余额
    private int money=100;

    //    存钱
    public void put(int number)
        money = money + number;
        System.out.println("存钱当前余额"+look());
    

    //    取钱
    public void get(int number) throws InterruptedException 
        if (money < number)
            System.out.println("余额不足");
            return;
        
        Thread.sleep(1000);
        money = money - number;
        System.out.println("取钱当前余额"+look());
    

    public int look()
        return money;
    


1、使用synchronized关键字。

package com;

public class A3
    public static void main(String[] args) throws InterruptedException 
        final Blank3 blank = new Blank3();
        Thread thread1 = new Thread(new Runnable() 
            @Override
            public void run() 
                try 
                    blank.get(100);
                 catch (InterruptedException e) 
                    e.printStackTrace();
                
            
        );

        Thread thread2 = new Thread(new Runnable() 
            @Override
            public void run() 
                try 
                    blank.get(100);
                 catch (InterruptedException e) 
                    e.printStackTrace();
                
            
        );

        thread1.start();
        thread2.start();
        Thread.sleep(2000);
        System.out.println(blank.look());
    


class Blank3
    //    账户余额
    private int money=100;

    //    存钱
    public synchronized void put(int number)
        money = money + number;
        System.out.println("存钱当前余额"+look());
    

    //    取钱
    public synchronized void get(int number) throws InterruptedException 
        if (money < number)
            System.out.println("余额不足");
            return;
        
        Thread.sleep(1000);
        money = money - number;
        System.out.println("取钱"+number+"当前余额"+look());
    

    public synchronized int look()
        return money;
    



2、wait和notify

package com;

public class A4 
    public static void main(String[] args) throws InterruptedException 
        GetAndSet getAndSet = new GetAndSet();
        GetThread getThread1 = new GetThread(getAndSet);
        GetThread getThread2 = new GetThread(getAndSet);
        GetThread getThread3 = new GetThread(getAndSet);
        GetThread getThread4 = new GetThread(getAndSet);
        SetThread setThread1 = new SetThread(getAndSet);
        getThread1.start();
        getThread2.start();
        getThread3.start();
        getThread4.start();
        Thread.sleep(1000);
        setThread1.start();
    


class GetThread extends Thread
    private GetAndSet getAndSet;

    public GetThread(GetAndSet getAndSet) 
        this.getAndSet = getAndSet;
    

    @Override
    public void run() 
        try 
            getAndSet.Get();
         catch (InterruptedException e) 
            e.printStackTrace();
        
    


class SetThread extends Thread
    private GetAndSet getAndSet;

    public SetThread(GetAndSet getAndSet) 
        this.getAndSet = getAndSet;
    

    @Override
    public void run() 
        try 
            getAndSet.Set();
         catch (InterruptedException e) 
            e.printStackTrace();
        
    


class GetAndSet
    private int singal;

    public synchronized void Get() throws InterruptedException 
        System.out.println("进入Get");
        if (singal != 1)
            System.out.println(Thread.currentThread().getName());
            wait();
        
        System.out.println("结束Get");
    

    public synchronized void Set() throws InterruptedException 
        System.out.println("进入Set");
        singal = 1;
        Thread.sleep(4000);
        notify();
        notifyAll();
        System.out.println("结束Set");
    



3、使用特殊域变量volatile实现线程同步。
3.使用特殊域变量(volatile)实现线程同步

  • a.volatile关键字为域变量的访问提供了一种免锁机制,
  • b.使用volatile修饰域相当于告诉虚拟机该域可能会被其他线程更新,
  • c.因此每次使用该域就要重新计算,而不是使用寄存器中的值
  • d.volatile不会提供任何原子操作,它也不能用来修饰final类型的变量
    ————————————————
    版权声明:本文为CSDN博主「北漂的橙子」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
    原文链接:https://blog.csdn.net/w893433055/article/details/85394588
package com;

public class A5
    public static void main(String[] args) 
        final Blank5 blank5 = new Blank5();
        new Thread(new Runnable() 
            @Override
            public void run() 
                blank5.setMoney(80);
            
        ).start();
        new Thread(new Runnable() 
            @Override
            public void run() 
                blank5.setMoney(80);
            
        ).start();
        new Thread(new Runnable() 
            @Override
            public void run() 
                blank5.setMoney(80);
            
        ).start();
    


class Blank5
    private volatile int money = 100;

    public void setMoney(int number)
        if (money >= number)
            money = money - number;
            System.out.println("取出了"+number+"剩余"+money);
        
            System.out.println("余额不足");
        
    



4、使用可重入锁实现线程同步。

package com;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class A6 
    public static void main(String[] args) 
        Blank6 blank6 = new Blank6();
        new Thread(new Runnable() 
            @Override
            public void run() 
                blank6.setMoney(80);
            
        ).start();
        new Thread(new Runnable() 
            @Override
            public void run() 
                blank6.setMoney(80);
            
        ).start();
        new Thread(new Runnable() 
            @Override
            public void run() 
                blank6.setMoney(80);
            
        ).start();
    


class Blank6
    private int money = 100;
    private Lock lock = new ReentrantLock();
    public void setMoney(int number)
        lock.lock();
        try
            if (money >= number)
                money = money - number;
                System.out.println("取出了"+number+"剩余"+money);
            else 
                System.out.println("余额不足");
                return;
            
        finally 
            lock.unlock();
        
    



5、使用局部变量实现线程同步。
五、使用ThreadLocal管理局部变量实现线程同步
ThreadLocal管理变量,则每一个使用该变量的线程都获得一个该变量的副本,副本之间相互独立,这样每一个线程都可以随意修改自己的副本,而不会对其他线程产生影响。
ThreadLocal类常用的方法:
1、get():返回该线程局部变量的当前线程副本中的值。
2、initialValue():返回此线程局部变量的当前线程的”初始值“。
3、remove():移除此线程局部变量当前线程的值。
4、set(T value):将此线程局部便利啊ing的当前线程副本中的值设置为指定值value。
https://blog.csdn.net/zhaojw_420/article/details/67823750
http://www.javashuo.com/article/p-sxmqgikl-hx.html

package com;

public class A7 
    public static void main(String[] args) throws InterruptedException 
        Blank7 blank7 = new Blank7();
        new Thread(new Runnable() 
            @Override
            public void run() 
                blank7.setMoney(80);
            
        ).start();
        Thread.sleep(1000);
        blank7.getMoney();
        new Thread(new Runnable() 
            @Override
            public void run() 
                blank7.setMoney(80);
            
        ).start();
        Thread.sleep(1000);
        blank7.getMoney();
        new Thread(new Runnable() 
            @Override
            public void run() 
                blank7.setMoney(80);
            
        ).start();
    


class Blank7
    private static ThreadLocal<Integer> money = new ThreadLocal<Integer>()
        @Override
        protected Integer initialValue() 
            return 100;
        
    ;

    public void getMoney()
        System.out.println(money.get());
    

    public void setMoney(int number)
        if (money.get() - number >= 0)
            money.set(money.get()-number);
            System.out.println("取出了"+number+"剩余"+money.get());
        else 
            System.out.println("余额不足");
            return;
        
    

6、使用阻塞队列实现线程同步。
7、使用原子变量实现线程同步。
https://blog.csdn.net/kehyuanyu/article/details/24797129

以上是关于Linux 线程同步都有哪些方法?的主要内容,如果未能解决你的问题,请参考以下文章

线程的线程的同步

java关键字有哪些?

多线程同步有哪些方法?

Linux 线程线程同步《三》

什么是线程安全,实现线程安全都有哪些方法

多线程