多线程的上下文安全设计

版权声明:本文为博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/weixin_42679286/article/details/96719518

多个线程进行上下文操作时会引起,线程安全问题,可通过ThreadLocal的特性,为每个线程开启副本,解决此类问题。

存放信息的对象

//存放信息的上下文对象
public class Context {
    //随便设置几个属性
    private String name;
    private String cardId;
    public void setName(String name) {
        this.name = name;
    }
    public String getName() {
        return this.name;
    }
    public void setCardId(String cardId) {
        this.cardId = cardId;
    }
    public String getCardId() {
        return cardId;
    }
}

 用ThreadLocal和单例模式为每个线程创建各自独立的上下文信息

/**
 *  ThreadLocal 为每个线程创建当前线程的副本,可保线程安全。
 */
public final class ActionContext {
    //通过线程副本存放context对象信息。
    private static final ThreadLocal<Context> threadLocal = new ThreadLocal<Context>() {
        @Override
        protected Context initialValue() {
            return new Context();
        }
    };
    //单例模式,确保线程安全。
    private ActionContext(){}
    private static class ContextHolder {
        private final static ActionContext actionContext = new ActionContext();
    }
    //调用单例方法。
    public static ActionContext getActionContext() {
        return ContextHolder.actionContext;
    }
    //调用 ThreadLocal 的get方法。
    public Context getContext() {
        return threadLocal.get();
    }
}

在一些方法中将信息塞到ThreadLocal的上下文对象中

//模拟从某处拿到信息,塞到context中。
public class QueryFromDBAction {
    //假设拿到一些数据
    public void execute() {
        String name = "Jun " + Thread.currentThread().getName();
        //把数据放入context对象中。
        ActionContext.getActionContext().getContext().setName(name);
    }
}

//模拟从某处拿到信息,塞到context中。
public class QueryFromHttpAction {
    public void execute() {
        //获取当前线程的id。
        String cardId =""+ Thread.currentThread().getId();
        //将信息放入context中。
        ActionContext.getActionContext().getContext().setCardId(cardId);
    }
}

 线程的逻辑执行单元

//逻辑执行单元
public class ExecutionTask implements Runnable {
    //假设从DB查询数据
    private QueryFromDBAction queryDbAction = new QueryFromDBAction();
    //假设从别的端口拿数据
    private QueryFromHttpAction httpAction = new QueryFromHttpAction();
    @Override
    public void run() {
        //模拟塞数据到上下文中
        queryDbAction.execute();
        //模拟塞数据到上下文中
        httpAction.execute();
        //从 ActionContext 的ThreadLocal<Context>线程副本中取出context对象
        Context context = ActionContext.getActionContext().getContext();
        System.out.println("线程名字: " + context.getName() + "   上下文ID: " + context.getCardId());
    }
}

Test_Main

    public static void main(String[] args) {
        //开启线程,传入逻辑执行单元。
        IntStream.range(1, 5)
                .forEach(i ->
                        new Thread(new ExecutionTask()).start()
                );
    }

 

展开阅读全文

多线程安全的队列

11-30

///////////////Queue.h/////////////////////////rnrn#ifndef __QUEUE_H__rn#define __QUEUE_H__rnrn/**rn*注意这个队列对于多线程是安全的rn*但是它没有很好的解决加入一个rn*指针类型的元素所带来生命周期rn*结束后带来指针指向的数据为空rn*的情况所以要安全的使用这个队rn*列必须在它外面包装上一层使其rn*安全,或者你在同一个函数里操rn*作这个队列那就不存在这个问题,rn*当然这样的程序是没有意义的rn*/rnrn#include rn#include rnrn#define CV_TIMEOUT 1rnrnrntemplaternclass Queuernrnprivate:rn typedef struct _queue rn HANDLE Guard;rn HANDLE NEmpty;rn HANDLE NFull;rn rn rn volatile DWORD Size; rn volatile DWORD first;rn volatile DWORD last;rn queue;rnrn T* MsgArray;rn queue *q;rn DWORD IsEmpty ();rn DWORD IsFull ();rnrn T Remove ();rn DWORD Insert (T value);rnrnpublic:rn Queue(DWORD size);rn ~Queue();rnrn T Get (DWORD MaxWait = INFINITE);rn DWORD Put (T value, DWORD MaxWait = INFINITE);rnrn;rn#endif //__QUEUE_H__rnrnrn//////////Queue.cpp///////////////////////rn#include "Queue.h"rnrntemplaternT Queue::Get (DWORD MaxWait)rnrn DWORD TotalWaitTime = 0;rn BOOL TimedOut = FALSE;rn T ret;rnrn WaitForSingleObject (q->Guard, INFINITE);rn while (IsEmpty () && !TimedOut) rn ReleaseMutex (q->Guard);rn WaitForSingleObject (q->NEmpty, CV_TIMEOUT);rn if (MaxWait != INFINITE) rn TotalWaitTime += CV_TIMEOUT;rn TimedOut = (TotalWaitTime > MaxWait);rn rn WaitForSingleObject (q->Guard, INFINITE);rn rnrn if (!TimedOut) rn ret = Remove ();rn rn PulseEvent (q->NFull);rn ReleaseMutex (q->Guard);rnrn return ret;rnrnrntemplaternDWORD Queue::Put (T value, DWORD MaxWait)rnrn DWORD TotalWaitTime = 0;rn BOOL TimedOut = FALSE;rnrn WaitForSingleObject (q->Guard, INFINITE);rn while (IsFull () && !TimedOut) rn ReleaseMutex (q->Guard);rn WaitForSingleObject (q->NFull, CV_TIMEOUT);rn if (MaxWait != INFINITE) rn TotalWaitTime += CV_TIMEOUT;rn TimedOut = (TotalWaitTime > MaxWait);rn rn WaitForSingleObject (q->Guard, INFINITE);rn rn rn if (!TimedOut) rn Insert (value); rnrn PulseEvent (q->NEmpty);rn ReleaseMutex (q->Guard);rn rn return TimedOut ? WAIT_TIMEOUT : 0;rnrnrntemplaternQueue::Queue(DWORD size)rnrn q = (queue*)malloc(sizeof(queue));rn rn q->first = q->last = 0;rn q->Size = size + 1;rnrn q->Guard = CreateMutex (NULL, FALSE, NULL);rn q->NEmpty = CreateEvent (NULL, TRUE, FALSE, NULL);rn q->NFull = CreateEvent (NULL, TRUE, FALSE, NULL);rnrn if ((MsgArray = (T*)calloc (size, sizeof(T))) == NULL) rn exit(1);rnrnrntemplaternQueue::~Queue()rnrn WaitForSingleObject (q->Guard, INFINITE);rn free (MsgArray);rn CloseHandle (q->NEmpty);rn CloseHandle (q->NFull);rn ReleaseMutex (q->Guard);rn CloseHandle (q->Guard);rn free(q);rnrnrnrntemplaternDWORD Queue::IsEmpty ()rnrn return (q->first == q->last);rnrnrntemplaternDWORD Queue::IsFull ()rnrn return ((q->last - q->first) % q->Size == 1);rnrnrntemplaternT Queue::Remove ()rnrn T ret = MsgArray[q->last];rn q->last = ((q->last + 1) % q->Size);rn return ret;rnrnrntemplaternDWORD Queue::Insert (T value)rnrn if (IsFull()) return 1;rn MsgArray[q->first] = value;rn q->first = ((q->first + 1) % q->Size);rn rn return 0;rn 论坛

如何安全的使用多线程???

04-09

在 vb6 中如何安全的使用多线程序rn我找一个多线程序的例子,但实在是太不稳定了,跑着跑着就挂了rn不知哪位我比较好的办法,附上我的代码rnclsThreads.clsrn------------------rnOption ExplicitrnPrivate Declare Function CreateThread Lib "kernel32" (ByVal pThreadAttributes As Any, ByVal dwStackSize As Long, ByVal lpStartAddress As Long, lpParameter As Any, ByVal dwCreationFlags As Long, lpThreadId As Long) As LongrnPrivate Declare Function ResumeThread Lib "kernel32" (ByVal hThread As Long) As LongrnPrivate Declare Function SuspendThread Lib "kernel32" (ByVal hThread As Long) As LongrnrnPublic ThreadStatus As BooleanrnPrivate Thread As LongrnPublic Property Let Enabled(ByVal vNewValue As Boolean)rnIf vNewValue = True And Me.ThreadStatus = False ThenrnResumeThread ThreadrnMe.ThreadStatus = TruernElseIf Me.ThreadStatus = True ThenrnSuspendThread ThreadrnMe.ThreadStatus = FalsernEnd IfrnEnd PropertyrnrnPublic Sub Initialize(lpfnBasFunc As Long)rn Dim dwStackSize As Longrn Dim dwCreationFlags As Longrn Dim lpThreadId As Longrn Dim lpParameter As Longrn Dim myNull As Longrn myNull = 0& 'create a null pointerrn dwStackSize = 0 '0±íʾÓÃexe stack sizern dwCreationFlags = 4 'ÓÃ4±íʾ³õʼ»¯ºóÏȲ»¼¤»î,ÈñðÈËÀ´¼¤»î.rn Thread = CreateThread(myNull, dwStackSize, lpfnBasFunc, myNull, dwCreationFlags, lpThreadId)rn If Thread = myNull Thenrn MsgBox "create thread failed"rn End IfrnEnd Subrn---------rnmodulernrnOption ExplicitrnrnPublic Sub Test1()rn Dim i As Longrn For i = 0 To 9999rn Debug.Print ("SSSSSSSSSSSSS")rn NextrnEnd SubrnPublic Sub Test2()rn Dim i As Longrn For i = 0 To 9999rn Debug.Print ("*************")rn NextrnEnd Subrnrn---rnformrn---rnDim myThread1 As New ClsThreadsrnDim myThread2 As New ClsThreadsrnrnPrivate Sub Command1_Click()rn'make new thread objectrn 'Dim myThread As New ClsThreadsrn '´´½¨ÏØ³Ç Foorn myThread1.Initialize AddressOf Test1rn '¼¤»îÏسÇrn myThread1.Enabled = Truern rnEnd SubrnrnPrivate Sub Command2_Click()rn '´´½¨ÏØ³Ç Foorn myThread2.Initialize AddressOf Test2rn '¼¤»îÏسÇrn myThread2.Enabled = TruernEnd SubrnrnPrivate Sub Command3_Click()rn myThread1.Enabled = FalsernEnd SubrnPrivate Sub Command4_Click()rn myThread2.Enabled = FalsernEnd Subrn 论坛

没有更多推荐了,返回首页