Metody klasy uruchamiane w osobnych wątkach

0

Witam
Czytając forum na temat wątków, zastanawiam się czy jest jakiś inny sposób na uruchamianie poszczególnych metod jednej klasy w osobnych wątkach. Czy jest inny na to sposób bardziej wygodny lub bardziej przejrzysty,

Dotychczasowe rozwiązanie przedstawiam w pseudokodzie:

class mysql_conect {

       mysql_test_conekt() {
        Thread t = new Thread() {      
            @Override
            public void run() {
                     ...jakieś polecenia...
                     }}}

      mysql_save_data() {
        Thread t = new Thread() {      
            @Override
            public void run() {
                     ...jakieś polecenia...
                     }}}
}
 
0

Adnotacja @Async ze Springa 3:

http://blog.springsource.com/2010/01/05/task-scheduling-simplifications-in-spring-3-0/

Zaletą @Async jest to, że Spring stosuje pulę wątków, więc jest to wydajniejsze niż tworzenie wątku za każdym razem.

0

Dzięki za tak szybką odpowiedz !!!

Wydaję mi się że Executor nie spełni moich założeń aby jeden obiekt posiadający kilka metod wykonywał je w osobnych wątkach.
Ponieważ z tego co wyczytałem i po-praktykowałem Executor jest świetny do zażądzania wątkami i ilością wątków, w połączeniu z implementacją Callable<V> dzięki czemu otrzymamy wynik po wykonanym działaniu. Executor jest dobry ale nie potrafi zrobić tak.
Przykład
a.send_data - osobny wątke nr1
a.test_polaczenia - osobny wątek nr2
a.get_data - osobny wątek nr 3

musiałbym tworzyć 3 różne obiekty i wyglądało by to tak
b.send_data - osobny wątek nr1
c.test_polaczenia - osobny watek nr 2
d.get_data - osobny watek nr 3

Jeśli jest na to jakiś dobry sposób to proszę napiszcie :P

Ostateczna wersja jaką udało się osiągnąć wygląda następująco w pseudokodzie:

 

class mysql_conect{

public Callable mysql_test_conekt() {
        Callable<boolean> Cal = new Callable() {
            private boolean mysql_test_status;
            @Override
            public boolean call()  
              ...polecenia ....
                return mysql_test_status;
            }
        };
        return Cal;
    }



public Callable mysql_send_data{
        Callable<boolean> Cal = new Callable() {
            private boolean mysql_test_status;
            @Override
            public boolean call()  
              ...polecenia ....
                return mysql_test_status;
            }
        };
        return Cal;
    }

}

Dodam że działa to na podobnej zasadzie jak Thread tylko interfejs Callable zwraca wynik po zakonczeniu operacji co jest bardzo wygodne.

0

Java jest językiem obiektowym, a każdy wątek też jest obiektem. Metody nie są samodzielnym bytem, za to są nimi zadania typu Runnable, Callable<V>, ForkJoinTask<V>, ponieważ po utworzeniu są obiektami. Żeby wrzucić wykonanie każdej metody w osobnym wątku tworzysz tyle pod-zadań1 jednego obiektu ile potrzebujesz i w nich umieszczasz kod wykonawczy takich metod. Twoje metody redukują się wtedy do wykorzystania jednego z executorów do wykonania tych zadań.
Jeżeli chcesz:

  1. wymusić jednowątkowość (nawet na wielordzeniowym CPU), to używasz
  • Executors.newSingleThreadExecutor(),
  1. wymusić konkretną liczbę wątków (bez względu na typ maszyny, w tym jeden wątek), to używasz
  • Executors.newFixedThreadPool(),
  1. wymusić nieograniczoną wielowątkowość (nawet na jednordzeniowym CPU), to używasz
  • Executors.newCachedThreadPool(),
  1. żeby te same zadania były wykonywane wielokrotnie przez te same wątki, to używasz
  • Executors.newScheduledThreadPool()2,
  1. użyć całej mocy maszyny jedno/wieloprocesorowej do jak najszybszego wykonania zadań, to używasz
  • new ForkJoinThreadPool()3.

Dzisiaj odchodzi się od bezpośredniego sterowania przez aplikację wątkami bo jest to nieoptymalne i wrażliwe na błędy. Te pule wątków są zupełnie wystarczające, żeby objąć wszystko co do tej pory we współbieżności wymyślono.

Co do Twojej potrzeby, to zauważ, że wielowątkowe wykonanie połączenia z bazą oraz wysyłanie do niej danych może spowodować, że wysyłka może nastąpić w czasie przeprowadzania połączenia. Jednak możliwość wysyłania pojawi się dopiero wtedy kiedy zadanie połączenia zostanie zakończone i to z sukcesem.
Krótko mówiąc możliwość rozpoczęcia tych zadań zależy od udanego zakończenia innych zadań. Nie wiem więc czy przemyślałeś to co chcesz osiągnąć.

Nie musisz też używać nieczytelnych anonimowych klas wewnętrznych wewnątrz metod (tak jak to pokazałeś z wątkami w pseudokodzie). Wszystkie zadania mogą być jawnymi klasami wewnętrznymi ze swoimi referencjami w postaci pól w obiekcie.
W metodach klasy można je wtedy przejrzyście i elegancko używać.

1 - Runnable, Callable<V> lub ForkJoinTask<V> (RecursiveAction/RecursiveTask<V>)
2 - w przerwach między wykonaniami zadania każdy wątek będzie w stanie idle/halt/lock.
3 - dostępna tylko w Javie 1.7; całkowicie zrywa powiązanie zadania z wątkiem/procesorem.

Szkielet przykładu użycia puli wątków:

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.CancellationException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

public class MySqlConnect
{
	public interface SendProgress
	{
		int getProgress();
	}

	public MySqlConnect()
	{
		//...
		executor = Executors.newFixedThreadPool(3);
		connected = tryConnect();
	}

	private Future<Boolean> tryConnect()
	{
		return executor.submit(mysql_test_connect);
	}

	public void dispose() { executor.shutdown(); }

	//...

	public Future<Boolean> send(final byte[] data) //przykład danych
	{
		final SendTask task = new SendTask(data);
		sending.add(task);
		return executor.submit(task);
	}

	public List<SendProgress> getTaskProgress()
	{
		List<SendProgress> progress = new ArrayList<>(sending.size());
		for(SendTask sendTask: sending)
			progress.add(sendTask);
		return progress;
	}

	/**
	 * Get result from asynchronic task.
	 * Waiting for the task end.
	 * @param <T> result type
	 * @param future asynchronic result
	 * @return synchronic result
	 */
	public <T> T getResultFromFuture(final Future<T> future)
	{
		T result = null;
		try { result = future.get(); } //possible to wait for task end
		catch(InterruptedException | ExecutionException
			| CancellationException ignore)
			{ result = null; }
		return result;
	}

	//jawna
	private class SendTask implements Callable<Boolean>, SendProgress
	{
		private SendTask(final byte[] data) { this.data = data; }

		@Override public Boolean call() throws Exception
		{
			boolean result = false;
			if(isConnected())
			{
				System.out.println("[SendTask] Bytes to send: " + data.length);
				//polecenia...
				++progress;
				//...
			}
			return Boolean.valueOf(result);
		}

		@Override public int getProgress() { return progress; }

		private int progress = 0;
		private final byte[] data;
	}

	//anonimowa z jawną referencją
	private Callable<Boolean> mysql_test_connect = new Callable<Boolean>()
	{
		@Override public Boolean call() throws Exception
		{
			boolean result = false;
			//polecenia...
			return Boolean.valueOf(result);
		}
	};

	//...

	private boolean isConnected()
	{
		final Boolean result = getResultFromFuture(connected);
		return result != null && result.booleanValue();
	}

	//lista do uzyskiwania informacji o postępie
	private final List<SendTask> sending = new ArrayList<>();

	private Future<Boolean> connected;

	private ExecutorService executor;
}
0

Ciekawy artykuł napisałeś. Dziękuję za tak dobry opis działania i przykładowy kod do wykorzystania.
Pozdrawiam

1 użytkowników online, w tym zalogowanych: 0, gości: 1