2009-04-28 17:52:00 kehui123 阅读数 912

 此程序不用人为选择块,只要输入位置就可以直接显示所需内容。控制器是KS0108B/A。

void WriteByte2(unsigned char dat)
{
 CheckState2();
 PORTA|=(1<<PA0);//SET_RS;//RS = 1;
 PORTA&=~(1<<PA1);//CLR_RW;//RW = 0;
 DDRB = 0XFF;
 Swait();
 PORTB = dat;//P2= dat;
 Swait();
 PORTA|=(1<<PA2);//SET_E;//E = 1;
 Swait();
 PORTA&=~(1<<PA2);//CLR_E;//E = 0;
 Swait();
}


void WriteByte1(unsigned char dat)
{
 CheckState1();
 PORTA|=(1<<PA0);//SET_RS;//RS = 1;
 PORTA&=~(1<<PA1);//CLR_RW;//RW = 0;
 DDRB = 0XFF;
 Swait();
 PORTB = dat;//P2= dat;
 Swait();
 PORTA|=(1<<PA2);//SET_E;//E = 1;
 Swait();
 PORTA&=~(1<<PA2);//CLR_E;//E = 0;
 Swait();
}

void WriteByte0(unsigned char dat)
{
 CheckState0();
 PORTA|=(1<<PA0);//SET_RS;//RS = 1;
 PORTA&=~(1<<PA1);//CLR_RW;//RW = 0;
 DDRB = 0XFF;
 Swait();
 PORTB = dat;//P2= dat;
 Swait();
 PORTA|=(1<<PA2);//SET_E;//E = 1;
 Swait();
 PORTA&=~(1<<PA2);//CLR_E;//E = 0;
 Swait();
}

/*-----------------------------------------------------------------------------------------------------*/
//向LCD发送命令
//command :命令
/*-----------------------------------------------------------------------------------------------------*/
void SendCommandToLCD2(unsigned char command)
{
 CheckState2();
 PORTA&=~(1<<PA1);//CLR_RW;//RW = 0;
 DDRB = 0XFF;
 Swait();
 PORTB = command;//P2 = command;
 Swait();
 PORTA|=(1<<PA2);//SET_E;//E = 1;
 Swait();
 PORTA&=~(1<<PA2);//CLR_E;//E = 0;
 Swait();
}

void SendCommandToLCD1(unsigned char command)
{
 CheckState1();
 PORTA&=~(1<<PA1);//CLR_RW;//RW = 0;
 DDRB = 0XFF;
 Swait();
 PORTB = command;//P2 = command;
 Swait();
 PORTA|=(1<<PA2);//SET_E;//E = 1;
 Swait();
 PORTA&=~(1<<PA2);//CLR_E;//E = 0;
 Swait();
}

void SendCommandToLCD0(unsigned char command)
{
 CheckState0();
 PORTA&=~(1<<PA1);//CLR_RW;//RW = 0;
 DDRB = 0XFF;
 Swait();
 PORTB = command;//P2 = command;
 Swait();
 PORTA|=(1<<PA2);//SET_E;//E = 1;
 Swait();
 PORTA&=~(1<<PA2);//CLR_E;//E = 0;
 Swait();
}
/*----------------------------------------------------------------------------------------------------*/
//设定行地址(页)--X 0-7
/*----------------------------------------------------------------------------------------------------*/
void SetLine0(unsigned char line){
 line = line & 0x07; // 0<=line<=7
 Swait();
 line = line | 0xb8; //1011 1xxx
 Swait();
 SendCommandToLCD0(line);
// SendCommandToLCD1(line);
// SendCommandToLCD2(line);

}

void SetLine1(unsigned char line){
 line = line & 0x07; // 0<=line<=7
 Swait();
 line = line | 0xb8; //1011 1xxx
 Swait();
 SendCommandToLCD1(line);
// SendCommandToLCD1(line);
// SendCommandToLCD2(line);

}

void SetLine2(unsigned char line){
 line = line & 0x07; // 0<=line<=7
 Swait();
 line = line | 0xb8; //1011 1xxx
 Swait();
 SendCommandToLCD2(line);
// SendCommandToLCD1(line);
// SendCommandToLCD2(line);

}

/////////////////////////////////////////////////////////////////////////////////////////////////////////////
//设定列地址--Y 0-63
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
void SetColumn0(unsigned char column){
 column = column & 0x3f; // 0=<column<=63
 Swait();
 column = column | 0x40; //01xx xxxx
 Swait();
 SendCommandToLCD0(column);
}
void SetColumn1(unsigned char column){
 column = column & 0x3f; // 0=<column<=63
 Swait();
 column = column | 0x40; //01xx xxxx
 Swait();
 SendCommandToLCD1(column);
}
void SetColumn2(unsigned char column){
 column = column & 0x3f; // 0=<column<=63
 Swait();
 column = column | 0x40; //01xx xxxx
 Swait();
 SendCommandToLCD2(column);
}

/////////////////////////////////////////////////////////////////////////////////////////////////////////////
//设定显示开始行--XX
void SetStartLine(unsigned char startline) //0--63
{
 startline=startline & 0x07;
 Swait();
 startline=startline | 0xc0; //1100 0000
 Swait();
 SendCommandToLCD0(startline);
 SendCommandToLCD1(startline);
 SendCommandToLCD2(startline);
}

//开关显示
void SetOnOff(unsigned char onoff)
{
 onoff=0x3e | onoff; //0011 111x
 Swait();
 SendCommandToLCD0(onoff);
 SendCommandToLCD1(onoff);
 SendCommandToLCD2(onoff);

}

/*---------------------------------------------------------------------------------------------------*/
//选择屏幕
//screen: 0-全屏,1-左屏,2-右屏
void SelectScreen(unsigned char screen)   //负有效 cs1: 0--右; cs2: 0--左
{
 if(screen == 2)
 {
  PORTA|=(1<<PA3);//CLR_CS1;//CSA = 0;//右屏
  Swait();
  PORTA|=(1<<PA4);//SET_CS2;//CSB = 1;
  Swait();
  PORTA&=~(1<<PA5);//SET_CS2;//CSB = 1;
  Swait();
 }
 if (screen == 1)
 {
  PORTA|=(1<<PA3);//CLR_CS2;//CSB = 0;
  Swait();
     PORTA&=~(1<<PA4);//SET_CS1;//CSA = 1;//左屏
  Swait();
  PORTA|=(1<<PA5);//SET_CS2;//CSB = 1;
  Swait();
 }
 if (screen == 0)
 {
  PORTA&=~(1<<PA3);//SET_CS1;//CSA = 1;//左屏
  Swait();
  PORTA|=(1<<PA4);//SET_CS1;//CSA = 1;//左屏
  Swait();
  PORTA|=(1<<PA5);//SET_CS1;//CSA = 1;//左屏
  Swait();
 }

}

/*---------------------------------------------------------------------------------------------------*/
//清屏
//screen: 0-全屏,1-左屏,2-右
void ClearScreen(unsigned char screen)
{
 unsigned char i,j;
// uchar k;
 SelectScreen(screen);
if(screen==0)
 { for(i=0;i<8;i++)
  {
  SetLine0(i);
  for(j=0;j<64;j++)
   {
   SetColumn0(j);
   Swait();
   WriteByte0(0x00);
   Swait();
   }
  }
 }
if(screen==1)
 { for(i=0;i<8;i++)
  {
  SetLine1(i);
  for(j=0;j<64;j++)
   {
   SetColumn1(j);
   Swait();
   WriteByte1(0x00);
   Swait();
   }
  }
 }
if(screen==2)
 { for(i=0;i<8;i++)
  {
  SetLine2(i);
  for(j=0;j<64;j++)
   {
   SetColumn2(j);
   Swait();
   WriteByte2(0x00);
   Swait();
   }
  }
 }

}
void ClearScreenAll()
{
 SelectScreen(0);
 SelectScreen(1);
 SelectScreen(2);
}

 

 


/*--------------------------------------------------------------------------------------------------
//读显示数据
unsigned char ReadByte()
{
// uint i;
 uchar dat;
 CheckState();
 RS = 1;
 RW = 1;
 Swait();
 P2 = 0xff;
 Swait();
 E = 1;
 Swait();
 dat = P0;
 Swait();
 E = 0;
 return(dat);
}
---------------------------------------------------------------------------------------------------*/
void InitLcd() //初始化LCD           不显示的问题

{
 unsigned char i=250; //延时
 while(i--);
 SetOnOff(0); //关显示
 //SelectScreen(2);
 //SetStartLine(0); //开始行:0
 //SelectScreen(1);
 //SetStartLine(0); //开始行:0
 //SetColumn(0);
// ClearScreen(1);//清屏
// ClearScreen(2);
 //SelectScreen(0);
 SetOnOff(1); //开显示
 //SelectScreen(0);
 //SetStartLine(0); //开始行:0
 SelectScreen(2);
 SetStartLine(0); //开始行:0
 SelectScreen(1);
 SetStartLine(0); //开始行:0
 SelectScreen(0);
 SetStartLine(0);
 ClearScreen(1);//清屏
 ClearScreen(2);
 ClearScreen(0);
}

void CheckState2()
{
 SelectScreen(2);
 uchar temp; //汇编中,这之前有个片选

 do{
  PORTA|=(1<<PA1);//SET_RW;//RW  = 1;
  PORTA&=~(1<<PA0);//CLR_RS;//RS  = 0;在这一句后面,汇编里面把数据口都置为1了,我没有改,看效果
  Swait();
  //P2 = 0XFF;
  PORTA|=(1<<PA2);//SET_E;//E = 1;
  //PORTB = 0XFF;
  DDRB = 0X00;
  Swait();
  temp = PINB;//temp = P2;
  templong = temp;
  //Swait();
  PORTA&=~(1<<PA2);//CLR_E;//E = 0;
  temp = 0x80 & temp;      //当第7bit为0时可以操作
 }while(!(temp == 0));
}

 


void CheckState1()
{
 SelectScreen(1);
 uchar temp; //汇编中,这之前有个片选

 do{
  PORTA|=(1<<PA1);//SET_RW;//RW  = 1;
  PORTA&=~(1<<PA0);//CLR_RS;//RS  = 0;在这一句后面,汇编里面把数据口都置为1了,我没有改,看效果
  Swait();
  //P2 = 0XFF;
  PORTA|=(1<<PA2);//SET_E;//E = 1;
  //PORTB = 0XFF;
  DDRB = 0X00;
  Swait();
  temp = PINB;//temp = P2;
  templong = temp;
  //Swait();
  PORTA&=~(1<<PA2);//CLR_E;//E = 0;
  temp = 0x80 & temp;      //当第7bit为0时可以操作
 }while(!(temp == 0));
}

 


void CheckState0()
{
 SelectScreen(0);
 uchar temp; //汇编中,这之前有个片选

 do{
  PORTA|=(1<<PA1);//SET_RW;//RW  = 1;
  PORTA&=~(1<<PA0);//CLR_RS;//RS  = 0;在这一句后面,汇编里面把数据口都置为1了,我没有改,看效果
  Swait();
  //P2 = 0XFF;
  PORTA|=(1<<PA2);//SET_E;//E = 1;
  //PORTB = 0XFF;
  DDRB = 0X00;
  Swait();
  temp = PINB;//temp = P2;
  templong = temp;
  //Swait();
  PORTA&=~(1<<PA2);//CLR_E;//E = 0;
  temp = 0x80 & temp;      //当第7bit为0时可以操作
 }while(!(temp == 0));
}
void Show(uchar lin,uchar column,uchar linechar,uchar columnchar,const uchar *p,uchar num)
{
 uchar i,j,k,screenbz;
 const uchar *p1;
 p1=p + 32 * num;
 k=column;
 screenbz=0;
 if(k>192) {return;}
 if(k<64)
 {
  SelectScreen(0); //如果列数<8(0,1,2,3,4,5,6,7)则写在第一屏上
  SetColumn0(k);
  screenbz=0;
 }
 else if(k<128)
 {
  SelectScreen(1); //否则 (8,9,10,11,12,13,14,15)写在第二屏上
  //k=k-64;  修改20090410--1  这一句在这里有点多余,而且在后面判断的时候会引发错误
  SetColumn1(k);
  screenbz=1;
 }
 else
 {
  SelectScreen(2);
  //k=k-128;  //这里同上,没有必要做减法
  SetColumn2(k);
  screenbz=2; 
 }
 SetLine0(lin);
 SetLine1(lin);
 SetLine2(lin);
 //SetColumn0(k);
 for(i=lin;i<linechar+lin;i++)
 {
  if(screenbz==0)
  {
   for(j=k;j<k+columnchar;j++)
   {
    if(j>=64)
    {
     SelectScreen(1);
     SetColumn1(0);
     SetLine1(i);
     for(j=64;j<k+columnchar;j++)//这里应该是从64开始往上加,但是总的上界不边,原来从K开始加的话,就又回去了
     { 
      // if(j==128)
      // {
      //  SelectScreen(2);
      //  SetColumn2(0);
      //  SetLine2(i); 
      //  for(j=128;j<k+columnchar;j++)
      //  { 
      //   WriteByte2(pgm_read_byte(p1));
      //   p1++;
      //  }
      // }
       WriteByte1(pgm_read_byte(p1));
       p1++;
     }
    }
    //if(j<64)
    else
    {
     WriteByte0(pgm_read_byte(p1));
     p1++;
    }
   }
   SelectScreen(0);
   SetLine0(i+1);
   SetColumn0(k);
  }
  if(screenbz==1)
  {
   for(j=k;j<k+columnchar;j++)
   { 
    if(j>=128) //对应前面的  修改20090410--1  ,如果前面不修改,这里的K根本到不了128,也就是到64的时候,事实上屏幕已经到边了,但

是数据还继续往里面写
    {
     SelectScreen(2);
     SetColumn2(0);
     SetLine2(i); 
     for(j=128;j<k+columnchar;j++)
     { 
      WriteByte2(pgm_read_byte(p1));
      p1++;
     }
    }
    //if(j<128)
    else
    {
     WriteByte1(pgm_read_byte(p1));
     p1++;
    }
   }
   SelectScreen(1);
   SetLine1(i+1);
   SetColumn1(k);
  }
  if(screenbz==2)
  {
   for(j=k;j<k+columnchar;j++)
   { 
    
    WriteByte2(pgm_read_byte(p1));
    p1++;
   }
   SelectScreen(2);
   SetLine2(i+1);
   SetColumn2(k);
  }
 }
}
void ShowChar(uchar lin,uchar column,uchar linechar,uchar columnchar,const uchar *p,uchar num)
{
 uchar i,j,k,screenbz;
 const uchar *p1;
 p1=p + 16 * num;
 k=column;
 screenbz=0;
 if(k>192) {return;}
 if(k<64)
 {
  SelectScreen(0); //如果列数<8(0,1,2,3,4,5,6,7)则写在第一屏上
  screenbz=0;
  SetColumn0(k);
 }
 else if(k<128)
 {
  SelectScreen(1); //如果列数<8(0,1,2,3,4,5,6,7)则写在第一屏上
  k=k-64;
  SetColumn1(k);
  screenbz=1;
 }
 else
 {
  SelectScreen(2); //否则 (8,9,10,11,12,13,14,15)写在第二屏上
  k=k-128;
  SetColumn2(k);
  screenbz=2;
 }
 SetLine0(lin);
 SetLine1(lin);
 SetLine2(lin);
 //SetColumn0(k);
for(i=lin;i<linechar+lin;i++)
 {
  if(screenbz==0)
  {
   for(j=k;j<k+columnchar;j++)
   {
    if(j>=64)
    {
     SelectScreen(1);
     SetColumn1(0);
     SetLine1(i);
     for(j=64;j<k+columnchar;j++)//这里应该是从64开始往上加,但是总的上界不边,原来从K开始加的话,就又回去了
     { 
      // if(j==128)
      // {
      //  SelectScreen(2);
      //  SetColumn2(0);
      //  SetLine2(i); 
      //  for(j=128;j<k+columnchar;j++)
      //  { 
      //   WriteByte2(pgm_read_byte(p1));
      //   p1++;
      //  }
      // }
       WriteByte1(pgm_read_byte(p1));
       p1++;
     }
    }
    //if(j<64)
    else
    {
     WriteByte0(pgm_read_byte(p1));
     p1++;
    }
   }
   SelectScreen(0);
   SetLine0(i+1);
   SetColumn0(k);
  }
  if(screenbz==1)
  {
   for(j=k;j<k+columnchar;j++)
   { 
    if(j>=128) //对应前面的  修改20090410--1  ,如果前面不修改,这里的K根本到不了128,也就是到64的时候,事实上屏幕已经到边了,但

是数据还继续往里面写
    {
     SelectScreen(2);
     SetColumn2(0);
     SetLine2(i); 
     for(j=128;j<k+columnchar;j++)
     { 
      WriteByte2(pgm_read_byte(p1));
      p1++;
     }
    }
    //if(j<128)
    else
    {
     WriteByte1(pgm_read_byte(p1));
     p1++;
    }
   }
   SelectScreen(1);
   SetLine1(i+1);
   SetColumn1(k);
  }
  if(screenbz==2)
  {
   for(j=k;j<k+columnchar;j++)
   { 
    
    WriteByte2(pgm_read_byte(p1));
    p1++;
   }
   SelectScreen(2);
   SetLine2(i+1);
   SetColumn2(k);
  }
 }
}
void ShowCharLittle(uchar lin,uchar column,uchar linechar,uchar columnchar,const uchar *p,uchar num)
{
 uchar i,j,k,screenbz;
 const uchar *p1;
 p1=p + 6 * num;
 k=column;
 screenbz=0;
 if(k>192) {return;}
 if(k<64)
 {
  SelectScreen(0); //如果列数<8(0,1,2,3,4,5,6,7)则写在第一屏上
  screenbz=0;
  SetColumn0(k);
 }
 if(k>63&&k<128)
 {
  SelectScreen(1); //如果列数<8(0,1,2,3,4,5,6,7)则写在第一屏上
  k=k-64;
  SetColumn1(k);
  screenbz=1;
 }
 else
 {
  SelectScreen(2); //否则 (8,9,10,11,12,13,14,15)写在第二屏上
  k=k-128;
  SetColumn2(k);
  screenbz=2;
 }
 SetLine0(lin);
 SetLine1(lin);
 SetLine2(lin);
 //SetColumn0(k);
 for(i=lin;i<linechar+lin;i++)
 {
  if(screenbz==0)
  {
   for(j=k;j<k+columnchar;j++)
   {
    if(j==64)
    {
     SelectScreen(1);
     SetColumn1(0);
     SetLine1(i);
     for(j=k;j<k+columnchar;j++)
     {
      if(j==128)
      {
       SelectScreen(2);
       SetColumn2(0);
       SetLine2(i);
       for(j=k;j<k+columnchar;j++)
       {
        WriteByte2(pgm_read_byte(p1));
        p1++; 
       } 
      }
      WriteByte1(pgm_read_byte(p1));
      p1++; 
     }
    }
    WriteByte0(pgm_read_byte(p1));
    p1++;
   }
   SelectScreen(1);
   SetLine0(i+1);
   SetColumn0(k);
  }
  if(screenbz==1)
  {
   for(j=k;j<k+columnchar;j++)
   {
    if(j==128)
    {
      SelectScreen(2);
      SetColumn2(0);
      SetLine2(i);
      for(j=k;j<k+columnchar;j++)
      {
       WriteByte2(pgm_read_byte(p1));
       p1++; 
      }  
    }
    WriteByte1(pgm_read_byte(p1));
    p1++;
   }
   SelectScreen(1);
   SetLine1(i+1);
   SetColumn1(k);
  }
  if(screenbz==2)
  {
   for(j=k;j<k+columnchar;j++)
   {
    WriteByte2(pgm_read_byte(p1));
    p1++;
   }
   SelectScreen(2);
   SetLine2(i+1);
   SetColumn1(k);
  }
 }
}

 

 

2019-11-17 12:22:05 qq_34666857 阅读数 117

​上一文中,我们使用JUC模拟了AND型信号量,对于信号量集,他的特点就是一次可以申请多个资源,并且可以设置资源分配下限,让设计者可以更好的来控制进程的执行。如果对于进程同步机制还有什么疑惑,可以参考我的另一篇博客

为了方便编程,并且信号量集在使用时更加方便,我们定义了一个SemaphoreUnit对象,用这个对象来记录申请的信号量类型,申请的数量和分配下限值,SemaphoreUnit定义如下:

@Data
public class SemaphoreUnit {
    //申请的信号量类型
    Semaphore semaphore;
    //分配下限
    Integer t;
    //申请的数量
    Integer d;
}

一个简单的示例

​ 因为在实现AND型信号量中已经踩过坑了,因此在这里直接给出只有两个信号量的Swait和Ssignal操作的代码,如下所示:

//定义锁和条件变量
static Lock lock = new ReentrantLock();
static Condition condition = lock.newCondition();

public static void Swait(String id, SemaphoreUnit unit1, SemaphoreUnit unit2) throws InterruptedException {
  lock.tryLock(1, TimeUnit.SECONDS);
  log.info("当前的两个信号量的状态:【{},{}】", unit1.getSemaphore().availablePermits(), unit2.getSemaphore().availablePermits());

  while (unit1.getSemaphore().availablePermits() < unit1.getT() ||
         unit2.getSemaphore().availablePermits() < unit2.getT()) {
    //如果资源小于分配下限值,就挂起线程,并将线程插入到condition的队列中
    log.info("线程【{}】被挂起。", id);
    condition.await();
    log.info("被挂起的线程【{}】被唤醒执行。", id);
  }
  
  log.info("为线程【{}】分配资源!", id);
  unit1.getSemaphore().acquire(unit1.getD());				//一次为线程分配多个信号量
  unit2.getSemaphore().acquire(unit2.getD());

  lock.unlock();
}

public static void Ssignal(String id, SemaphoreUnit unit1, SemaphoreUnit unit2) throws InterruptedException {
  log.info("线程【{}】执行了释放资源", id);
  lock.tryLock(1, TimeUnit.SECONDS);
  unit1.getSemaphore().release(unit1.getD());			//释放信号量
  unit2.getSemaphore().release(unit2.getD());
  //唤醒等待队列中的线程
  condition.signal();
  log.info("释放后所有信号量的状态:【{},{}】", unit1.getSemaphore().availablePermits(), unit2.getSemaphore().availablePermits());
  
  lock.unlock();
}



可申请N种型号量的示例

​ 这里我们将上面的Swait和Ssignal操作中参数列表进行修改,允许同时申请和释放N类信号量,代码如下:

public static void Swait(String id, SemaphoreUnit... list) throws InterruptedException {
  lock.tryLock(1, TimeUnit.SECONDS);

  while (true) {
    //如果资源小于分配下限值,就挂起线程,并将线程插入到condition的队列中
    int count = 0;
    for (SemaphoreUnit unit : list) {
      //信号量的值不低于分配下限才可分配
      if (unit.getSemaphore().availablePermits() >= unit.getT()) {
        count++;
      } else {					//任一条件不满足时,无法进行资源分配
        break;
      }
    }
    if (count == list.length) {
      break;
    }
    log.info("线程【{}】被挂起。", id);
    condition.await();
    log.info("被挂起的线程【{}】被唤醒执行。", id);
  }

  log.info("为线程【{}】分配资源!", id);
  for (SemaphoreUnit unit : list) {
    unit.getSemaphore().acquire(unit.getD());
  }

  lock.unlock();
}

public static void Ssignal(String id, SemaphoreUnit... list) throws InterruptedException {
  log.info("线程【{}】执行了释放资源", id);

  lock.tryLock(1, TimeUnit.SECONDS);
  //释放资源
  for (SemaphoreUnit unit : list) {
    unit.getSemaphore().release(unit.getD());
  }
  //唤醒等待队列中的线程
  condition.signal();
  
  lock.unlock();
}



解决生产者-消费者问题

​ 最后还是通过一个例子来完整的演示一下信号量集的使用,解决的还是生产者-消费者问题,代码如下:


import XXX.SemaphoreUnit;
import lombok.extern.slf4j.Slf4j;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 信号量集
 */
@Slf4j
public class SemaphoreSetTest {
	//信号量、数据定义与初始化
  static final Semaphore mutex = new Semaphore(1);

  static List<Integer> buffer = new ArrayList<>();

  static final Semaphore empty = new Semaphore(50);
  static final Semaphore full = new Semaphore(0);

  static Lock lock = new ReentrantLock();
  static Condition condition = lock.newCondition();

  static Integer count = 0;

  //生产者
  static class Producer extends Thread {
    Producer(String name) {
      super.setName(name);
    }

    @Override
    public void run() {
      do {
        try {
          //构造SemaphoreUnit
          SemaphoreUnit unit1 = new SemaphoreUnit();
          SemaphoreUnit unit2 = new SemaphoreUnit();
          unit1.setSemaphore(mutex);
          unit1.setT(1);
          unit1.setD(1);
          unit2.setSemaphore(empty);
          unit2.setT(1);
          unit2.setD(1);
          Swait(this.getName(), unit1, unit2);
          log.info("生产了一条消息:【{}】", count);
          buffer.add(count++);
          //Thread.sleep(1000);
          //替换资源,释放一个full信号量
          unit2.setSemaphore(full);
          Ssignal(this.getName(), unit1, unit2);
        } catch (InterruptedException e) {
          log.error("生产消息时产生异常!");
        }
      } while (true);
    }
  }

  //消费者
  static class Consumer extends Thread {
    Consumer(String name) {
      super.setName(name);
    }

    @Override
    public void run() {
      do {
        try {
          //构造SemaphoreUnit
          SemaphoreUnit unit1 = new SemaphoreUnit();
          SemaphoreUnit unit2 = new SemaphoreUnit();
          unit1.setSemaphore(mutex);
          unit1.setT(1);
          unit1.setD(1);
          unit2.setSemaphore(full);
          unit2.setT(1);
          unit2.setD(1);
          Swait(this.getName(), unit1, unit2);
          log.info("消费了一条消息:【{}】", buffer.remove(0));
          //Thread.sleep(1000);
          //替换资源,释放一个empty信号量
          unit2.setSemaphore(empty);
          Ssignal(this.getName(), unit1, unit2);
        } catch (InterruptedException e) {
          log.error("消费消息时产生异常!");
        }
      } while (true);
    }
  }

  public static void Swait(String id, SemaphoreUnit... list) throws InterruptedException {
    lock.tryLock(1, TimeUnit.SECONDS);

    while (true) {
      //如果资源小于分配下限值,就挂起线程,并将线程插入到condition的队列中
      int count = 0;
      for (SemaphoreUnit unit : list) {
        if (unit.getSemaphore().availablePermits() >= unit.getT()) {
          count++;
        } else {
          break;
        }
      }
      if (count == list.length) {
        break;
      }
      log.info("线程【{}】被挂起。", id);
      condition.await();
      log.info("被挂起的线程【{}】被唤醒执行。", id);
    }

    log.info("为线程【{}】分配资源!", id);
    for (SemaphoreUnit unit : list) {
      unit.getSemaphore().acquire(unit.getD());
    }

    lock.unlock();
  }

  public static void Ssignal(String id, SemaphoreUnit... list) throws InterruptedException {
    log.info("线程【{}】执行了释放资源", id);

    lock.tryLock(1, TimeUnit.SECONDS);
    //释放资源
    for (SemaphoreUnit unit : list) {
      unit.getSemaphore().release(unit.getD());
    }
    //唤醒等待队列中的所有线程
    condition.signalAll();
    
    lock.unlock();
  }

  public static void main(String[] args) {
    Producer p1 = new Producer("p1");
    Producer p2 = new Producer("p2");
   	
    Consumer c1 = new Consumer("c1");
    Consumer c2 = new Consumer("c2");
    
    p1.start();
    p2.start();
    c1.start();
    c2.start();
  }
}


​ 下图是部分的执行结果:

部分执行结果

​ 代码中的SemaphoreUnit,其中设置的申请资源的数量与资源分配下限都是1,这是因为生产者-消费者问题的特性,细心的朋友也发现了,这种特殊的情况下,信号量集等价与AND型信号量。

​ 但是,我们也可以通过设置t的值,来保证系统中信号量不会被分配完毕,比如可以设置empty信号量对应的t值为2,这样可以限制缓冲池中最多只有49条消息,信号量集的使用还是很灵活的。

总结

​ 本文我们使用Java模拟了信号量集,并用信号量集解决了生产者-消费者问题,但是这里只讲了信号量的Swait(S,1,1)的情况,这种情况下,信号量集也就等价于AND型信号量了,但是信号量集还有一些特殊的操作。

​ 下面我们对信号量集的一些特殊操作做一个说明:

  1. Swait(S, d, d):一次申请多个资源,此时资源下限等于申请的数量,也就是只要资源足够,就可以申请;
  2. Swait(S, 1, 1):这也是我们上面使用的,等价于AND型信号量;
  3. Swait(S, 1, 0):这是一种特殊且很有用的信号量操作,可以用来做开关,当 S>=1时,允许多个进程进入特定区,但是当S变为0时,将阻止任何进程进入特定区。

​其中的第三个操作特别适用于解决读者-写者问题,可参考我的另一篇博客。
信号量集是一个当时特别流行的一个应用,但是缺点也很明显:1.资源被严重浪费,严重的恶化了资源的利用率;2.使进程经常会发生饥饿现象(因为个别资源被占用的概率很大,会导致进程因为申请不到所有资源迟迟得不到执行)。但是掌握其对我们理解并发编程也有很大的帮助。


​ 又到了分隔线以下,本文到此就结束了,本文内容是因博主个人兴趣才产生的,皆是自己去实现的(毕竟JUC已经提供了较为完善的并发控制机制😛),因此如果有什么错误,还请批评指正。

​ 本文的所有java代码都已通过测试,对其中有什么疑惑的,可以评论区留言,欢迎你的留言与讨论;另外原创不易,如果本文对你有所帮助,还请留下个赞,以表支持。

​ 希望本文可以帮助你理解加深理解进程同步,也可以帮助你理解Java并发编程,如果对操作系统有兴趣的话,还可以查看我的其他几篇博客,都是OS的干货(目录),喜欢的话还请点赞、评论加关注_

2018-04-14 08:32:36 qq_28808697 阅读数 1400

    基本思想:将进程在整个运行中需要的所有资源,一次性全部分配给进程,待进程使用完后一起释放。(避免死锁状态)在Wait中加入AND条件,又称AND同步或同时wait操作:Swait

    Swait(S1,S2,...Sn) If S1 and Sn>= 1 //所需要的所有资源都满足  

    then

    for i:=1 to n      do Si := Si -1;  endfor    else

    当发现第一个Si  < 1 就把该进程放入等待队列,并将其程序计数器(存放下一个要执行语句的地址)置于Swait操作的开始位置endif

    Ssignal(S1,S2,...,Sn)//将所有占有的资源挨个释放

    for i:= 1 to n do Si := Si + 1;

    将所有等待Si的进程由等待队列取出放入到就绪队列 Endfor;

   )

    

2006-06-09 17:03:00 shenhy 阅读数 1818
一、使用TOP指令进行性能分析。
×××××××××××××××××××××××××××××××××××××××
Load averages: 0.51, 0.54, 0.56
97 processes: 90 sleeping, 7 running
Cpu states:
CPU   LOAD   USER   NICE    SYS   IDLE BLOCK SWAIT   INTR   SSYS
 0     0.02     0.0%     0.0%   0.0%   100.0%   0.0%   0.0%     0.0%   0.0%
 1      1.00     0.0%    0.0%   0.0%  100.0%   0.0%   0.0%     0.0%   0.0%
---   ---- - - - - - - - -----
avg    0.51     0.0%    0.0%    0.0%   100.0% 0.0%    0.0%    0.0%   0.0%
 
Memory: 538436K (470452K) real, 1290028K (1180268K) virtual, 1993180K free Page
# 1/4
 
CPU TTY     PID USERNAME PRI NI   SIZE    RES STATE    TIME %WCPU %CPU COMMAND
 0 pts/tb 22379 root     152 20 22588K   115M run      6:22 1.14 1.13 java
 1   ?       34 root     152 20     0K 7520K run      5:09 0.35  0.35 vxfsd
 0   ?     1374 root     152 20 1544K 2204K run      0:05 0.23 0.23 dmisp
 0   ?       13 root     152 20     0K   192K run      0:00 0.14 0.14 ioconfig
 1   ?    24972 oracle   154 20 32832K 1832K sleep    0:00 0.16 0.14 oracleor
×××××××××××××××××××××××××××××××××××××××
 
说明信息:
第一行的项目依次为平均负载、当前时间。
第二行为进程情况,依次为进程总数、运行进程数、等待进程数、休眠进程数空闲进程数。
第三行为CPU状态,依次为用户占用、优先进程占用、系统占用、闲置进程占用。
第四行为内存状态、交换状态,依次为已用内存、平均可用内存、已用交换区容量、可用交换区容量。然后下面就是和ps相仿的各进程情况列表了。
 
 
我们可以利用Top命令来查看最耗CPU资源的进程。Top命令还会根据进程占用CPU资源的多少而动态改变。总的来说,Top命令的功能强于ps,但需要长久占用前台,所以用户应该根据自己的情况来使用这个命令。 

二、 Top命令的语法格式 
Top [ -SbiInquv ] [ -dcount ] [ -stime ] [ -ofield ] [ -Uusername ] [ number ] 
使用权限:所有使用者 
相关命令:Kill ,ps,stty,mem,renice 
补充说明: 
执行Top指令可显示当前正在系统中执行的程序,并通过它所提供的交互式截面,用功能键加以管理。 
相关参数说明: 
Top 显示系统程序的执行状态,并指定每间隔时间刷新一次信息 
-s 使用累计模式显示系统程序的执行状态 
-b 使用“batch”模式显示系统的执行状态,此时所有的功能键均没有任何作用 
-i 使用“interactive”模式显示系统的执行状态 
-I 显示系统程序的执行状态,不显示空闲进程及已经成为僵尸的进程 
-n 使用“no-interactive” 模式显示系统的执行状态 
-q 仅在root下用,重置Top优先级到-20 
-u 不显示用户UID 
-v 显示版本号 
-dcount 屏幕刷新count次后,Top命令自己也退出 
-stime 设置屏幕刷新的时间间隔time,缺省为5秒 
-ofield 在指定的字段中,排序进程显示域 
-Uusername 仅仅显示username拥有的进程 

操作命令:
d            输入刷新数
u            显示指定用户的程序执行状态
o            改变显示字段的顺序
n 或 #       设置最多要显示几个程序。若设为0,则不作任何限制
空格键       立即更新显示中的数据
h或 ?      显示在线说明
Ctrl+ L       重新显示画面
q            结束程序
s            设置屏幕刷新的时间间隔,单位为秒
k            中止执行中的程序,相当于命令:kill
r            调整执行程序的优先权
e            显示系统的错误列表
I或 i        切换是否显示闲置中及成为僵尸的程序
从上面的介绍中可以看到,Top命令是一个功能十分强大的监控系统的工具,尤其对于系统管理员而言更是如此。一般的用户可能会觉得ps命令其实就够用了,但是Top命令的强劲功能确实提供了不少方便。熟悉并灵活应用Top的 命令,可以大大方便系统管理人员和用户,并收到事半功倍之。
2019-06-06 16:03:43 qq_21433411 阅读数 91
//允许多个读者同时读
//不允许读-写 写-写存在
#include<bits/stdc++.h>
using namespace std;

int RN;//最多允许Rn个读者同时读

int L=RN, mx=1;//空位, 有无写者

void Reader()
{
    do
    {
        Swait(L, 1, 1);//L>=1 L=L-1 //有空位 空位-1
        Swait(mx, 1, 0);//mx>=1 没有人写
        /*****************/
        //读
        /*****************/
        Ssignal(L, 1)//L=L+1 离开
        
    }while(true)
    
}

void Writer()
{
    do
    {
        Swait(mx, 1, 1, L, RN, 0);//mx>=1 没有人写mx=mx-1, L==RN//没有人读
        /************/
        //写
        /************/
        Ssingal(mx, 1);//mx=mx+1
        
    }while(true)
}

int main()
{


    return 0;
}

HP-UNIX常用命令

阅读数 3101

信号量的应用

阅读数 668

进程同步

阅读数 138

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