C++标准库 队列 怎么在多个函数内调用?

C++标准库 队列 怎么在多个函数内调用? 就好像全局变量一样.

3个回答

你把queue定义在函数外不就行了。。像全局变量那样

**By convention, global variables are declared at the top of a file, below the includes, but above any code. **

一个是全局变量方法,另一个是把这个队列作为函数参数传到各个函数中去使用

Csdn user default icon
上传中...
上传图片
插入图片
抄袭、复制答案,以达到刷声望分或其他目的的行为,在CSDN问答是严格禁止的,一经发现立刻封号。是时候展现真正的技术了!
其他相关推荐
C++标准库 队列 怎么在多个函数内调用?
C++标准库 队列 怎么在多个函数内调用? 就好像全局变量一样.
运行到广度优先遍历函数调用队列时就出问题
#include<iostream> using namespace std; /*********************各类定义******************************/ class Node//基本抽象数据类型 { public: char ch; //记录名称,如果将这里改成数组,结点名称可以是多个字符 int flag;//记录结点是否被访问 }; class Graph //图类,此类中,封装了图的一些成员和一些必须的成员函数 { private: int getSub(char); //获取某名称的下标 Node* arrNode; //记录名称和是否访问的数组 int numVertex,numEdge;//记录图的顶点数和边数 int **matrix; //用一个二维数组记录两点间是否相连,1相连,0断开 public: void setCh(char,int); //将数组的arrNode的每一个单元设置一个结点名称 Graph(int); ~Graph(); int getNumVertex();//获得图的顶点数 char first(char ch);//获得相邻结点 char next(char ch1,char ch2);//获得隔着ch2,但与ch2相邻的结点 void setEdge(char,char,int w=1);//设置两顶点的边和权重(权重默认为1) void eraserEdge(char,char); void outPut(); int getMark(char);//获取是否被访问的记录,已访问返回1,未访问返回0 void setMark(char);//把已访问的结点,设置标记 }; template<class T> class myQueue { private: int theFront; // 1 counterclockwise from theFront element int theBack; // position of theBack element int arrayLength; // queue capacity T *queue; // element array public: myQueue(int initialCapacity = 10); ~myQueue() {delete [] queue;} bool empty() const {return theFront == theBack;} int size() const {return (theBack - theFront + arrayLength) % arrayLength;} T& front() {// return front element /*if (theFront == theBack) throw queueEmpty();*/ return queue[(theFront + 1) % arrayLength]; } T& back() {// return theBack element /*if (theFront == theBack) throw queueEmpty();*/ return queue[theBack]; } void pop() {// remove theFront element /*if (theFront == theBack) throw queueEmpty();*/ theFront = (theFront + 1) % arrayLength; queue[theFront].~T(); // destructor for T } void push(const T& theElement); }; /*************各函数的函数体*****************************/ Graph::Graph(int n) { int i,j; arrNode=new Node[n]; numVertex=n; numEdge=0; for(i=0;i<numVertex;i++) arrNode[i].flag=0; matrix=new int*[numVertex]; for(i=0;i<numVertex;i++) matrix[i]=new int[numVertex]; for(i=0;i<numVertex;i++) for(j=0;j<numVertex;j++) matrix[i][j]=matrix[j][i]=0; } Graph::~Graph() { delete [] arrNode; for(int i=0;i<numVertex;i++) delete [] matrix[i]; delete [] matrix; } int Graph::getSub(char ch) //获取下标 { for(int i=0;i<numVertex;i++) if(ch==arrNode[i].ch) return i; } int Graph::getNumVertex() //获取顶点数 { return numVertex; } char Graph::first(char ch) //获取相邻结点 { for(int i=0;i<numVertex;i++) if(matrix[getSub(ch)][i]!=0) return arrNode[i].ch; return ch; } char Graph::next(char ch1,char ch2) //获取与ch2相邻的结点 { for(int i=getSub(ch2)+1;i<numVertex;i++) if(matrix[getSub(ch1)][i]!=0) return arrNode[i].ch; return ch1; } void Graph::setEdge(char ch1,char ch2,int w) //设置边 { if(matrix[getSub(ch1)][getSub(ch2)]==0) { numEdge++; matrix[getSub(ch1)][getSub(ch2)]=matrix[getSub(ch2)][getSub(ch1)]=1; } } void Graph::eraserEdge(char ch1,char ch2) { if(matrix[getSub(ch1)][getSub(ch2)]==1) { numEdge--; matrix[getSub(ch1)][getSub(ch2)]=matrix[getSub(ch2)][getSub(ch1)]=0; } } void Graph::outPut() { for(int i=0;i<numVertex;i++) { for(int j=0;j<numVertex;j++) { cout<<matrix[i][j]<<" "; } cout<<endl<<endl;; } } int Graph::getMark(char ch)//获取访问信息 { for(int i=0;i<numVertex;i++) if(ch==arrNode[i].ch) return arrNode[i].flag; return -1; } void Graph::setMark(char ch) //设置标记 { for(int i=0;i<numVertex;i++) if(ch==arrNode[i].ch) arrNode[i].flag=1; } void Graph::setCh(char ch,int n)//将结点名称设置在字符中 { arrNode[n].ch=ch; } template<class T> myQueue<T>::myQueue(int initialCapacity) {// Constructor. /*if (initialCapacity < 1) {ostringstream s; s << "Initial capacity = " << initialCapacity << " Must be > 0"; throw illegalParameterValue(s.str()); }*/ arrayLength = initialCapacity; queue = new T[arrayLength]; theFront = 0; theBack = 0; } template<class T> void myQueue<T>::push(const T& theElement) {// Add theElement to queue. // increase array length if necessary if ((theBack + 1) % arrayLength == theFront) {// double array length // allocate a new array T* newQueue = new T[2 * arrayLength]; // copy elements into new array int start = (theFront + 1) % arrayLength; if (start < 2) // no wrap around copy(queue + start, queue + start + arrayLength - 1, newQueue); else { // queue wraps around copy(queue + start, queue + arrayLength, newQueue); copy(queue, queue + theBack + 1, newQueue + arrayLength - start); } // switch to newQueue and set theFront and theBack theFront = 2 * arrayLength - 1; theBack = arrayLength - 2; // queue size arrayLength - 1 arrayLength *= 2; queue = newQueue; } // put theElement at the theBack of the queue theBack = (theBack + 1) % arrayLength; queue[theBack] = theElement; } /***************主函数*********************/ void DFS(Graph*,char); //深度优先遍历函数声明 void BFS(Graph*,char,myQueue*); //广度优先遍历函数声明 int main() { char option; int numVer,numE,i; char temp1,temp2; //temp1,temp2作临时变量,记录输入的值 // myQueue* q=new myQueue; cout << "输入定点数和弧数:"; cin >> numVer >> numE; Graph myGraph1(numVer); Graph myGraph2(numVer); cout << "请输入" << numVer << "个顶点:\n"; for(i=0;i<numVer;i++) //将结点名称设置在数组中 { cout << "输入顶点" << i << ":"; cin >> temp1; myGraph1.setCh(temp1,i); myGraph2.setCh(temp1,i); } cout << "请输入" << numE << "条弧:\n"; for(i=0;i<numE;i++) //设置边 { cout << "输入弧" << i << ":"; cin >> temp1 >> temp2; myGraph1.setEdge(temp1,temp2); myGraph2.setEdge(temp1,temp2); } myGraph1.outPut(); while(1) { cout<<"2:删除边 "<<endl; cout<<"3: 深度遍历"<<endl; cout<<"4: 广度遍历"<<endl; cout<<"0: 退出"<<endl; cout<<"\n请输入你的选择:"; cin>>option; switch(option) { case'0': exit(0); break; case'2': { cout << "输入弧的顶点:" << endl; cin >> temp1 >> temp2; myGraph1.eraserEdge(temp1,temp2); myGraph2.eraserEdge(temp1,temp2); myGraph1.outPut(); break; } case'3': { cout << "深度优先结果:"; DFS(&myGraph1,'a'); cout<<endl; break; } case'4': { cout << "\n广度优先结果:"; //BFS(&myGraph2,'a',q); cout<<endl; break; } cout<<endl; } }cout<<endl; } void DFS(Graph* G,char ch) //深度优先遍历函数体 { int i=0; cout << ch << " "; G->setMark(ch); for(char w=G->first(ch);i<G->getNumVertex();w=G->next(ch,w),i++) if(G->getMark(w)==0) DFS(G,w); } void BFS(Graph* G,char ch,myQueue* q) //广度优先遍历函数体 { char v,w; q->push(ch); G->setMark(ch); while(q->size()!=0) { q->pop(v); cout << v << " "; int i=0; for(w=G->first(v);i<G->getNumVertex();w=G->next(v,w),i++) if(G->getMark(w)==0) { G->setMark(w); q->push(w); } } } /*输入顶点数和弧数:8 9 输入8个顶点. 输入顶点0:a 输入顶点1:b 输入顶点2:c 输入顶点3:d 输入顶点4:e 输入顶点5:f 输入顶点6:g 输入顶点7:h 输入9条弧. 输入弧0:a b 1 输入弧1:b d 1 输入弧2:b e 1 输入弧3:d h 1 输入弧4:e h 1 输入弧5:a c 1 输入弧6:c f 1 输入弧7:c g 1 输入弧8:f g 1 */
来个阻塞队列的代码实现,谢谢
请教一下各位,我想实现阻塞队列的功能,比如所有个队列对象gQueue,它有两个函数qin(),qout(), 我有多个线程需要写一个文件,这时候需要给这几个线程写文件的地方排队,大概代码如下 //线程内部 gQueue.qIn(); writeFile(); gQueue.qOut(); //线程内部 一个线程writeFile的时候,另外的线程再qin里面按先进先出顺序排队,当这个线程调用qout的时候队列里面按先进先出顺序使得最先进去的下一个线程能操作文件, 我自己写过一些方式,但是多次运行以后可能会有排队错误,麻烦各位高手不吝赐教,谢谢,顺便说一下简单使用互斥量是不能实现的,那家伙只再两个线程间有用
MFC循环创建多线程同时处调用同一函数读写文本和excel操作
CString fname[3000]; CString name[1024] ; int j1 ; int nIndex = 0; void CDisposeXJXDlg::OnBtnSelSrcFile() { // TODO: Add your control notification handler code here CFileDialog dlg(TRUE, NULL, NULL ,OFN_HIDEREADONLY| OFN_OVERWRITEPROMPT |OFN_ALLOWMULTISELECT,"(*.tmp) | *.tmp||",AfxGetMainWnd()); TCHAR* pszFile =new TCHAR[MAX_PATH]; memset(pszFile,0,sizeof(TCHAR)*MAX_PATH); dlg.m_ofn.lpstrFile = pszFile ; if(IDOK==dlg.DoModal()) { int i = 0 ; POSITION pos ; //把起始位置赋给pos pos = dlg.GetStartPosition(); //把打开的多个文件名赋给fname数组 while(pos!=NULL) { fname[i] = dlg.GetNextPathName(pos); name[i] = fname[i].Right(fname[i].GetLength()-fname[i].ReverseFind('\\')-1); m_ListOutInfo.AddString(name[i]); i++ ; } } } int t=0 ; int tt = 0 ; int TheradsCount = 0; int j2 = 0; int j3 = 0; CWinThread* pThread[MAX_THREADS]; void CDisposeXJXDlg::OnBtnTypeset() { GetDlgItem(IDC_BTN_TYPESET)->EnableWindow(FALSE); TheradsCount = m_ListOutInfo.GetCount(); j2 = TheradsCount ; for(int m1=0 ;j2<MAX_THREADS? m1<j2 :m1<MAX_THREADS ;m1++) { pThread[m1] =AfxBeginThread(CDisposeXJXDlg::ThreadFun,this,THREAD_PRIORITY_NORMAL,0,CREATE_SUSPENDED); pThread[m1]->ResumeThread(); } } UINT CDisposeXJXDlg::ThreadFun(LPVOID pParam) { CDisposeXJXDlg* dlg=(CDisposeXJXDlg*)pParam; WaitForSingleObject(semaphoreWrite.m_hObject,INFINITE); dlg->m_strFbdPath = fname[t]; dlg->StartDispose(fname[t]); delete []dlg ; Sleep(100) ; t++ ; ReleaseSemaphore(semaphoreWrite.m_hObject,1,NULL); return 0 ; } 这是我自己写的,在MFC对话框里添加了一个列表框,打开多个文本然后点击按钮执行OnBtnTypeset()函数,最后想通过创建多个线程,同时调用StartDispose(fname[t])来处理多个文本和处理EXCEL,做了几天了出来的结果就是线程只能按队列执行,文本读取或EXCEl处理一两个对话框就结束了或者内存泄露,完全达不到任务要求, 求大神们给指点,真心求教....谢谢了!
多线程互斥失败的问题
请教一下各位,我有个地方需要多个线程排队访问某个资源的问题,我有个排队 对象 queue,他有两个函数queueIn(进队列),和queueOut(出队列), 为了简单起见我这里只用了两个线程去排队,queue里面有个mutex用于互斥,queueIn里面会去lock这个互斥量,queueOut会去unlock这个互斥量,程序启动 了以后两个线程会在自己的run函数(qt,其他的类似)里面去调用queue.queueIn()进行排队,因为有互斥量的存在按道理一个线程会lock成功,而另一个线程会阻塞在里面直到前一个线程调用queueOut释放掉互斥量,但是呢结果是这两个线程居然都lock互斥量成功了,都直接通过了这个阻塞函数,互斥量居然没起到他的作用,请教一下各位大神,这种情况该怎么处理,我就是想弄个阻塞队列,让多个线程阻塞在queueIn这里,等待上一个线程资源访问完成再依次序通过,但是在目前两个线程这种简单情况下互斥量是类成员,居然表现得像是函数的局部变量一样,没起到互斥作用,请帮助解决一下,谢谢
QMetaObject::invokeMethod 返回true,但是对应的slot函数没有执行
先上代码: ``` QMetaObject::invokeMethod(m_manager, "onEventRequest", Qt::QueuedConnection, Q_ARG(int, 0)); ``` 经过调试发现invokeMethod返回true,但是却没有执行对应的onEventRequest函数. 这些参数中: m_manager是在主线程中创建的,onEventRequest的声明: public slots: void onEventRequest(int idx); 执行invokeMethod的线程是由QThread::Run创建的 如果参数换成Qt::DirectConnection是可以调用到函数onEventRequest的声明的,但是这样就是同一个线程执行了... 我在onEventRequest函数中加了断点,发现没有执行进这个函数 请问这样的情况如何调试? 之前有没有碰到过这样的问题? 返回true的话就是说event已经在消息队列里了,那是不是有可能因为什么原因导致阻塞了消息队列?
python multiprocessing 问题
最近写一个简单的web后端项目(结构简单,并发不高,但是处理逻辑稍复杂),在某一个url路由的响应函数(Run)中,处理逻辑是每次调用到Run,都启动一个子进程来处理逻辑,子进程完了之后通过multiprocessing.Queue将结果反馈给父进程(父进程收到消息表示子进程完成任务并准备结束了)。 ```python Run函数中的流程: 1. q = Queue() #创建queue 2. p = Process(target=run_child,args=(q)) #创建处理业务的子进程,run_child函数耗时较长 3. parent_listen_q(p,q) #这个函数中以gevent协程的方式不停循环从q中get消息,同时join子进程 parent_listen_q函数流程: def _inner(): 1. while True: #循环收消息 msg = q.get_nowait(q) if msg: break gevent.sleep(0.01) 2. handle(msg) #处理消息 3. p.join() #避免僵尸 gevent.spawn(_inner) #协程 ``` 这个逻辑看起来没有问题,并且很简洁(连queue都是每个子进程分开的互不影响),但是问题就出在每个子进程一个queue上。 一个场景是: 用户接连调用了两次Run,期间间隔很短,子进程的生命周期长于这个间隔。会启动了两个子进程,记为c1,c2,父进程记为p。 第一次调用Run的时候,关系是:父p,子c1,队列q1。 第二次调用Run的时候,关系是:父p,子c2,队列q2,但此时c1还未结束,父进程的parent_listen_q协程也还在运行中,c2会继承这个运行的协程, 导致的问题是有两个进程(p,c2)同时在循环从q1中get数据,当后续c1结束向q1中发消息时,有可能消息被c2抢先取到,而真正关心这个消息的p却收不到消息了,这样就出了问题。 出现这个问题的根本原因有两个: 1. fork子进程会继承父进程的栈空间,导致了协程也被继承下来了。 2. queue底层其实也是基本的ipc对象,看Queue的代码是pipe实现的,在fork的时候也会被继承,并且,如果不继承的话反而实现不了ipc了。 所以,上面这种结构的处理逻辑对这个问题就不是蛮好解决了,看起来不能给queue设置一个“close_on_exec”的标志。 我在这里暂时用了一个很挫的方法曲线解决了这个问题(确实挺搓,因为不是避免,而是修补): ```python 修补后的parent_listen_q函数流程: def _inner(): 1. while True: msg = q.get_nowait() if p._parent_pid != os.getpid(): #如果是子进程 if msg: q.put_nowait(msg) #如果发现被子进程不小心收错了,重新塞回去(汗。。) return #直接结束这个冒牌货协程 if msg: break gevent.sleep(0.01) 2. handle(msg) 3. p.join() #如果在非父进程中,这行会报异常 gevent.spawn(_inner) ``` 在这里向各位大神请赐教,不知有没有更优雅的方法解决这个问题,本人新手,求带。
VB6.0使用iphlpapi.dll,很多的函数返回122,怎么解决?
(122 缓冲区不是足够大) 代码如下 第二节 获取MIB-II 接口表 1、函数: Private Declare Function GetIfTable Lib "iphlpapi.dll" (ByRef pIfTable As MIB_IFTABLE, ByRef pdwSize As Long, ByVal bOrder As Long) As Long 参数说明: pIfTable:[输入]成功的话指向一个MIB_IFTABLE类型的缓冲区。 PdwSize:[输入,输出]指定pIfTable参数所占缓冲区的大小,如果缓冲区不是足够大返回接口表,函数设置这个参数等于所必须的缓冲区大小。 bOrder:[输入]指定返回的接口表是否按接口序号按上升顺序排列。如果参数为TRUE那么按上升顺序排列。 返回值:成功,返回0;失败,返回错误代码。 2、使用到的类型: ①MIB_IFTABLE 包含接口表 Private Type MIB_IFTABLE dwNumEntries As Long '当前网络接口的总数 MIB_Table(9) As MIB_IFROW '指向一个包含MIB_IFROW类型的指针 End Type ②MIB_IFROW 保存接口信息 Private Type MIB_IFROW wszName(0 To 511) As Byte '接口名称的Unicode字符串,必须为512字节 dwIndex As Long '接口编号 dwType As Long '接口类型,参看IP_ADAPTER_INFO类型的Type成员 dwMtu As Long '最大传输单元 dwSpeed As Long '接口速度(字节) dwPhysAddrLen As Long '由bPhysAddr获得的物理地址有效长度 bPhysAddr(0 To 7) As Byte '物理地址 dwAdminStatus As Long '接口管理状态 dwOperStatus As Long '操作状态,以下值之一: 常量名称 值 说明 MIB_IF_OPER_STATUS_NON_OPERATIONAL 0 网络适配器被禁止,例如:地址冲突 MIB_IF_OPER_STATUS_UNREACHABLE 1 没有连接 MIB_IF_OPER_STATUS_DISCONNECTED 2 局域网:电缆未连接;广域网:无载波信号 MIB_IF_OPER_STATUS_CONNECTING 3 广域网适配器连接中 MIB_IF_OPER_STATUS_CONNECTED 4 广域网适配器连接上远程对等点 MIB_IF_OPER_STATUS_OPERATIONAL 5 局域网适配器默认状态 dwLastChange As Long '操作状态最后改变的时间 dwInOctets As Long '总共收到(字节) dwInUcastPkts As Long '总共收到(unicast包) dwInNUcastPkts As Long '总共收到(non-unicast包),包括广播包和多点传送包 dwInDiscards As Long '收到后丢弃包总数(即使没有错误) dwInErrors As Long '收到出错包总数 dwInUnknownProtos As Long '收到后因协议不明而丢弃的包总数 dwOutOctets As Long '总共发送(字节) dwOutUcastPkts As Long '总共发送(unicast包) dwOutNUcastPkts As Long '总共发送(non-unicast包),包括广播包和多点传送包 dwOutDiscards As Long '发送丢弃包总数(即使没有错误) dwOutErrors As Long '发送出错包总数 dwOutQLen As Long '发送队列长度 dwDescrLen As Long ' bDescr部分有效长度 bDescr(0 To 255) As Byte '接口描述 End Type 3、使用到的常量: 无 4、主要代码分析: ①分配足够大的缓存空间,然后直接调用GetIfTable获得接口信息: Dim ifT As MIB_IFTABLE LenIfT = Len(ifT) RValue = GetIfTable(ifT, LenIfT, True) ②由ifT.dwNumEntries获得接口数量,然后用for…next循环获取每个接口信息: For i = 0 To ifT.dwNumEntries - 1 Print "接口编号:" & ifT.MIB_Table(i).dwIndex Print "接口速度:" & ifT.MIB_Table(i).dwSpeed Next ③物理地址的获得: PhysAddr = Right("00" & Hex(ifT.MIB_Table(i).bPhysAddr(0)), 2) For j = 1 To 5 PhysAddr = PhysAddr & "-" & Right("00" & Hex(ifT.MIB_Table(i).bPhysAddr(j)), 2) Next Print "物理地址:" & PhysAddr
LINUX网卡驱动用iperf测试UDP丢包
大神们好,小弟最近做了一个LINUX网卡驱动,用iperf来测试时候,发现UDP有丢包。 用iperf测试UDP,一共测试两次,一次一个小时,速率为1000M,丢包率大概在0.0001%左右 再继续测试时,发现会大量丢包,已经惨不忍睹了,不知道什么问题,现在把收发代码贴出来,请大家指正一下: /*发送回调函数*/ ``` netdev_tx_t net_tx (struct sk_buff *skb, struct net_device *dev) { NET_PRIV *net_priv = netdev_priv(dev); unsigned long flags; PNET_SKB_DATA pskb ; spin_lock_irqsave(&net_priv->send_lock, flags); /*list_skbPool pool中取一个pskb*/ pskb = (PNET_SKB_DATA)listGetFirstNodeFromList(&net_priv->dev->list_skbPool); /*如果没取到,则返回*/ if(pskb == NULL) { spin_unlock_irqrestore(&net_priv->send_lock, flags); return NETDEV_TX_BUSY; } /*取到一个pskb后,把这个pskb从队列list_skbPool移除*/ pskb = listRemoveNodeFromListHead(&net_priv->dev->list_skbPool); /*把skb挂在pskb中*/ pskb->skb = skb; /*把pskb插入队列list_skbSend尾部,等待另外一个线程来取数据,继而发送出去*/ listAddNodeToListTail(pskb, &net_priv->dev->list_skbSend); dev->trans_start = jiffies; up(net_priv->dev->semElsSend); spin_unlock_irqrestore(&net_priv->send_lock, flags); return NETDEV_TX_OK; } ``` 此时另外有一个线程,在不断取数据,取到后就发送出去。 接收时,是一个线程一直从链路上收数据,收到数据就调用下面这个接收函数 /*接收函数*/ ``` int net_rx(unsigned char *data, unsigned int len, struct net_device *dev) { struct sk_buff *skb = NULL; NET_PRIV *net_priv = netdev_priv(dev); unsigned long flags; /*分配套接字缓冲区*/ skb = dev_alloc_skb(len + 2); if(skb == NULL) { printk("%s:memory is not sufficient, rcvd packet dropped.\n", dev->name); dev->stats.rx_dropped++; return -1; } skb_reserve(skb, 2); skb->dev = dev; memcpy(skb_put(skb, len), data, len); skb->protocol = eth_type_trans(skb, dev); if(NET_RX_SUCCESS != netif_receive_skb(skb)) { dev->stats.tx_errors ++; dev->stats.tx_dropped ++; return -2; } /*接收包统计*/ dev->stats.rx_packets ++; dev->stats.rx_bytes += len; return 0; } ``` 之前加了一些统计打印,发现从链路及驱动里面没有丢包。 丢包发生在送给协议栈后,在协议栈丢掉了 通过命令netstat -su发现打印如下: Udp: 30681696 packets received 71 packets to unknown port received. 436 packet receive errors 266 packets sent 436 receive buffer errors 0 send buffer errors IgnoredMulti: 393 其中436 packet receive errors 这个统计值436,正好就是iperf统计的丢包数量。 在网上搜了很多,说是当出现packet receive errors 的时候,说明是应用层来不及接收导致的。但是我用两台PC机(linux系统)进行测试,没有丢包。 这个问题困惑了很久,希望得到大神指点,谢谢!
Servlet实现排队处理视频转换请求中的疑问,贴代码请斧正!
项目需求:客户端上传视频(*.avi),server将请求排队,然后一个一个排队处理转换任务. 前面问过该问题,现在自己实现,由于多线程不精通. 如下实现该排队需求,自己感觉不太准确,有问题,请指出,并给个解决方法. (tip,没有servlet的单线程模式或将dopost方法synchronied,视乎这样做是由系统将请求排队,request请求不能立即得到回复! :) ) 1.TaskBean 写了一个bean,这个bean用来存储了转换任务的属性. [code="java"]package videoconvert; public class TaskBean { private String videoPath; private String flvPath; private boolean isConverting; public String getVideoPath() { return videoPath; } public void setVideoPath(String videoPath) { this.videoPath = videoPath; } public String getFlvPath() { return flvPath; } public void setFlvPath(String flvPath) { this.flvPath = flvPath; } public boolean getIsConverting() { return isConverting; } public void setIsConverting(boolean isConverting) { this.isConverting = isConverting; } }[/code] 2.VideoConvert 这是个servlet,用来排队请求,并调度转换工具进行转换. [code="java"]package videoconvert; import java.io.IOException; import java.io.PrintWriter; import java.util.Collections; import java.util.LinkedList; import java.util.List; import java.util.Timer; import java.util.TimerTask; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class VideoConvert extends HttpServlet { private static final long serialVersionUID = 6312986385178354217L; private static Logger logger = LoggerFactory.getLogger(VideoConvert.class); private static LinkedList<TaskBean> taskStack = new LinkedList<TaskBean>(); public VideoConvert() { super(); } @Override public void init() throws ServletException { // TODO Auto-generated method stub super.init(); new Timer().schedule(new TimerTask() { public void run() { checkTaskList();[color=red]//这里按时间间隔检查栈.并取出task来执行.[/color] } }, 0, 10000); } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doPost(request, response); } protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { if (request.getRemoteAddr().equals("127.0.0.1")) { TaskBean task = new TaskBean(); String videoPath = (String) request.getAttribute("VideoPath"); String flvPath = (String) request.getAttribute("FlvPath"); if (null != videoPath && null != flvPath) { task.setVideoPath(videoPath); task.setFlvPath(flvPath); task.setIsConverting(false); synchronized (taskStack) { [color=red] //多线程以taskStack栈作为同步量,当持有taskStack对象才添加任务进栈[/color] taskStack.addLast(task); logger.info("add task to queue!"); } } else { logger.info("the paramters are null!"); } } else { logger.info("visit is illegal!"); } response.setContentType("text/html; charset=UTF-8"); PrintWriter pw = response.getWriter(); pw.write("上传视频成功,等待转换....") pw.flush(); } //这个是timer触发的任务,取出stack中的一个任务,然后检查是否在转换中,如果没有就调用工具进行转换. private void checkTaskList() { synchronized (taskStack) { [color=red]//这里感觉不准确,因为同步块里面,Timer新建的了一个线程p1来调用外 //部工具 // FFmpeg.并且会(process.waitFor();)等待FFmepg运行完成来返回结果. 所以在调 // 用外部工具的整个过程都占用了同步量(taskStack).所以应该如何改进...[/color] TaskBean task = taskStack.peekFirst(); if (null != task) { if (!task.getIsConverting()) { task.setIsConverting(true); executProcess(task); } } } } private void executProcess(TaskBean task) { MediaUtility mu = new MediaUtility(); //在类MediaUtility 中调用新建线程调用工具,并waitfor()结果 if (mu.video2Flv(task.getVideoPath(), task.getFlvPath())) { taskStack.removeFirst(); logger.info("achieve video convert,removeFirst task!"); } else { logger.error("video convert fail!"); taskStack.removeFirst(); } } }[/code] 3.调用工具的类 MediaUtility [code="java"]package videoconvert; import java.io.BufferedReader; import java.io.File; import java.io.FileOutputStream; import java.io.FileWriter; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class MediaUtility { private static Logger logger = LoggerFactory.getLogger(MediaUtility.class); public boolean video2Flv(String videoPath, String flvPath) { if (!checkfile(videoPath)) { logger.error(videoPath + " is not file!"); return false; } String cmdStr = "ffmpeg -i " + videoPath + " " + flvPath; logger.info(cmdStr); logger.info("Starting convert video to flv...."); BufferedReader ffmpegOut = null; try { Process process = Runtime.getRuntime().exec(cmdStr); // ffmpegOut = new BufferedReader(new InputStreamReader(process // .getInputStream())); ffmpegOut = new BufferedReader(new InputStreamReader(process .getErrorStream())); // FileWriter fileOut = new FileWriter(new File("c:/F.txt")); String dLine = ""; while ((dLine = ffmpegOut.readLine()) != null) { logger.info(dLine); // fileOut.write(dLine); } // fileOut.close(); process.waitFor(); logger.info("***************************** end convert video *************************"); ffmpegOut.close(); return true; } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); return false; } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); return false; } finally { try { if (null != ffmpegOut) { ffmpegOut.close(); } } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } public static boolean checkfile(String path) { File file = new File(path); if (!file.isFile()) { return false; } return true; } // public static void main(String[] args) { // boolean bb = video2Flv("d:/ffmpeg/uploadVideo/Video1.wmv", // "d:/ffmpeg/convertedVideo/pp1.flv"); // logger.info("the bb result is " + bb); // boolean cc = video2Flv("d:/ffmpeg/uploadVideo/Video2.wmv", // "d:/ffmpeg/convertedVideo/pp2.flv"); // logger.info("the cc result is " + cc); // }[/code] 如上面的代码.上面的代码每次只能有一个视频在转换,如果想改进为最多同时有3个或者6个任务在执行呢? 此时是否要再设置一个信号量呢? [b]问题补充:[/b] [quote]放一个线程池(ThreadPool),可以解决你的问题,每次把你的任务丢到线程池里面。[/quote] 只知道连接数据库用到线程池.请求排队也可以用线程池?有这样的应用吗? 8) 并且具体如何用,能给个例子吗? [b]问题补充:[/b] [quote]jdk的cocurrent里面有现成的。 Java代码 queue = new LinkedBlockingQueue<Runnable>(1000); threadPool = new ThreadPoolExecutor(5, 10, 10, TimeUnit.SECONDS, queue); 这样就定义了一个线程池。 Java代码 threadPool.execute(runnable); 这样就放入线程池一个Runnable了,就可以满足你的需求了。[/quote] 感谢taopian的热心回复,马上看jdk的cocurrent. :) [b]问题补充:[/b] [quote]jdk的cocurrent里面有现成的。 [/quote] 谢谢了,cocurrent包,感觉很好,应该就是解决问题的办法了. :idea: 另外,我还有一个不好很明白,就是 ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, RejectedExecutionHandler handler) 构造函数中的 BlockingQueue<Runnable> workQueue的作用? 感觉JVM可以在内部维护这个队列,不用释放出接口来. 当然尽管API上讲了" 大队列 用小池, 小队列 用大池," 可以提高资源利用率.然而现在还不是很理解.后面在实践中体会了. 嘿嘿 [b]问题补充:[/b] 先问再吃饭. :D [quote]BlockingQueue<Runnable> workQueue 这个就是放一个队列,任务扔到队列里面,可以对并发做控制。 把这个队列暴露出来,能够让你进行很好的控制,并且你可以继承BlockingQueue,来加入你自己需要的功能,:)。[/quote] 嗯,是的,可以自己继承BlockingQueue,再增加一些自己的功能,然后放入executor中去. 面向接口 :) 现在我已经可以顺利,并准确的运行了. 最后一个疑问: executor.excute(new Runnable(){ public void run(){ run 中的变量要求是final的.final变量的GC时间是否会有影响呢? 或者有别的更好解决方法 } } ) 我是这样的改的: 这个bean被迫为final的了.呵呵,有更好的方法吗? [code="java"]if (null != videoPath && null != flvPath) { final TaskBean task = new TaskBean(); task.setVideoPath(videoPath); task.setFlvPath(flvPath); threadPool.execute(new Runnable(){ public void run(){ MediaUtility.video2Flv(task.getVideoPath(), task.getFlvPath()); } });[/code] 哎呀,罗嗦了,吃饭先..... :D
请教一个JVM内存优化的问题
<p>自己写的一个WEB爬虫程序,程序方面的优化我自己感觉做的不错了,各种对象的使用都想办法不让内存浪费。 <br />但还是吃内存吃的很厉害 <br />我想主要的消耗在于,对于每一个爬下来的网页,都要变成String在内存中分析超链接,这个估计很大。 <br />另外对于每一个分析到的URL,都需要保存起来。 <br /><br /><br />我当前使用的内存优化参数如下 <br />-Xms1024M -Xmx1024M -Xmn512M -Xss128K -XX:+UseParallelGC -XX:ParallelGCThreads=20 -XX:+UseParallelOldGC -XX:MaxPermSize=128M -XX:NewSize=128M -XX:MaxNewSize=128M -XX:SurvivorRatio=8 <br /><br />机器内存是1G。。上面那堆参数,除了最大最小堆内存我也是不求甚解,不是非常明白我设置了什么。网上搜答案也是五花八门,希望大家能推荐一个内存优化参数的文章 <br />另外我上面设置的参数有哪些方面可以优化,或者哪些地方写的不对。 <br /><br />核心的代码我都贴到下面:(我是刚入门的菜鸟,大家别笑话我写的差哦) <br /><br />这个是主类,负责控制,那个monitor大家就当做System.out.println,其实就是</p> <pre name="code" class="java">package com.wood.core; import java.util.concurrent.*; import java.util.*; import com.wood.core.monitor.*; public class CrawlerController implements Runnable { //一个类,用来管理URL,由各抓取线程共享 private URLMap urlmap; //已成功处理URL总数 private int count; //目标完成任务总数 private int total; //管理所有的抓取线程,必要时进行关闭,由于抓取线程存在I/O读取,而I/0读取中的线程是不可中断的,需调用线程的cancel函数 private Crawler[] crawlers; //线程工厂,可以监视产生的线程工作状态 private CrawlerFactory factory; //线程数目 private int threadMax; //当前活动线程 private int threadActive; //线程执行器 private ExecutorService exec; //当系统关闭时取消 private boolean canceled; public CrawlerController(String seed) { this(seed,20,10000); } public CrawlerController(List&lt;string&gt; seeds) { this(seeds,20,10000); } public CrawlerController(String seed,int threadNum) { this(seed,threadNum,10000); } public CrawlerController(List&lt;string&gt; seeds,int threadNum) { this(seeds,threadNum,10000); } public CrawlerController(String seed,int threadNum,int total) { //初始化URLMap,保存抓取启始位置 urlmap=new URLMap(seed); init(threadNum, total); } public CrawlerController(List&lt;string&gt; seeds,int threadNum,int total) { urlmap=new URLMap(seeds); init(threadNum, total); } //构造 private void init(int threadNum,int total) { //初始完成URL总数为0 count=0; //设置目标完成总数 this.total=total; threadMax=threadNum; threadActive=threadNum; //初始化工作线程 crawlers=new Crawler[threadMax]; String id="Crawler "; for(int i=0;i&lt;threadmax;i++) crawlers[i]="new" crawler(id+(i+1),this,urlmap);="" factory="new" crawlerfactory();="" exec="Executors.newCachedThreadPool(factory);" canceled="false;" }="" 检查当前工作线程状态,并打印系统状态="" private="" boolean="" check()="" {="" if(canceled)="" return="" false;="" int="" count="getCount();" if(count=""&gt;total) { MonitorHolder.getMonitor().print("已抓取"+COUNT+"页面,完成目标任务"+total+"页面\n"); cancel(); return false; } threadActive=factory.getActive(); /*if(threadActive&lt;=0) { MonitorHolder.getMonitor().print("无活动工作线程,抓取任务提前结束\n"); cancel(); return false; }*/ MonitorHolder.getMonitor().print_status("统计信息:成功抓取"+COUNT+"页面,当前活动线程为"+threadActive+"个\n"); return true; } //结束抓取 public void cancel() { //调用每个抓取线程的离开方法 for(Crawler cw:crawlers) cw.cancel(); //销毁工厂 factory.destory(); exec.shutdownNow(); MonitorHolder.getMonitor().print("成功结束抓取工作,共抓取"+getCount()+"页面\n"); this.canceled=true; } public synchronized void count() { count++; } public synchronized int getCount() { return count; } public int getTotal() { return total; } public URLMap getMap() { return urlmap; } @Override public void run() { while(!Thread.currentThread().isInterrupted() &amp;&amp; !canceled) { try { MonitorHolder.getMonitor().print("初始化完毕\n"); MonitorHolder.getMonitor().print("开始抓取工作\n"); for(Crawler cw:crawlers) exec.execute(cw); int check_count=0; while (check()){ TimeUnit.SECONDS.sleep(5); check_count++; if(check_count==24) { //每2分钟把待处理URL队列打乱一次 urlmap.shuffle(); MonitorHolder.getMonitor().print("控制信息!!!待抓取URL顺序将打乱\n"); check_count=0; System.gc(); } } } catch (InterruptedException e) { MonitorHolder.getMonitor().print("抓取工作被中断\n"); cancel(); } } } public static void main(String[] args) { List&lt;string&gt; seeds=new ArrayList&lt;string&gt;(15); //问问的各个分类 seeds.add("http://wenwen.soso.com/"); seeds.add("http://wenwen.soso.com/z/c1879048192.htm"); seeds.add("http://wenwen.soso.com/z/c1090519040.htm"); seeds.add("http://wenwen.soso.com/z/c1627389952.htm"); seeds.add("http://wenwen.soso.com/z/c855638016.htm"); seeds.add("http://wenwen.soso.com/z/c1191182336.htm"); seeds.add("http://wenwen.soso.com/z/c1191182336.htm"); seeds.add("http://wenwen.soso.com/z/c620756992.htm"); seeds.add("http://wenwen.soso.com/z/c553648128.htm"); seeds.add("http://wenwen.soso.com/z/c385875968.htm"); seeds.add("http://wenwen.soso.com/z/c687865856.htm"); seeds.add("http://wenwen.soso.com/z/c16777216.htm"); seeds.add("http://wenwen.soso.com/z/c318767104.htm"); seeds.add("http://wenwen.soso.com/z/c150994944.htm"); seeds.add("http://wenwen.soso.com/z/c922746880.htm"); seeds.add("http://wenwen.soso.com/z/c83886080.htm"); CrawlerController controller=new CrawlerController(seeds,5,2000000); List&lt;string&gt; format=new ArrayList&lt;string&gt;(10); //问问回答问题的格式 format.add("http://wenwen.soso.com/z/q"); controller.getMap().addLimit(format); Thread thread=new Thread(controller); thread.start(); } } </pre> <p><br /></p> <pre name="code" class="java">package com.wood.core; import java.util.*; import java.util.concurrent.TimeUnit; import java.util.regex.*; import java.io.*; import java.net.*; import com.wood.core.monitor.*; public class Crawler implements Runnable { //抓取线程ID private String id; //控制器 private CrawlerController controller; //URL,供各线程共享 private URLMap urlmap; private boolean canceled; //保存抓取的网页内容,用于分析超链接 private String content; //字符集 private String charset; //匹配超链接的正则表达式 private String link_reg="&lt;a\\s+(.*?)href\\s*=\\s*\"?(.*?)[\"(\\s*?)](.*?)&gt;"; private Pattern link_pattern=Pattern.compile(link_reg,Pattern.CASE_INSENSITIVE); //匹配字符集的正则表达式 private String charset_reg="&lt;meta\\s+http-equiv=\"content-type\"\\s+content=\"text html;="" charset="(.*?)\&amp;quot;(.*?)"&gt; "; private Pattern charset_pattern=Pattern.compile(charset_reg, Pattern.CASE_INSENSITIVE); private Pattern dir_path_pattern=Pattern.compile("^\\w+$", Pattern.CASE_INSENSITIVE); //保存当前抓取的URL的主机 private String host; //生成待建立的文件夹名 private String host_path; //抓取网页的根目录 private String root_path="E:\\web"; private File root_dir=new File(root_path); //输入来自URL,输出到文件 private InputStreamReader in=null; private OutputStreamWriter out=null; //输入输出缓冲区 private char[] buf=new char[10240]; //网页内容缓冲区 private StringBuilder contentbuilder=new StringBuilder(1024*1024); //通过一个URL,下载网页内容 private void download(URL url) { in=null; out=null; //建立抓取文件 String urlfile=url.getFile(); if(urlfile.endsWith("/")) urlfile+="indexindex"; File file=new File(host_path,urlfile); File file_dir=new File(file.getParent()); if(!file_dir.exists()) file_dir.mkdirs(); MonitorHolder.getMonitor().print(id+"开始准备下载"+url.toString()+"\n"); try { //打开链接 URLConnection conn=url.openConnection(); //超时30秒 conn.setConnectTimeout(30000); conn.setDoOutput(true); //不是HTML不下载 /*String type=conn.getContentType(); if( (type==null)|| (!type.equals("text/html")) ) return;*/ //将网页内容的缓冲区清空 contentbuilder.setLength(0); in=new InputStreamReader(conn.getInputStream(),charset); out=new OutputStreamWriter(new FileOutputStream(file),charset); int len; //读取网页内容,并写入文件,保存到网页内容缓冲里面 while((len=in.read(buf, 0, 10240)) &gt;0) { out.write(buf, 0, len); //append可以减小系统损耗 contentbuilder.append(buf, 0, len); } out.flush(); //将网页内容缓冲区的内容读到content中,用于分析 content=null; content=contentbuilder.toString(); } catch (IOException e) { MonitorHolder.getMonitor().print("错误!!!"+id+"下载页面"+url.toString()+"错误\n"); } finally{ try { if(in!=null) in.close(); if(out!=null) out.close(); } catch (IOException e) { } in=null; out=null; } } public Crawler(String id,CrawlerController controller,URLMap urlmap) { this.id=id; this.controller=controller; this.urlmap=urlmap; this.charset="utf-8"; canceled=false; MonitorHolder.getMonitor().print(id+"就绪\n"); } //设置字符集,通过正则表达式获取字符集格式 //为了性能,该功能并未使用 private void setCharset() { Matcher matcher=charset_pattern.matcher(content); String CHARSET="GB2312"; if(matcher.find()) CHARSET=matcher.group(1).trim(); this.charset=CHARSET; try { content=new String(content.getBytes("GB2312"),CHARSET); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } } //从网页内容中分析超链接 private List&lt;string&gt; retrieveLinks(URL url) { List&lt;string&gt; urls=new LinkedList&lt;string&gt;(); if(content==null) return urls; Matcher matcher=link_pattern.matcher(content); String link; String host_reg="http://"+host; String host_nowww; if(host.contains("www")) host_nowww=host.substring(host.lastIndexOf("w")+2); else { host_nowww=host; } while(matcher.find()) { //通过抓取第1组的内容 link=matcher.group(2).trim().toLowerCase(); if (link.length() &lt; 1) { continue; } //网页内部链接,忽略 if (link.charAt(0) == '#') { continue; } //发送邮件链接,忽略 if (link.indexOf("mailto:") != -1) { continue; } if (link.toLowerCase().indexOf("javascript") != -1) { continue; } //分析绝对地址或相对地址 if (link.indexOf("://") == -1){ if (link.charAt(0) == '/') //处理绝对地址 link = "http://" + host+ link; else if(link.startsWith("./")) link="http://" + host+ link.substring(1); else { String file = url.getFile(); String file_path=file.substring(0, file.lastIndexOf('/')); while(link.startsWith("../")) { link=link.substring(link.indexOf("/")+1); file_path=file_path.substring(0, file_path.lastIndexOf("/")); } link="http://"+host+file_path+"/"+link; } } int index = link.indexOf('#'); if (index != -1) { link = link.substring(0, index); } if(!urlmap.testHost(link)) continue; if(!urlmap.testLimit(link)) continue; urls.add(link); } return urls; } //设置主机并建立目录 private void setHost(String host) { this.host=host; this.host_path=root_path+"\\"+host; File host_dir=new File(host_path); if(!host_dir.exists()) host_dir.mkdirs(); } @Override public void run() { if(!root_dir.exists()) root_dir.mkdirs(); while(!Thread.currentThread().isInterrupted() &amp;&amp; !canceled &amp;&amp; (controller.getCount()&lt;controller.gettotal()) )="" {="" try="" {="" 获得一个待抓取的url,如果没有可用url,则进入阻塞状态,该方法调用是线程安全的="" string="" urlstring="urlmap.getURL();" monitorholder.getmonitor().print(id+"开始抓取"+urlstring+"\n");="" 建立url="" url="" url="new" url(urlstring);="" 设置主机="" sethost(url.gethost());="" 清空网页内容,并下贼="" content="null;" download(url);="" 未下载到内容="" if(content="=null)" {="" continue;="" }="" monitorholder.getmonitor().print(id+"抓取"+urlstring+"完毕,进行解析\n");="" setcharset();="" 分析超链接="" list&lt;string=""&gt; urls=retrieveLinks(url); //将分析到的超连接加入到待抓取的URL队列中,并将成功抓取数+1 urlmap.addAll(urls); controller.count(); MonitorHolder.getMonitor().print(id+"解析"+urlstring+"完毕,共计"+urls.size()+"个超链接,开始新任务\n"); content=null; TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { MonitorHolder.getMonitor().print(id+"被中断\n"); canceled=true; } catch (MalformedURLException e) { MonitorHolder.getMonitor().print(id+"报告:URL格式错误\n"); }catch (Exception e) { e.printStackTrace(); } } } //退出,由控制器调用,关闭所有底层I/O资源 public void cancel() { try { if(in!=null) in.close(); } catch (IOException e) { } try { if(out!=null) out.close(); } catch (IOException e) { e.printStackTrace(); } MonitorHolder.getMonitor().print(id+"停止工作\n"); canceled=true; } } </pre> <p> </p> <pre name="code" class="java">package com.wood.core; import java.net.MalformedURLException; import java.net.URL; import java.util.concurrent.*; import java.util.*; public class URLMap { //待抓取的URL队列 private LinkedList&lt;string&gt; URLQueue; //缓存解析出来的URL,使用Hash方便快速查找,&lt;k,false&gt;表示未抓取,&lt;k,true&gt;表示抓取结束 private HashMap&lt;string,boolean&gt; cachedURL; private HashSet&lt;string&gt; hosts; private HashSet&lt;string&gt; format_limit; //默认构造器,初始化队列,集合 private URLMap() { URLQueue=new LinkedList&lt;string&gt;(); cachedURL=new HashMap&lt;string,boolean&gt;(50000); hosts=new HashSet&lt;string&gt;(20); format_limit=new HashSet&lt;string&gt;(20); } public URLMap(List&lt;string&gt; seeds) { this(); for(String s:seeds) cachedURL.put(s, false); URLQueue.addAll(seeds); for (String string : seeds) { try { URL test=new URL(string); String host=test.getHost(); hosts.add(host); } catch (MalformedURLException e) { e.printStackTrace(); } } } public URLMap(String seed) { this(); cachedURL.put(seed, false); URLQueue.add(seed); try { URL test=new URL(seed); hosts.add(test.getHost()); } catch (MalformedURLException e) { e.printStackTrace(); } } //将解析出来的URL添加到URLMap中,如果有重复则忽略,URL有效性由外部保证,该方法是线程安全的 public synchronized void addURL(String url) throws InterruptedException { //如果该URL已存在,忽略该URL if(cachedURL.keySet().contains(url)) return; cachedURL.put(url, false); URLQueue.add(url); //有可用URL,唤醒所有阻塞线程 notifyAll(); } //将解析出来的URL添加到URLMap中,如果有重复则忽略,URL有效性由外部保证,该方法是同步的 public synchronized void addAll(List&lt;string&gt; urls) throws InterruptedException { for (String url : urls) { if(cachedURL.keySet().contains(url)) continue; cachedURL.put(url,false); URLQueue.add(url); } notifyAll(); } //从当前URL队列中获取一个URL,如果当前队列无可用URL,则该线程进入阻塞状态 public synchronized String getURL() throws InterruptedException { //该处进入阻塞 while(URLQueue.size()==0) wait(); //将其从队列中删除 String url=URLQueue.remove(); cachedURL.put(url, true); return url; } public boolean testHost(String host) { for(String host_allow:hosts) { if(host.contains(host_allow)) return true; } return false; } public void addHost(List&lt;string&gt; l) { hosts.addAll(l); } public void addLimit(List&lt;string&gt; l) { format_limit.addAll(l); } public boolean testLimit(String url) { if(format_limit.size()==0) return true; else { for(String s:format_limit) if(url.contains(s)) return true; } return false; } //将待抓取队列打乱 private int swap_check=0; public synchronized void shuffle() { swap_check++; int size=URLQueue.size(); if(size&lt;1000) { java.util.Collections.shuffle(URLQueue); swap_check=0; } else { if(size&gt;=1000 &amp;&amp; size&lt;5000) { if(swap_check==5) { java.util.Collections.shuffle(URLQueue); swap_check=0; } } else { if(swap_check==15) { java.util.Collections.shuffle(URLQueue); swap_check=0; } } } } } </pre> <p><br /><br />附件里是内存使用情况<br /><strong>问题补充</strong><br />主要占用内存的是char[] <br />我的策略是所有分析到的有效URL都存储在HashMap中 <br />但是几万个URL不至于占用大多数内存吧? <br />我的工作线程只有5个,设置的多了,一会就崩了 <br /><br />哪位高人路过,帮我看眼吧 <br />祝你新年好运~</p> <p> </p> <p>附件里是内存占用,缓存的URL总数是20000个,一个大概占用80B,程序内存总数是150MB</p> <p>我不知道到底是什么东西不停的吃内存,回收不掉,程序运行的时间越长,线程数越多,内存吃的越快</p>
动态规划入门到熟悉,看不懂来打我啊
持续更新。。。。。。 2.1斐波那契系列问题 2.2矩阵系列问题 2.3跳跃系列问题 3.1 01背包 3.2 完全背包 3.3多重背包 3.4 一些变形选讲 2.1斐波那契系列问题 在数学上,斐波纳契数列以如下被以递归的方法定义:F(0)=0,F(1)=1, F(n)=F(n-1)+F(n-2)(n&gt;=2,n∈N*)根据定义,前十项为1, 1, 2, 3...
Java学习的正确打开方式
在博主认为,对于入门级学习java的最佳学习方法莫过于视频+博客+书籍+总结,前三者博主将淋漓尽致地挥毫于这篇博客文章中,至于总结在于个人,实际上越到后面你会发现学习的最好方式就是阅读参考官方文档其次就是国内的书籍,博客次之,这又是一个层次了,这里暂时不提后面再谈。博主将为各位入门java保驾护航,各位只管冲鸭!!!上天是公平的,只要不辜负时间,时间自然不会辜负你。 何谓学习?博主所理解的学习,它是一个过程,是一个不断累积、不断沉淀、不断总结、善于传达自己的个人见解以及乐于分享的过程。
程序员必须掌握的核心算法有哪些?
由于我之前一直强调数据结构以及算法学习的重要性,所以就有一些读者经常问我,数据结构与算法应该要学习到哪个程度呢?,说实话,这个问题我不知道要怎么回答你,主要取决于你想学习到哪些程度,不过针对这个问题,我稍微总结一下我学过的算法知识点,以及我觉得值得学习的算法。这些算法与数据结构的学习大多数是零散的,并没有一本把他们全部覆盖的书籍。下面是我觉得值得学习的一些算法以及数据结构,当然,我也会整理一些看过...
Python——画一棵漂亮的樱花树(不同种樱花+玫瑰+圣诞树喔)
最近翻到一篇知乎,上面有不少用Python(大多是turtle库)绘制的树图,感觉很漂亮,我整理了一下,挑了一些我觉得不错的代码分享给大家(这些我都测试过,确实可以生成) one 樱花树 动态生成樱花 效果图(这个是动态的): 实现代码 import turtle as T import random import time # 画樱花的躯干(60,t) def Tree(branch, ...
大学四年自学走来,这些私藏的实用工具/学习网站我贡献出来了
大学四年,看课本是不可能一直看课本的了,对于学习,特别是自学,善于搜索网上的一些资源来辅助,还是非常有必要的,下面我就把这几年私藏的各种资源,网站贡献出来给你们。主要有:电子书搜索、实用工具、在线视频学习网站、非视频学习网站、软件下载、面试/求职必备网站。 注意:文中提到的所有资源,文末我都给你整理好了,你们只管拿去,如果觉得不错,转发、分享就是最大的支持了。 一、电子书搜索 对于大部分程序员...
shell脚本:备份数据库、代码上线
备份MySQL数据库 场景: 一台MySQL服务器,跑着5个数据库,在没有做主从的情况下,需要对这5个库进行备份 需求: 1)每天备份一次,需要备份所有的库 2)把备份数据存放到/data/backup/下 3)备份文件名称格式示例:dbname-2019-11-23.sql 4)需要对1天以前的所有sql文件压缩,格式为gzip 5)本地数据保留1周 6)需要把备份的数据同步到远程备份中心,假如...
如何安装 IntelliJ IDEA 最新版本——详细教程
IntelliJ IDEA 简称 IDEA,被业界公认为最好的 Java 集成开发工具,尤其在智能代码助手、代码自动提示、代码重构、代码版本管理(Git、SVN、Maven)、单元测试、代码分析等方面有着亮眼的发挥。IDEA 产于捷克,开发人员以严谨著称的东欧程序员为主。IDEA 分为社区版和付费版两个版本。 我呢,一直是 Eclipse 的忠实粉丝,差不多十年的老用户了。很早就接触到了 IDEA...
面试还搞不懂redis,快看看这40道面试题(含答案和思维导图)
Redis 面试题 1、什么是 Redis?. 2、Redis 的数据类型? 3、使用 Redis 有哪些好处? 4、Redis 相比 Memcached 有哪些优势? 5、Memcache 与 Redis 的区别都有哪些? 6、Redis 是单进程单线程的? 7、一个字符串类型的值能存储最大容量是多少? 8、Redis 的持久化机制是什么?各自的优缺点? 9、Redis 常见性...
为什么要推荐大家学习字节码?
配套视频: 为什么推荐大家学习Java字节码 https://www.bilibili.com/video/av77600176/ 一、背景 本文主要探讨:为什么要学习 JVM 字节码? 可能很多人会觉得没必要,因为平时开发用不到,而且不学这个也没耽误学习。 但是这里分享一点感悟,即人总是根据自己已经掌握的知识和技能来解决问题的。 这里有个悖论,有时候你觉得有些技术没用恰恰是...
【设计模式】单例模式的八种写法分析
网上泛滥流传单例模式的写法种类,有说7种的,也有说6种的,当然也不排除说5种的,他们说的有错吗?其实没有对与错,刨根问底,写法终究是写法,其本质精髓大体一致!因此完全没必要去追究写法的多少,有这个时间还不如跟着宜春去网吧偷耳机、去田里抓青蛙得了,一天天的....
《面试宝典》:检验是否为合格的初中级程序员的面试知识点,你都知道了吗?查漏补缺
欢迎关注文章系列,一起学习 《提升能力,涨薪可待篇》 《面试知识,工作可待篇》 《实战演练,拒绝996篇》 也欢迎关注公 众 号【Ccww笔记】,原创技术文章第一时间推出 如果此文对你有帮助、喜欢的话,那就点个赞呗,点个关注呗! 《面试知识,工作可待篇》-Java笔试面试基础知识大全 前言 是不是感觉找工作面试是那么难呢? 在找工作面试应在学习的基础进行总结面试知识点,工作也指日可待,欢...
在阿里,40岁的奋斗姿势
在阿里,40岁的奋斗姿势 在阿里,什么样的年纪可以称为老呢?35岁? 在云网络,有这样一群人,他们的平均年龄接近40,却刚刚开辟职业生涯的第二战场。 他们的奋斗姿势是什么样的呢? 洛神赋 “翩若惊鸿,婉若游龙。荣曜秋菊,华茂春松。髣髴兮若轻云之蔽月,飘飖兮若流风之回雪。远而望之,皎若太阳升朝霞;迫而察之,灼若芙蕖出渌波。” 爱洛神,爱阿里云 2018年,阿里云网络产品部门启动洛神2.0升...
【超详细分析】关于三次握手与四次挥手面试官想考我们什么?
在面试中,三次握手和四次挥手可以说是问的最频繁的一个知识点了,我相信大家也都看过很多关于三次握手与四次挥手的文章,今天的这篇文章,重点是围绕着面试,我们应该掌握哪些比较重要的点,哪些是比较被面试官给问到的,我觉得如果你能把我下面列举的一些点都记住、理解,我想就差不多了。 三次握手 当面试官问你为什么需要有三次握手、三次握手的作用、讲讲三次三次握手的时候,我想很多人会这样回答: 首先很多人会先讲下握...
压测学习总结(1)——高并发性能指标:QPS、TPS、RT、吞吐量详解
一、QPS,每秒查询 QPS:Queries Per Second意思是“每秒查询率”,是一台服务器每秒能够相应的查询次数,是对一个特定的查询服务器在规定时间内所处理流量多少的衡量标准。互联网中,作为域名系统服务器的机器的性能经常用每秒查询率来衡量。 二、TPS,每秒事务 TPS:是TransactionsPerSecond的缩写,也就是事务数/秒。它是软件测试结果的测量单位。一个事务是指一...
新程序员七宗罪
当我发表这篇文章《为什么每个工程师都应该开始考虑开发中的分析和编程技能呢?》时,我从未想到它会对读者产生如此积极的影响。那些想要开始探索编程和数据科学领域的人向我寻求建议;还有一些人问我下一篇文章的发布日期;还有许多人询问如何顺利过渡到这个职业。我非常鼓励大家继续分享我在这个旅程的经验,学习,成功和失败,以帮助尽可能多的人过渡到一个充满无数好处和机会的职业生涯。亲爱的读者,谢谢你。 -罗伯特。 ...
活到老,学到老,程序员也该如此
全文共2763字,预计学习时长8分钟 图片来源:Pixabay 此前,“网传阿里巴巴要求尽快实现P8全员35周岁以内”的消息闹得沸沸扬扬。虽然很快被阿里辟谣,但苍蝇不叮无缝的蛋,无蜜不招彩蝶蜂。消息从何而来?真相究竟怎样?我们无从而知。我们只知道一个事实:不知从何时开始,程序猿也被划在了“吃青春饭”行业之列。 饱受“996ICU”摧残后,好不容易“头秃了变强了”,即将步入为“高...
2019年Spring Boot面试都问了什么?快看看这22道面试题!
Spring Boot 面试题 1、什么是 Spring Boot? 2、Spring Boot 有哪些优点? 3、什么是 JavaConfig? 4、如何重新加载 Spring Boot 上的更改,而无需重新启动服务器? 5、Spring Boot 中的监视器是什么? 6、如何在 Spring Boot 中禁用 Actuator 端点安全性? 7、如何在自定义端口上运行 Sprin...
小白学 Python 爬虫(5):前置准备(四)数据库基础
人生苦短,我用 Python 前文传送门: 小白学 Python 爬虫(1):开篇 小白学 Python 爬虫(2):前置准备(一)基本类库的安装 小白学 Python 爬虫(3):前置准备(二)Linux基础入门 小白学 Python 爬虫(4):前置准备(三)Docker基础入门 本篇文章,我们接着介绍基础内容,数据库。 爬虫将数据爬取完成后,总要有地方存放吧,这个数据存在哪里呢? ...
面试官:关于Java性能优化,你有什么技巧
通过使用一些辅助性工具来找到程序中的瓶颈,然后就可以对瓶颈部分的代码进行优化。 一般有两种方案:即优化代码或更改设计方法。我们一般会选择后者,因为不去调用以下代码要比调用一些优化的代码更能提高程序的性能。而一个设计良好的程序能够精简代码,从而提高性能。 下面将提供一些在JAVA程序的设计和编码中,为了能够提高JAVA程序的性能,而经常采用的一些方法和技巧。 1.对象的生成和大小的调整。 J...
JavaScript 中, 5 种增加代码可读性的最佳实践
作者:Milos Protic 译者:前端小智 来源:blog.risingstack 为了保证的可读性,本文采用意译而非直译。 简介 如果咱们关注代码本身结构及可读笥,而不是只关心它是否能工作,那么咱们写代码是有一定的水准。专业开发人员将为未来的自己和“其他人”编写代码,而不仅仅只编写能应付当前工作的代码。 在此基础上,可读性高的代码可以定义为自解释的、易于人理解的、易于更改...
【图解算法面试】记一次面试:说说游戏中的敏感词过滤是如何实现的?
版权声明:本文为苦逼的码农原创。未经同意禁止任何形式转载,特别是那些复制粘贴到别的平台的,否则,必定追究。欢迎大家多多转发,谢谢。 小秋今天去面试了,面试官问了一个与敏感词过滤算法相关的问题,然而小秋对敏感词过滤算法一点也没听说过。于是,有了下下事情的发生… 面试官开怼 面试官:玩过王者荣耀吧?了解过敏感词过滤吗?,例如在游戏里,如果我们发送“你在干嘛?麻痹演员啊你?”,由于“麻痹”是一个敏感词,...
程序员需要了解的硬核知识之汇编语言(一)
之前的系列文章从 CPU 和内存方面简单介绍了一下汇编语言,但是还没有系统的了解一下汇编语言,汇编语言作为第二代计算机语言,会用一些容易理解和记忆的字母,单词来代替一个特定的指令,作为高级编程语言的基础,有必要系统的了解一下汇编语言,那么本篇文章希望大家跟我一起来了解一下汇编语言。 汇编语言和本地代码 我们在之前的文章中探讨过,计算机 CPU 只能运行本地代码(机器语言)程序,用 C 语言等高级语...
GitHub 标星 1.6w+,我发现了一个宝藏项目,作为编程新手有福了!
大家好,我是 Rocky0429,一个最近老在 GitHub 上闲逛的蒟蒻… 特别惭愧的是,虽然我很早就知道 GitHub,但是学会逛 GitHub 的时间特别晚。当时一方面是因为菜,看着这种全是英文的东西难受,不知道该怎么去玩,另一方面是一直在搞 ACM,没有做一些工程类的项目,所以想当然的以为和 GitHub 也没什么关系(当然这种想法是错误的)。 后来自己花了一个星期看完了 Pyt...
Java知识体系最强总结(2020版)
更新于2019-12-15 10:38:00 本人从事Java开发已多年,平时有记录问题解决方案和总结知识点的习惯,整理了一些有关Java的知识体系,这不是最终版,会不定期的更新。也算是记录自己在从事编程工作的成长足迹,通过博客可以促进博主与阅读者的共同进步,结交更多志同道合的朋友。特此分享给大家,本人见识有限,写的博客难免有错误或者疏忽的地方,还望各位大佬指点,在此表示感激不尽。 文章目录...
计算机专业的书普遍都这么贵,你们都是怎么获取资源的?
介绍几个可以下载编程电子书籍的网站。 1.Github Github上编程书资源很多,你可以根据类型和语言去搜索。推荐几个热门的: free-programming-books-zh_CN:58K 星的GitHub,编程语言、WEB、函数、大数据、操作系统、在线课程、数据库相关书籍应有尽有,共有几百本。 Go语言高级编程:涵盖CGO,Go汇编语言,RPC实现,Protobuf插件实现,Web框架实...
毕业5年,我问遍了身边的大佬,总结了他们的学习方法
我问了身边10个大佬,总结了他们的学习方法,原来成功都是有迹可循的。
这些软件太强了,Windows必装!尤其程序员!
Windows可谓是大多数人的生产力工具,集娱乐办公于一体,虽然在程序员这个群体中都说苹果是信仰,但是大部分不都是从Windows过来的,而且现在依然有很多的程序员用Windows。 所以,今天我就把我私藏的Windows必装的软件分享给大家,如果有一个你没有用过甚至没有听过,那你就赚了????,这可都是提升你幸福感的高效率生产力工具哦! 走起!???? NO、1 ScreenToGif 屏幕,摄像头和白板...
大学四年因为知道了这32个网站,我成了别人眼中的大神!
依稀记得,毕业那天,我们导员发给我毕业证的时候对我说“你可是咱们系的风云人物啊”,哎呀,别提当时多开心啦????,嗯,我们导员是所有导员中最帅的一个,真的???? 不过,导员说的是实话,很多人都叫我大神的,为啥,因为我知道这32个网站啊,你说强不强????,这次是绝对的干货,看好啦,走起来! PS:每个网站都是学计算机混互联网必须知道的,真的牛杯,我就不过多介绍了,大家自行探索,觉得没用的,尽管留言吐槽吧???? 社...
Fiddler+夜神模拟器进行APP抓包
Fiddler+夜神模拟器进行APP抓包 作者:霞落满天 需求:对公司APP进行抓包获取详细的接口信息,这是现在开发必备的。 工具:Fiddler抓包,夜神模拟器 模拟手机 安装APP 1.下载Fiddler https://www.telerik.com/download/fiddler Fiddler正是在这里帮助您记录计算机和Internet之间传递的所有HTTP和HTTPS通信...
Java9到Java13各版本新特性代码全部详解(全网独家原创)
Java现在已经发展到了Java13了(正式版本),相信很多朋友还对各个版本还不是很熟悉,这里面专门把Java9到Java13各个版本的一些新特性做了一些详细讲解。我在网上也找了很多,但基本都是官方文档的CV,没有任何代码演示,而且官方的示例代码也不是很好找得到,官方API目前还是Java10,官方文档真是坑啊。所以我在这里专门写了一篇文章,主要针对平时开发与有关的功能Java9到Java13各...
一文带你看清 HTTP 所有概念
上一篇文章我们大致讲解了一下 HTTP 的基本特征和使用,大家反响很不错,那么本篇文章我们就来深究一下 HTTP 的特性。我们接着上篇文章没有说完的 HTTP 标头继续来介绍(此篇文章会介绍所有标头的概念,但没有深入底层) HTTP 标头 先来回顾一下 HTTP1.1 标头都有哪几种 HTTP 1.1 的标头主要分为四种,通用标头、实体标头、请求标头、响应标头,现在我们来对这几种标头进行介绍 通用...
Java程序员2020年最新进入 BATJ华为等大厂必读书单及技能
0 要做一名高级点儿的代码民工,我们首先要有如下各项技能 ! Google more and Baidu less! Stackoverflow First! Stack Overflow - Where Developers Learn, Share, &amp; Build Careers​stackoverflow.com 汇聚最多编程问题且有世界各路大佬解答 尝试着阅读官方原版...
作为一个程序员,CPU的这些硬核知识你必须会!
CPU对每个程序员来说,是个既熟悉又陌生的东西? 如果你只知道CPU是中央处理器的话,那可能对你并没有什么用,那么作为程序员的我们,必须要搞懂的就是CPU这家伙是如何运行的,尤其要搞懂它里面的寄存器是怎么一回事,因为这将让你从底层明白程序的运行机制。 随我一起,来好好认识下CPU这货吧 把CPU掰开来看 对于CPU来说,我们首先就要搞明白它是怎么回事,也就是它的内部构造,当然,CPU那么牛的一个东...
破14亿,Python分析我国存在哪些人口危机!
一、背景 二、爬取数据 三、数据分析 1、总人口 2、男女人口比例 3、人口城镇化 4、人口增长率 5、人口老化(抚养比) 6、各省人口 7、世界人口 四、遇到的问题 遇到的问题 1、数据分页,需要获取从1949-2018年数据,观察到有近20年参数:LAST20,由此推测获取近70年的参数可设置为:LAST70 2、2019年数据没有放上去,可以手动添加上去 3、将数据进行 行列转换 4、列名...
Python实战:抓肺炎疫情实时数据,画2019-nCoV疫情地图
今天,群里白垩老师问如何用python画武汉肺炎疫情地图。白垩老师是研究海洋生态与地球生物的学者,国家重点实验室成员,于不惑之年学习python,实为我等学习楷模。先前我并没有关注武汉肺炎的具体数据,也没有画过类似的数据分布图。于是就拿了两个小时,专门研究了一下,遂成此文。
[HTML] HTML常用基础标签
文章目录HTTP基础语法HTTP基本结构标签、元素、属性注释文档声明网页编码文字和段落标签标题标签h1--h6段落标签p换行标签br和水平线标签hr预标签pre很少用。修饰标签和特殊符号文字标签特殊符号列表标签无序列表ul有序列表ol定义列表图像标签img超链接a和定义锚点超链接标签a结合name属性定义锚点链接扩展功能 HTTP基础语法 div标签是块级结构布局元素,因此结合css盒模型讲解。 ...
在家远程办公效率低?那你一定要收好这个「在家办公」神器!
相信大家都已经收到国务院延长春节假期的消息,接下来,在家远程办公可能将会持续一段时间。 但是问题来了。远程办公不是人在电脑前就当坐班了,相反,对于沟通效率,文件协作,以及信息安全都有着极高的要求。有着非常多的挑战,比如: 1在异地互相不见面的会议上,如何提高沟通效率? 2文件之间的来往反馈如何做到及时性?如何保证信息安全? 3如何规划安排每天工作,以及如何进行成果验收? ...... ...
作为一个程序员,内存和磁盘的这些事情,你不得不知道啊!!!
截止目前,我已经分享了如下几篇文章: 一个程序在计算机中是如何运行的?超级干货!!! 作为一个程序员,CPU的这些硬核知识你必须会! 作为一个程序员,内存的这些硬核知识你必须懂! 这些知识可以说是我们之前都不太重视的基础知识,可能大家在上大学的时候都学习过了,但是嘞,当时由于老师讲解的没那么有趣,又加上这些知识本身就比较枯燥,所以嘞,大家当初几乎等于没学。 再说啦,学习这些,也看不出来有什么用啊!...
相关热词 c# 数组类型 泛型约束 c#的赛狗日程序 c# 传递数组 可变参数 c# 生成存储过程 c# list 补集 c#获得所有窗体 c# 当前秒数转成年月日 c#中的枚举 c# 计算校验和 连续随机数不重复c#
立即提问