0

im newbie with multi-threads and im trying to make a program that use it. Technically, it has a class with executor and a worker class. The worker class takes the info to work on, process it, if there are more infos to processes the worker calls the executor again and the program keeps going until the text is finished, after that the workers decrease the AtomicInteger check variable and and if equals zero change the status attribute to DONE and saves it. The executor class receive the attributes needed to the worker class, check if the text/site was proceeded before, if not increments an atomic integer attribute and put the new text/site reference into a Vector for further checks again.

The Executor class:

 public class DoScrapper implements IDoScrapper {
   private static Logger log = LoggerFactory.getLogger(DoScrapper.class);

   private final ThreadPoolExecutor executor = (ThreadPoolExecutor) Executors.newFixedThreadPool(30);



   void doScrapper(final ScrapperWorker scrapperWorker) {
       executor.execute(scrapperWorker);
   }

   @Override
   public void sendToProccess(
           ScrapperEntity entity,
           String urlToProccess,
           String rootUrl,
           Vector<String> urlVisited,
           IRequestService requestService,
           IDoScrapper doScrapper,
           Database database) {

       urlVisited.add(urlToProccess);
       int qtd = entity.addUrlInAction();;
       log.info("sending to procces url {} for keyword {} , with {} sites opened to visited",
               urlToProccess, entity.getKeyword(), qtd);
       ScrapperWorker newWorker = new ScrapperWorker(
               entity, urlToProccess, rootUrl,
               urlVisited, requestService,
               doScrapper, database
       );
       doScrapper(newWorker);
   }

}

The worker class:

    public class ScrapperWorker implements Runnable{
        private static final Logger log = LoggerFactory.getLogger(ScrapperWorker.class);
    
    
        final ScrapperEntity entity;
        final String url;
        final String rootUrl;
        final Vector<String> urlVisited;
        private final IDoScrapper doScrapper;
        private final Database database;
    
    
    
        public ScrapperWorker(ScrapperEntity entity,
                              String url,
                              String rootUrl,
                              Vector<String> urlVisited,
                              IRequestService requestService,
                              IDoScrapper doScrapper,
                              Database database) {
            this.entity = entity;
            this.url = url;
            this.rootUrl = rootUrl;
            this.urlVisited = urlVisited;
            this.requestService = requestService;
            this.doScrapper = doScrapper;
            this.database = database;
        }
    
        @Override
        public void run() {
            log.info("thred doing scrapper for url {} keyword {} ",
                    url, entity.getKeyword());
            .... 
            do actions and can or cannot call based on the check() method doScrapper();
            ...


            decreaseUrlInActionAndVerifyStatus();
        }
    
       
    
        private void decreaseUrlInActionAndVerifyStatus() {
            int sitesToCheck = entity.decreaseUrlInAction();
            log.info("decreasing visitedSites on entity {}, sites to check {}, sites visited {}  ",
                    entity.getId(), sitesToCheck,  urlVisited.size());
    
    
            if(sitesToCheck == 0 ){
                log.info("last url, setting STATUS as done ... sitess visited: {} ", urlVisited.size());
                entity.setStatus(StatusScrapper.DONE);
                database.updateData(entity);
            }
    
        }    
    
    private boolean check( String urlComplete, Vector<String> urlVisited) {
        return urlComplete != null
                && isNotVisitedUrl(urlComplete, urlVisited);
    }
    private boolean isNotVisitedUrl(String url, Vector<String> vector) {

        return ! vector.contains(url);

    }
    


    }


The methods on entity class to decrease and increase:

    private volatile AtomicInteger countUrlInAction;

    public int addUrlInAction(){
        return countUrlInAction.addAndGet(1);
    }

    public int decreaseUrlInAction() {
        return countUrlInAction.decrementAndGet();
    }

The problem im having its that sometimes, principally when I put more Threads on the thread pool of the executor or more parallel processing [ more entities to be processed in parallel ] the AtomicInteger become random, sometimes stopping in 1, others in 7 and so on, and the status never change to DONE. Isn't for AtomicInteger to increase and decrease correctly in this case or Im missing something? If I cant use AtomicInteger for this, which way is better to do this check?

There aren't any error in the console log, so all threads must be processing all the texts and decreasing the AtomicInteger variable

Myphre
  • 11
  • 2
  • 1
    Your code as pasted cannot possibly work; it would NPE because you never initialize the `countUriInAction` field. Clearly then you've pasted parts of your code and not all of it. One explanation is that you keep re-initializing this field. It is important to paste the _actual_ code that fails, not your guess at the 'relevant parts' - after all, you're asking questions, it's likely you don't really know how everything interacts. – rzwitserloot Jun 19 '23 at 00:19
  • 1
    There are a number of glaring issues with the few things you have pasted: [1] Making a final reference `volatile` is completely useless - `countUrlInAction` shouldn't be volatile (or, if you're re-assigning it, don't re-assign it). [2] You are writing to other fields with abandon. That's not how thread safety works. You can't share fields between threads __at all__ without things going wonky unless you establish Happens-Before/Happens-After, which you aren't. [3] Your operations aren't atomic. Most likely, AtomicInteger isn't part of the solution. – rzwitserloot Jun 19 '23 at 00:22

2 Answers2

0

You should use decreaseUrlInActionAndVerifyStatus(); always in a finally block. Otherwise RuntimeExceptions occurring during processing will lead to omitting the decrementing. Why these Exceptions occur is another question, but I suppose more Threads will lead to other problems, perhaps the changing functions on entity are not properly synchronized.

aschoerk
  • 3,333
  • 2
  • 15
  • 29
  • 1
    Does the -1 mean, you tested and it did not work? I would be very much interested, how you solved the problem, since I am using AtomicIntegers very much and they never failed. – aschoerk Jun 19 '23 at 04:29
  • Hi, sorry for the delay, in my mind i answered but now i see i do not. I think I dont put -1 in your awnsred, I considered it. Anyway, I put and anwsred myself about what and happened or should happned. – Myphre Aug 14 '23 at 11:59
0

Just to answer the question, i dont know if was some cache from linux or from the Intelij Idea, but after I closed it and rebooted the system everything works fine. [ normally i keep my computer on so enven after a day the error keeped happening ]

About some exception in the thread, i could not find anything. For precautions, i put the finally block too.

Thx for the help. And sorry for the delay in the return.

Myphre
  • 11
  • 2
  • 1
    I think this is not a general solution. I agree with the commenters on your question, that you should provide full code that exhibits the unexpected behaviour. From my perspective it looks like you don't use any thread safety principles as locking and unlocking, when you are accessing and modifying variables from multiple threads. – Amuoeba Aug 16 '23 at 07:48