ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Thread, Runnable, Callable, ThreadPool
    개발/안드로이드 2021. 4. 23. 17:29

    1. Thread

     

    Thread 클래스는 Java 언어에서 비동기 작업시 대표적으로 사용하는 클래스다. 코틀린과 람다를 이용하면 아래와 같이 간단하게 비동기 작업 코드를 짤 수 있어서 짧은 디코딩 작업이나, 연산처리를 할 때 주로 사용된다. 

     

    fun testThread() {
        val thread1 = Thread {
            Thread.sleep(1000)
            Log.d(this.toString(), "this is test thread1")
        }
    
        val thread2 = Thread {
            Log.d(this.toString(), "this is test thread2")
        }
    
        thread1.start()
        thread2.start()
    }

     

    그런데 Thread를 생성하는 작업은 안드로이드 밑단 운영체제에서 pthread를 생성하게 되는 작업이므로 추가적인 메모리를 할당받게 된다. 그래서 Thread를 많이 생성할수록  더 많은 메모리 공간을 차지하게되기 때문에 무한정 만들 수 없다. 

     

    2. Runnable 

     

    앞서 언급한 Thread를 간소화한 형태다. Thread는 클래스인 반면 Runnable은 인터페이스라서 다른 클래스를 상속하고 있는 클래스에 Runnable을 추가할 수 있고 상속 형태 없이 익명 클래스 형태로 바로 써도 된다. 실행은 run() 클래스를 호출해서 가능하다. 

     

    fun testRunnable() {
        val runnable = Runnable { Log.d(this.toString(), "test runnable") }
    
        runnable.run()
    }

     

    3. Callable 

     

    Runnable과 비슷하지만 Callable은 비동기 작업에서 리턴값도 줄 수 있고 결과 값을 연산하지 못하면 Exception을 발생시키기도 한다. 리턴값을 제공해야하는 비동기작업이면 Runnable 말고 Callable을 쓰면 된다. 아래 코드는 Callable에서 정수 값들을 리턴하도록 하고 각각의 합을 로그로 찍은 것이다

     

    fun testCallable() {
        val callableOne = Callable { 1 }
        val callableTwo = Callable { 2 }
        val callableLog = Callable { Log.d(this.toString(), "this is callable log") }
    
        try {
            val merged = callableOne.call() + callableTwo.call()
            callableLog.call()
    
            Log.d(this.toString(), "this is callable merged: $merged")
        } catch (e: Exception) {
            e.printStackTrace()
        }
    }

     

    4. ThreadPool

     

    앞서 설명한 Thread, Runnable, Callable 모두 쓰레드 계열이라서 개별로 메모리를 쓰게돼, 무한정 만들면 메모리 낭비가 발생한다. 그래서 자바에서는 ThreadPool이란 것으로 쓰레드를 관리할 수 있도록 했다.  ThreadPool 관리 객체는 Executors 클래스를 통해서 생성하는데 이 클래스 검색해보면 내부의 함수를 통해 다양한 방식으로 Thread를 관리할 수 있는 것을 확인 할 수 있다. 지면상 모두 설명할 수 없고, 아래 코드에서 쓰는 newFixedThreadPool 함수만 설명하면 쓰레드를 최대 4개만 만들 수 있게 하고 나머지 작업은 재사용하겠다는 듯이다. 새로운 작업을 추가해도 쓰레드는 4개 이상 생기지 않으며 나머지는 재사용되기 때문에 메모리 낭비에 대한 부담 없이 새로운 작업을 추가할 수 있다.

     

    작업은 Callable, Runnable의 형태로 추가 가능하며 submit 함수로 추가한다. 이때 리턴 값은 Future 클래스로 받게되고, get() 함수를 통해 결과 값을 얻을 수 있다.

     

    fun testExecutorService() {
        val cachedPool = Executors.newFixedThreadPool(4)
        val futureOne = cachedPool.submit(Callable { 1 })
        val futureTwo = cachedPool.submit(Callable { 2 })
        val printLog = cachedPool.submit(Runnable {
            Log.d(this.toString(), "something...") })
    
        try {
            val merged = futureOne.get() + futureTwo.get()
            printLog.get()
            Log.d(this.toString(), "merged: $merged")
    
        } catch (e: Exception) {
            e.printStackTrace()
        }
    }

     

    댓글

Designed by Tistory.