DATABASE-Crontab

Crontab

  • 유닉스 OS 계열에서 특정 시간에 특정 작업을 해야하는 경우 사용하는 스케쥴러입니다.

  • crontab basic

    • 스케쥴 설정

      아래의 커멘드를 입력하면 스케줄을 설정할수 있는 vi 에디터 페이지가 생성된다. 여기에 어떤 주기로 어떤 파일을 실행할지에 대한 리스트를 작성해주면 된다.

      1
      $ crontab -e
    • 스케쥴 리스트 확인

      현재 crontab의 스케쥴을 확인할 수 있다.

      1
      $ crontab -l
  • 주기 설정

    1
    2
    3
    4
    5
    vi time.py

    import datetime
    today = datetime.datetime.now()
    print(str(today))
    • crontab -e 설정

    • * * * * * ->
      분(0-59) | 시간(0-23) | 일(1-31) | 월(1-12) | 요일(0-7)

      • 요일에서 0과 7은 일요일

      • 2분 간격으로 실행

        1
        */2 * * * * python3 /home/ubuntu/time.py >> time.txt
      • 매시 10분에 실행

        1
        10 * * * * python3 /home/ubuntu/time.py >> time.txt
      • 매시 10분과 20분에 실행

        1
        10,20 * * * * python3 /home/ubuntu/time.py >> time.txt
      • 매일 5시 10분과 20분에 실행

        1
        10,20 5 * * * python3 /home/ubuntu/time.py >> time.txt
      • 일요일 5시 10분과 20분에 실행

        1
        10,20 5 * * 0 python3 /home/ubuntu/time.py >> time.txt
      • 5시에서 10시까지 매시에 5분마다 time.py를 실행하고 결과를 time.txt에 저장

        1
        */5 5-10 * * 0 python3 /home/ubuntu/time.py >> time.txt
  • time zone 변경

    타임존 변경

    • 현재 사용 시간 확인

      $ timedatectl

    • 사용하는 파일 심볼릭 링크확인

      $ ls -l /etc/localtime

    • 사용 할수 있는 타임존 확인

      $ timedatectl list-timezones | grep Asia

    • 타임존 변경

      $ sudo timedatectl set-timezone Asia/Seoul

    • 심볼릭 링크 수정

      $ sudo unlink /etc/localtime

      $ sudo ln -s /usr/share/zoneinfo/Asia/Seoul /etc/localtime

  • vim 에디터 인코딩 변경

    $ vi .vimrc

    set encoding=utf-8

  • crontab 로그 확인

    아래의 명령으로 crontab의 시스템 로그를 확인 할수 있습니다.

    $ grep CRON /var/log/syslog

  • crontab 에러 확인 및 해결

    에러가 발생하면 아래와 같은 에러 로그를 확인할수 있습니다.

    Mar 4 07:42:01 ip-172-31-3-64 CRON[7494]: (CRON) info (No MTA installed,
    discarding output)

- MTA : Mail Transfer Agent
- 에러 메시지 확인하는 방법 1
    - $ sudo apt-get install postfix
    - $ cat /var/mail/ubuntu
- 에러 메시지 확인하는 방법 2
    - $ sudo apt install mailutils
    - $ mail
- crontab에서 실행되는 python 환경

    - crontab 에서는 .bash_profile이 실행되지 않기때문에 pyenv 환경이 적용되지 않습니다.

        <!--hexoPostRenderEscape:<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">vi version.py</span><br><span class="line"></span><br><span class="line">import sys</span><br><span class="line">print(sys.version.split(&quot; &quot;)[0])</span><br></pre></td></tr></table></figure>:hexoPostRenderEscape-->

        <!--hexoPostRenderEscape:<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">crontab -e</span><br><span class="line"></span><br><span class="line">\* \* \* \* \* python /home/ubuntu/version.py &gt;&gt; version.txt</span><br></pre></td></tr></table></figure>:hexoPostRenderEscape-->

- pyenv 환경에 있는 python으로 실행

    -  직접 python 경로 입력

       \* \* \* \* \* /home/ubuntu/.pyenv/versions/python3/bin/python /home/ubuntu/version.py >> version.txt

    - PATH 설정

      PATH=/usr/local/bin/:/sbin:/bin:/usr/sbin:/home/ubuntu/.pyenv/versions/python3/bin

      \* \* \* \* \* python /home/ubuntu/version.py >> version.txt

Comment and share

MongoDB

MongoDB Setting

  1. MongoDB 설치 및 설정

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    sudo apt update -y
    sudo apt upgrade -y
    sudo apt install -y mongodb
    sudo systemctl status mongodb
    sudo vi /etc/mongodb.conf
    # bind_ip = 0.0.0.0
    # auth = true
    # 패스워드 설정
    $ mongo
    > use admin
    > db.createUser({ user: "rada", pwd: "radapw", roles: [ "root" ] })
    > quit()
    $ sudo systemctl restart mongodb
    # aws 인스턴스 27017 포트 접속 허용

    mongo 명령어를 입력하면 mongo shell에 접속됩니다.

  2. Install Robomongo

    https://robomongo.org/ 페이지에서 경로에서 ROBO 3T 다운로드 후 설치 합니다.

  3. Connection

    MongoDB Connections에서 ip를 입력하여 서버의 mongoDB에 접속합니다.


문법

ref : https://docs.mongodb.com/v3.6/reference/

  • Create Database

    • mongo 라는 이름의 데이터 베이스 생성

      1
      use mongo
    • 현재 사용중인 데이터 베이스 확인

      1
      db
    • database list 확인

      1
      show dbs
      • 데이터 베에스를 생성후에 최소 1개이상의 document를 추가해야 생성된 데이터 베이스가 보인다
- document 생성
    <!--hexoPostRenderEscape:<figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">use</span> mongo</span><br><span class="line">db.user.insert(&#123;<span class="string">&quot;name&quot;</span>:<span class="string">&quot;alice&quot;</span>, <span class="string">&quot;age&quot;</span>:<span class="number">20</span>, <span class="string">&quot;email&quot;</span>:<span class="string">&quot;alice@gmail.com&quot;</span>&#125;)</span><br></pre></td></tr></table></figure>:hexoPostRenderEscape-->
  • Delete Database

    • 현재 사용중인 데이터 베이스 삭제
      1
      db.dropDatabase()
  • Create Collection

    refer https://docs.mongodb.com/v3.6/reference/method/db.createCollection/

    • syntax

      1
      db.createCollection(collection 이름, [option])
    • option

      • capped : true로 설정하면 collection의 최대 용량을 설정 (최대 용량의 크기는 size 옵션으로 설정), 설정된 최대용량 이상으로 데이터가 입력되면 오래된 데이터 부터 자동으로 삭제됩니다.
      • autoIndex : true로 설정하면 _id 필드에 index가 자동으로 생성됩니다.
      • size : 숫자 데이터를 사용하며 collection의 최대 사이즈를 byte 단위로 지정
      • max : 숫자 데이터를 사용하며 최대 document 갯수를 설정
    • user 컬렉션을 생성

      1
      db.createCollection("user")
    • autoIndex와 max 옵션을 설정하여 info 컬렉션을 생성

      1
      2
      db.createCollection("info1", { autoIndexId: true, capped: true, size: 500, max:5 })
      db.createCollection("info2", { autoIndexId: true, capped: true, size: 50, max:5 })
    • createCollection을 사용하지 않고 article 컬렉션을 생성

      1
      db.articles.insert( {"title":"data science", "contents":"mongodb" } )
    • 컬렉션 리스트 확인

      1
      show collections
  • Delete Collection

    • articles 컬렉션 삭제
      1
      db.articles.drop()
  • Make Document

    • syntax

      1
      db.<collection_name>.insert(<document>)
    • info 컬렉션에 document 추가

      1
      2
      3
      db.info1.insert({ "subject":"python", "level":3 })
      db.info1.insert({ "subject":"web", "level":1 })
      db.info1.insert({ "subject":"sql", "level":2 })
    • 한번에 여러개의 document 추가

      • max:5 옵션 제한에 걸려 5개의 데이터가 info1에 들어간다.

        1
        2
        3
        4
        5
        6
        7
        8
        db.info1.insert( [
        { "subject":"python", "level":3 },
        { "subject":"web", "level":1 },
        { "subject":"sql", "level":2 },
        { "subject":"python", "level":3 },
        { "subject":"web", "level":1 },
        { "subject":"sql", "level":2 },
        ])
      • size:50 옵션 제한에 걸려 4개의 데이터가 info2에 입력된다.

        1
        2
        3
        4
        5
        6
        7
        db.info2.insert( [
        { "subject":"python", "level":3 },
        { "subject":"web", "level":1 },
        { "subject":"sql", "level":2 },
        { "subject":"python", "level":3 },
        { "subject":"web", "level":1 },
        ])
      • info collection 생성하면서 document 추가

        1
        2
        3
        4
        5
        6
        7
        8
        db.info.insert( [
        { "subject":"python", "level":3 },
        { "subject":"web", "level":1 },
        { "subject":"sql", "level":2 },
        { "subject":"java", "level":3 },
        { "subject":"html", "level":1 },
        { "subject":"css", "level":2 },
        ])
  • Delete Document

    • level2인 데이터 삭제 : 제약조건이 걸려있는 컬렉션의 도큐먼트는 삭제가 안됩니다.
      1
      db.info.remove( {level:2} )
  • Find

    ref : https://docs.mongodb.com/manual/reference/method/db.collection.find/index.html

    • syntax

      1
      db.collection.find(query, projection)

      query : document 조회 조건을 설정. 모든 document를 조회 할때는 ({})를 사용

      projection : document를 조회할때 보여지는 필드(컬럼)를 정의

    • query

      • 기본 document 조회

        • info 컬렉션에 있는 모든 document 조회

          1
          2
          db.info.find()
          db.getCollection('info').find({})
        • subject가 python인 document 조회

          1
          db.info.find({"subject": "python"})
      • 비교 연산자

        ref : https://docs.mongodb.com/v3.6/reference/operator/query/

        • level이 2 이하인 document를 조회

          1
          db.info.find({"level": {$lte: 2} })
        • level이 3 이상인 document를 조회

          1
          db.info.find({"level": {$gte: 3} })
        • subject가 java와 python을 포함하는 document 조회

          1
          db.info.find( {"subject": {$in: ["java", "python"]}} )
      • 논리 연산자

        1. $or : 조건중 하나라도 true이면 true
        2. $and : 모든 조건이 true이면 true
        3. $not : 조건중 하나라도 false이면 true
        4. $nor : 모든 조건이 false이면 true (or와 반대 개념)
        • subject가 python이고 level이 3이상인 document 조회

          1
          db.info.find({ $and: [ { "subject":"python" }, { "level": {$gte: 3} } ] })
        • subject가 python이아니고 level이 1이하가 아닌 document 조회

          1
          db.info.find({ $nor: [ { "subject":"python" }, { "level": {$lte: 1} } ] })
        • level이 2보다 크지 않은 document 조회 (2 포함)

          1
          db.info.find({ "level": { $not: {$gt: 2} } })
      • $where

        • $where 연산자를 사용하면 자바스크립트 표현식 사용이 가능합니다.

        • level이 1인 document 조회

          1
          db.info.find( { $where: "this.level == 1"} )
    • projection

      document를 조회할때 보여지는 필드(컬럼)를 정의합니다.

      • Basic

      • subject와 comments만 출력되도록 find

        • 설정을 true 값을 설정하던가 false 값을 설정합니다. ( _id는 따로 설정을 안하면 true )
          1
          2
          3
          db.info.find({},{"_id":false, "level":false})
          db.info.find({},{"subject":true, "level":true})
          db.info.find({},{"_id":false, "subject":true, "level":true})
  • Find Method

    find method를 사용하면 find를 사용한 document의 결과를 가공하여 출력할수 있습니다.

    • sort

      • document를 정렬시켜 줍니다.
      • ‘sort({key: value})’ 와 같은 포멧으로 사용을 하며 key는 정렬할 필드명을 작성하고, value는 오름차순은 1, 내림차순을 -1을 넣어주면 됩니다.
    • info 컬렉션의 document를 level 오름차순으로 정렬

      1
      db.info.find().sort({"level":1})
    • info 컬렉션의 document를 level 내림차순으로 정렬

      1
      db.info.find().sort({"level":-1})
    • level을 기준으로 내림차순으로 정렬한 후 subject를 기준으로 오름차순으로 정렬

      1
      db.info.find().sort({"level":-1, "subject":1})
    • limit

      • limit을 사용하면 document출력 결과의 수룰 제한할수 있습니다.

      • document의 결과를 3개 까지만 출력

        1
        db.info.find().limit(3)
      • document의 결과를 level로 내림차순으로 정렬하고 3개까지만 출력

        1
        db.info.find().sort({"level":-1}).limit(3)
    • skip

      skip을 검색한 document의 결과의 시작부분을 설정할때 사용합니다.

      • document를 3번째 부터 출력

        1
        db.info.find().skip(2)

        limit, skip을 함께 사용해서 mysql의 limit과 같이 사용할수 있습니다.

  • update

    ref : https://docs.mongodb.com/manual/reference/command/update/index.html

    • syntax

      1
      db.collection.update( query, update, { upsert: <bool>, multi: <bool> })
      • upsert : insert와 update의 합성어 (데이터가 있으면 update, 없으면 insert 한다는 의미)

      • multi : true로 설정되면 여려개의 document를 수정합니다. 기본값은 false

    • 특정 document를 새로운 document로 수정하기

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      db.info.update(
      { "subject": "html" },
      { "subject": "sass", "level":2}
      )

      db.info.update(
      { "subject": "less" },
      { "subject": "less", "level": 2},
      { "upsert": true}
      )
  • $set, $unset

    • $set을 사용하면 특정 document의 필드를 수정할수 있습니다.

    • $unset를 사용하면 특정 document의 필드 제거할수 있습니다.

    • python의 level을 3으로 수정 (한개의 데이터만 수정)

      1
      db.info.update( { subject: "java" }, { $set: { level: 4 } } )
    • level 2를 level 1로 수정 (여러개의 데이터 수정)

      1
      2
      3
      4
      5
      db.info.update(
      { level: 2 },
      { $set: { level: 1 } },
      { multi: true }
      )
    • subject가 sass인 document의 level필드 삭제

      1
      2
      3
      4
      db.info.update(
      { subject: "sass" },
      { $unset: {level: 1} }
      )

      level: 1의 1은 true를 의미합니다.

    • level이 2이하인 데이터를 1로 수정하기

      1
      2
      3
      4
      5
      db.info.update(
      { level: {$lte: 2} },
      { $set: {level: 1} },
      { multi: 1 }
      )
    • level이 없는 데이터 level 추가하기

      1
      2
      3
      4
      5
      db.info.update(
      { level: {$exists: false} },
      { $set: {level: 2} },
      { multi: 1 }
      )
  • Function

    자바스크립트 문법으로 함수 작성이 가능합니다.

    • skip 함수

      1
      2
      3
      4
      5
      var showSkip = function(start){
      return db.info.find().skip(start)
      }

      showSkip(3)

Comment and share

Multi-Thread Programming

Process and Thread

  • Process: OS로부터 메모리를 할당받아 실행중인 프로그램
  • Thread: 프로세스 동작의 최소 단위로, 하나의 프로세스는 여러 스레드로 이루어질 수 있다.

멀티스레드 프로그래밍의 장단점

  • 장점
    • 여러 동작을 병렬적으로 처리하여 CPU의 사용률 향상
    • 시간이 걸리는 동작을 분리하여 프로그램의 응답성 향상
  • 단점
    • Context Switching 오버헤드 발생
    • 스레드 제어의 어려움

스레드 구현

  • 스레드 생성 방법

    1. 익명함수

      1
      2
      3
      4
      5
      Thread threadOne = new Thread(new Runnable() {
      public void run() {
      System.out.println("Hello Thread!");
      }
      });
    2. 람다식

      1
      2
      3
      Thread threadTwo = new Thread(() -> {
      System.out.println("Hello Again, Thread!");
      });
    3. Thread Class 상속

      1
      2
      3
      4
      5
      6
      class MyThread extends Thread {
      public void run() {
      System.out.println("Hello Again Again, Thread!");
      }
      }
      Thread threadThree = new MyThread();
  • 스레드 실행

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    Thread threadOne = new Thread(() -> {
    for (int i = 0; i < 10; i++) {
    System.out.print("1");
    }
    });

    Thread threadTwo = new Thread(() -> {
    for (int i = 0; i < 10; i++) {
    System.out.print("2");
    }
    });

    threadOne.start();
    threadTwo.start();
    System.out.println("Done!");

    Thread 객체는 일회성이기 때문에 여러 번 start()메소드를 호출 할 수 없다


스레드의 상태 및 제어

  • 스레드의 상태

    • getState() 메소드로 스레드의 상태를 확인할 수 있다.

      열거형 상수 설명
      NEW start() 메소드가 아직 호출되지 않음
      RUNNABLE JVM에 의해 실행 가능한 상태
      BLOCKED 객체가 블락된 상태
      WAITING sleep(), wait(), join() 등에 의해 무한히 대기 중인 상태
      TIMED_WAITING sleep(), wait(), join() 등에 의해 정해진 시간 동안 대기 중인 상태
      TERMINATE run() 메소드가 종료된 상태
  • 스레드의 우선순위 제어

    1
    2
    3
    public final static int MIN_PRIORITY = 1;
    public final static int NORM_PRIORITY = 5;
    public final static int MAX_PRIORITY = 10;
    메소드 설명
    void setPriority(int newPriority) 새로운 우선순위로 설정한다.
    int getPriority() 우선순위를 반환한다.
  • sleep()을 이용한 제어

    1
    2
    Thread.sleep(1000); // ms
    Thread.sleep(100, 200); // ms + ns
  • join()을 이용한 스레드 조인

    • 스레드 동작을 동기화하기 위해 사용

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      Thread t1 = new Thread(() -> System.out.println("A"));
      Thread t2 = new Thread(() -> System.out.println("B"));

      t1.start(); // t1 thread start

      t1.join(); // t1 thread 종료시 까지 다른 thread 대기

      t2.start(); // t2 thread start

      t2.join(100); // 100ms 기다린 후에도 t2가 종료되지 않으면 다른 쓰레드 시작

      System.out.println("C");
  • interrupt()를 이용한 대기 중지

    • 기존 동작을 방해하고 반응을 강제하는 메소드
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      Thread tSleep = new Thread(() -> {
      try {
      Thread.sleep(1000);
      } catch (InterruptedException e) {
      System.out.println("Interrupted");
      }
      });

      tSleep.start();

      try {
      Thread.sleep(500);
      } catch (InterruptedException e) {
      e.printStackTrace();
      }

      tSleep.interrupt();
  • yield()를 이용한 상태 제어

    • sleep()과 달리 곧바로 RUNNABLE 상태로 변경
    • 다른 쓰레드에게 양보하고 바로 실행 대기
    • 테스트용으로 쓰임 -> 쓰레드의 동작이 명확하지 않음(바로 양보하지 않는다)
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      new Thread(() -> {
      for (int i = 0; i < 20; i++) {
      if (i % 2 == 0) {
      System.out.print("1");
      } else {
      Thread.yield();
      }
      }
      }).start();

      new Thread(() -> {
      for (int i = 0; i < 20; i++) {
      if (i % 2 == 0) {
      System.out.print("2");
      } else {
      Thread.yield();
      }
      }
      }).start();
  • 스레드의 종료

    • run() 메소드의 종료
    • stop() 메소드 호출 (deprecated)

데몬 스레드

  • 정의 : 다른 스레드가 종료될 경우 함께 종료되는 보조 스레드

  • 활용법

    • 보통 대기하며 동작하는 무한 루프로 구현
    • 일정 시간 마다 동작
    • interrupt에 의해서 동작
  • setDaemon() 메소드로 데몬 스레드로 설정

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    class DaemonThread extends Thread {
    public DaemonThread() {
    this.setDaemon(true);
    }

    @Override
    public void run() {
    while (true) {
    try {
    Thread.sleep(1000);
    } catch (InterruptedException e) {
    e.printStackTrace();
    }
    System.out.println("Daemon Thread Run");
    }
    }
    }

데이터 공유와 동기화

  • 스레드간 데이터 공유 시 신뢰성에 문제가 발생할 수 있음

    결과는 1000000이 나오지 않는다 -> count값이 동기화되지 않았기 때문

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    class Counter {
    int count = 0;
    }
    public class Main {
    public static void main(String args[]){
    Counter counter = new Counter();

    for (int i = 0; i < 1000; i++) {
    new Thread(() -> {
    for (int j = 0; j < 1000; j++) {
    counter.count = counter.count + 1;
    }
    }).start();
    }

    try {
    Thread.sleep(1000);
    } catch (InterruptedException e) {
    e.printStackTrace();
    }

    System.out.println(counter.count);
    }
    }
  • synchronized 키워드 사용

    1
    2
    3
    synchronized void method() {
    // 공유 데이터 사용
    }
    1
    2
    3
    4
    5
    void method() {
    synchronized(sharedObj) {
    // 공유 데이터 사용
    }
    }
    • Example

      • this 객체의 Intrinsic Lock을 이용한 구현

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        class Counter {
        private int count = 0;
        public int increaseCount() {
        synchronized (this) {
        return ++count; // 읽고, 수정하고, 쓰는 작업
        }
        }

        public int getCount() {
        return count;
        }
        }
      • 메소드에 synchronized 키워드 사용

        • synchronized 키워드가 사용된 메소드를 호출하기 위해서는 해당 객체를 소유해야만 호출이 가능. 소유하지 못하면 Blocking
          1
          2
          3
          4
          5
          6
          7
          8
          9
          10
          class Counter {
          private int count = 0;
          public synchronized int increaseCount() {
          return ++count; // 읽고, 수정하고, 쓰는 작업
          }

          public int getCount() {
          return count;
          }
          }
  • wait(), notify(), notifyAll()

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    class WorkObject {
    public synchronized void methodA() {
    System.out.println("ThreadA의 methodA() 작업 실행");
    notify(); // 일시정지 상태에 있는 ThreadB를 실행 대기상태로 만듬
    try {
    wait(); // ThreadA를 일시 정지 상태로 만듬
    } catch (InterruptedException e) {
    e.printStackTrace();
    }
    }

    public synchronized void methodB() {
    System.out.println("ThreadB의 methodB() 작업 실행");
    notify(); // 일시정지 상태에 있는 ThreadA를 실행 대기상태로 만듬
    try {
    wait(); // ThreadB를 일시 정지 상태로 만듬
    } catch (InterruptedException e) {
    e.printStackTrace();
    }
    }
    }


    class ThreadA extends Thread {
    private WorkObject workObject;

    ThreadA(WorkObject workObject) {
    this.workObject = workObject;
    }

    public void run() {
    for(int i=0; i<10; i++) {
    workObject.methodA(); // 공유객체의 methodA를 반복적으로 호출
    }
    }
    }

    class ThreadB extends Thread{
    private WorkObject workObject;

    ThreadB(WorkObject workObject) {
    this.workObject = workObject;
    }

    public void run() {
    for(int i=0; i<10; i++) {
    workObject.methodB(); // 공유객체의 methodA를 반복적으로 호출
    }
    }
    }

    class Main {
    public static void main(String[] args) {
    WorkObject sharedObject = new WorkObject();

    ThreadA threadA = new ThreadA(sharedObject);
    ThreadB threadB = new ThreadB(sharedObject);

    threadA.start();
    threadB.start();
    }
    }

세마포어

  • 사전적 의미 : 횟대(깃발)

  • n개의 깃발을 놓고, 여러 스레드가 경쟁하도록 하는 sync 기법

  • n = 1이면, BinarySemaphore라고 하며, Lock과 유사하게 동작

  • 메소드

    • release(int permits) : permits만큼의 자원 개수 반환
    • acquire(int permits) : permits만큼의 자원 사용 (Blocking O)
    • tryAcquire(long timeout, TimeUnit unit) : unit단위의 timeout이후 자원을 얻지 못하면 false, 얻으면 true (Blocking X)
    • acquireUninterruptibly() : 자원을 얻지못해도 interrupt 발생 X
  • Example

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    public class Main {
    public static void main(String[] args) {
    Semaphore sem = new Semaphore(1);

    sem.release(10);
    sem.tryAcquire();
    System.out.println(sem.availablePermits());

    try { // Blocking으로 동작
    sem.acquire(12);
    } catch (InterruptedException e) {
    e.printStackTrace();
    }

    sem.acquireUninterruptibly(); // interrupt에 반응하지 않음
    try {
    System.out.println(sem.tryAcquire(100, TimeUnit.MILLISECONDS)); // Blocking 하지 않고, 실패하면 false
    } catch (InterruptedException e) {
    e.printStackTrace();
    }
    sem.release();

    System.out.println(sem.availablePermits());
    }
    }
  • Dining Philosopher (식사하는 철학자)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    class Philosopher extends Thread {
    private final int id;
    private final Fork left;
    private final Fork right;

    public Philosopher(int id, Fork left, Fork right) {
    this.id = id;
    this.left = left;
    this.right = right;
    }

    @Override
    public void run() {
    while (true) {
    try {
    left.acquire();
    System.out.println(id + ": left taken");
    } catch (InterruptedException e) {
    e.printStackTrace();
    }

    try {
    if(!right.tryAcquire(1000, TimeUnit.MILLISECONDS)){
    left.release();
    Thread.yield();
    continue;
    }
    System.out.println(id + ": right taken");
    } catch (InterruptedException e) {
    e.printStackTrace();
    }

    try {
    Thread.sleep(2000);
    System.out.println(id + "is eating");
    } catch (InterruptedException e) {
    e.printStackTrace();
    }

    left.release();
    right.release();
    Thread.yield();
    }
    }
    }

    class Fork extends Semaphore {

    public Fork() {
    super(1);
    }
    }
    public class Main {
    public static void main(String[] args) {
    Philosopher[] phils = new Philosopher[5];
    Fork[] forks = new Fork[5];

    for (int i = 0; i < 5; i++) {
    forks[i] = new Fork();
    }

    for (int i = 0; i < 5 - 1; i++) {
    phils[i] = new Philosopher(i, forks[i], forks[(i+1) % 5]);
    }

    phils[4] = new Philosopher(4, forks[0], forks[4]);
    for (int i = 0; i < 5; i++){
    phils[i].start();
    }
    }
    }

JCF와 Thread

  • Vector VS List

    • List
      • 메소드들이 Synchronized 되지 않음
      • 속도는 빠르다.
    • Vector
      • 메소드들이 Synchronized 되어 있음
      • 속도는 느리다.
  • List를 Synchronized 되게 하는 방법

    • Collections의 synchronizedList() 사용
      1
      2
      List<Integer> list1 = new ArrayList<>();
      List<Integer> list2 = Collections.synchronizedList(list1);

      Thread Pool

  • Thread Pool이란?

    • 미리 생성해 둔 Thread의 집합을 스레드 풀이라고 한다.
    • 미리 Thread를 생성해 두고, 작업만 Thread에 할당하여 동작
    • 사용 이유
      • Thread를 직접 만들어 사용할 경우, Multi-Thread 작업을 계속 할 때, Thread 생성/삭제 오버해드가 크다.
      • 배치작업 (모아두고 한 번에 처리하는 작업)에 유용하여 많이 사용된다.
  • Thread Pool 생성

    • Single Thread Pool

      1
      ExecutorService pool = Executors.newSingleThreadExecutor();
      • Thread가 1개인 ThreadPool 생성(SingleThread)
      • 실패 시, 새로운 Thread를 생성하지 않음
    • Cached Thread Pool

      1
      ExecutorService pool = Executors.newCachedThreadPool();
      • 초기 Thread 0개
      • 코어 Thread 0개 (일하지 않아도 종료시키지 않는 Thread)
      • 요청 작업보다 Thead가 부족하면 새 스레드 생성
      • 60초동안 일하지 않는 Thread 제거
    • Fixed Thread Pool

      1
      ExecutorService pool = Executors.newFixedThreadPool(int nThreads);
      • 최대 Thread nThreads개
      • 코어 Thread nThreads개
      • 작업하지 않는 Thread도 제거하지 않고 동작
    • Scheduler Thread Pool

      1
      ScheduledExecutorService pool = Executors.newScheduledThreadPool(int corePoolSize);
      • 지정된 delay후에 실행하도록함(주기적으로 실행하는 명령을 예약)
  • 작업 생성과 처리 요청

    • 작업 생성

      • Runnable 구현 클래스

        1
        2
        3
        4
        5
        6
        Runnable task = new Runnable(){
        @Override
        public void run(){
        // 작업 내용
        }
        }
      • Callable 구현 클래스

        1
        2
        3
        4
        5
        6
        7
        Callable<T> task = new Callable<T>(){
        @Override
        public T call() throws Exception{
        // 작업 내용
        return T;
        }

        Runnable run()은 Return 값이 없고, Callable call()은 Return 값이 있음

    • 처리 요청

      • 매소드

        리턴 타입 메소드 설명
        void execute(Runnable command) Runnable을 작업 큐에 저장, 작업 처리 결과를 받지 못함
        Future<?> submit(Runnable task) Runnable 작업 큐에 저장, 리턴된 Future를 통해 작업 처리 결과를 얻을 수 있음
        Future submit(Runnable task, V result) Runnable 작업 큐에 저장, 리턴된 Future를 통해 작업 처리 결과를 얻을 수 있음
        Future submit(Callable task) Callable 작업 큐에 저장, 리턴된 Future를 통해 작업 처리 결과를 얻을 수 있음
        • execute()

          • 작업 처리 결과를 반환하지 않는다.

          • 작업 처리 도중 예외가 발생하면 스레드가 종료되고 해당 스레드는 스레드 풀에서 제거된다.

          • 다른 작업을 처리하기 위해 새로운 스레드를 생성한다.

        • submit()

          • 작업 처리 결과를 반환한다.

          • 작업 처리 도중 예외가 발생하더라도 스레드는 종료되지 않고 다음 작업을 위해 재사용

          • 스레드의 생성 오버헤드를 방지하기 위해서라도 submit() 을 가급적 사용한다.

  • Thread Pool 종료

    Thread Pool에 속한 Thread는 데몬 스레드가 아니다

    따라서 주 스레드 종료시 강제종료 되지 않기 때문에 main 스레드가 종료되어도 실행상태가 유지된다.

    어플리케이션을 종료하기 위해서는 스레드 풀을 강제종료시켜야 한다.

    • excutorService.shutdown()

      작업큐에 남아있는 작업까지 모두 마무리 후 종료 (오버헤드를 줄이기 위해 일반적으로 많이 사용.)

    • excutorService.shoutdownNow()

      작업큐 작업 잔량 상관없이 강제 종료

    • excutorService.awaitTermination(long timeout, TimeUnit unit)

      모든 작업 처리를 timeout 시간안에 처리하면 true 리턴 ,처리하지 못하면 작업스레드들을 interrupt()시키고 false리턴

Comment and share

백준 1766 : 문제링크

  • 문제유형

    • 탐색
    • 위상정렬
  • 설명

    • 위상정렬
      1. 진입 차수가 0인 정점을 큐에 삽입
      2. 큐에서 원소를 꺼내 해당 원소와 간선을 제거
      3. 제거 이후 진입 차수가 0이 된 정점을 큐에 삽입
      4. 큐가 빌 때까지 2 ~ 3을 반복
    • 위상정렬 사용이유 : 2번 조건 - 먼저 푸는 것이 좋은 문제가 있는 문제는, 먼저 푸는 것이 좋은 문제를 반드시 먼저 풀어야 한다.)
    • 힙 사용이유 : 3번 조건 - 가능하면 쉬운 문제부터 풀어야 한다
  • 풀이

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    import heapq

    n, m = map(int, input().split())
    array = [[] for i in range(n + 1)]
    indegree = [0] * (n + 1)

    heap = []
    result = []

    for _ in range(m):
    x, y = map(int, input().split())
    array[x].append(y)
    indegree[y] += 1

    for i in range(n+1):
    if indegree[i] == 0:
    heapq.heappush(heap, i)

    result = []

    while heap:
    data = heapq.heappop(heap)
    result.append(data)
    for y in array[data]:
    indegree[y] -= 1
    if indegree[y] == 0:
    heapq.heappuh(heap, y)

    for i in result:
    print(i, end = ' ')

Comment and share

백준 1715 : 문제링크

  • 문제유형 :

    • 탐색
    • 그리디
  • 설명

    • 가장 작은 숫자의 카드 묶음을 먼저 합쳤을 때 최소값
    • heap을 사용해서 작은 값 두개를 pop한다
    • 추출한 두 개의 값을 합해서 결과에 더한다
    • 합한 값을 다시 heap에 push한다
    • heap에 값이 한 개 남았을 때 종료한다
  • 풀이

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    import heapq

    n = int(input())
    heap = []

    for i in range(n):
    data = int(input())
    heaq.heappush(heapq, data)

    result = 0

    while len(heap) != 1:
    one = heapq.heappop(heap)
    two = heapq.heappop(heap)
    sum_value = one + two
    result += sum_value
    heapq.heappush(heap, sum_value)

    print(result)

Comment and share

VIEW

Explain

  • 가상 테이블로 일부분만 데이터로 보고자 할때 사용합니다.

  • 실제 데이터를 저장하고 있지는 않습니다.

  • 한마디로 특정 컬럼의 데이터를 보여주는 역할만 합니다.

  • 뷰를 사용함으로 쿼리를 더 단순하게 만들수 있습니다.

  • 한번 생성된 뷰는 수정이 불가능 하며 인덱스설정이 불가능 합니다.

Syntax

1
CREATE VIEW <뷰이름> AS (QUERY)

Example

  • 국가코드와 국가이름이 있는 뷰

    1
    2
    3
    CREATE VIEW code_name AS
    SELECT code, name
    FROM country
  • city 테이블에 국가 이름 추가

    1
    2
    3
    4
    SELECT *
    FROM city
    JOIN code_name
    ON city.countrycode = code_name.code

INDEX

Explain

  • 정의 : 테이블에서 데이터를 검색할때 빠르게 찾을수 있도록 해주는 기능입니다.

  • 장점 : 검색속도가 빨라짐

  • 단점 : 저장공간을 10% 정도 더 많이 차지 INSERT, DELETE, UPDATE 할때 속도가 느려짐

  • 사용법 : SELECT시 WHERE 절에 들어가는 컬럼을 Index로 설정하면 좋다.

  • 내부 작동 원리 (B-Tree)

    • 루트노드와 리프노드의 계층적 구조로 루트노드를 이용하여 리프노드에서의 데이터를 빠르게 찾을수 있는 자료구조 알고리즘.

Syntax

1
CREATE INDEX <인덱스 이름> AS <테이블 이름(필드 이름)>

Example

  • employees 데이터 베이스에서 실행

  • 실행계획을 확인하여 인덱스를 사용하는지 확인

    1
    2
    3
    4
    5
    6
    7
    8
    9
    EXPLAIN
    SELECT *
    FROM salaries
    WHERE from_date < "1986-01-01"

    EXPLAIN
    SELECT *
    FROM salaries
    WHERE to_date < "1986-01-01"
  • 인덱스 확인

    1
    SHOW INDEX FROM salaries;
  • 인덱스 생성

    1
    2
    3
    4
    5
    CREATE INDEX fdate
    ON salaries (from_date)

    CREATE INDEX tdate
    ON salaries (to_date)
  • 여러개의 컬럼을 가지는 인덱스 생성도 가능

    1
    2
    CREATE INDEX ftdate
    ON salaries (from_date, to_date)
  • 인덱스 삭제

    1
    2
    3
    4
    5
    6
    7
    8
    DROP INDEX fdate
    ON salaries

    DROP INDEX tdate
    ON salaries

    DROP INDEX ftdate
    ON salaries
  • 여러개의 컬럼을 조건으로 WHERE절에 사용하는 경우 인덱스 확인

  • 인덱스가 하나의 컬럼에 있을때 보다 둘다 있을때가 더 빠름

    1
    2
    3
    4
    EXPLAIN
    SELECT *
    FROM salaries
    WHERE from_date < "1986-01-01" AND to_date < "1986-01-01"

TRIGGER

정의 : 특정 테이블을 감시하고 있다가 설정된 조건의 쿼리가 실행되면 미리 지정한 쿼리가 자동으로 실행되도록하는 방법

Syntax

1
2
3
4
5
6
create trigger {trigger name}
{before | after} {insert | update |delete}
on <table name> for each row
begin
<excute query>
end

Example

  • database 생성

    1
    2
    3
    4
    5
    6
    7
    8
    9
    create table data1 (
    id varchar(20),
    msg varchar(50) not null
    );

    create table data_backup (
    id varchar(20),
    msg varchar(50) not null
    );
  • Trigger 생성

    1
    2
    3
    4
    5
    6
    7
    create trigger backup
    before delete on data1
    for each row
    begin
    insert into data_backup(id, msg)
    values(old.id, old.msg);
    end

Comment and share

Sub Query

sub query는 query 문 안에 있는 query를 의미합니다.

SELECT절, FROM절, WHERE 등에 사용이 가능합니다.

Example

  • 전체 나라수, 전체 도시수, 전체 언어수를 출력 ( SELECT 절에 사용 )

    SUB QUERY 사용시 JOIN TABLE 크기를 줄일 수 있다

    1
    2
    3
    4
    5
    SELECT
    (SELECT count(name) FROM city) AS total_city,
    (SELECT count(name) FROM country) AS total_country,
    (SELECT count(DISTINCT(Language)) FROM countrylanguage) AS total_language
    FROM DUAL
  • 800만 이상되는 도시의 국가코드, 이름, 도시인구수를 출력 ( FROM 절에 사용 )

    • SUB QUERY를 안 쓸때

      • city table row : 4079
      • coutnry table row : 239
      • JOIN TABLE : 4079 * 239
        1
        2
        3
        4
        5
        6
        7
        8
        SELECT *
        FROM
        city
        JOIN
        (SELECT code, name
        FROM country) AS country
        ON city.countrycode = country.code
        HAVING city.population > 8000000;
    • SUB QUERY를 쓸 때

      • city table row : 10
      • coutnry table row : 239
      • JOIN TABLE : 10 * 239
        1
        2
        3
        4
        5
        6
        7
        8
        9
        SELECT *
        FROM
        (SELECT countrycode, name, population
        FROM city
        WHERE population > 8000000) AS city
        JOIN
        (SELECT code, name
        FROM country) AS country
        ON city.countrycode = country.code;

  • 800만 이상 도시의 국가코드, 국가이름, 대통령이름을 출력( WHERE 절에 사용 )

    1
    2
    3
    4
    5
    SELECT code, name, HeadOfState
    FROM country
    WHERE code IN (
    SELECT DISTINCT(countrycode) FROM city WHERE population > 8000000
    )

Comment and share

백준 1927 : 문제링크

  • 문제유형 :

    • 탐색

  • 설명

    • 파이썬에서 heapq 라이브러리를 이용하면 힙 구현 가능하다.
  • 풀이

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    import heapq

    n = int(input())
    heap = []
    result = []

    for _ in range(n):
    data = int(input())
    if data = 0:
    if heap:
    result.append(heapq.heappop(heap))
    else:
    result.append(0)
    else:
    heapq.heappush(heap, data)

    for data in result:
    print(data)

Comment and share

백준 1939 : 문제링크

  • 문제유형 :

    • 탐색

    • 이진탐색

    • BFS

  • 설명

    • 다리의 개수 M의 최대 100,000, 중량 제한 C는 최대 1,000,000,000

    • 이진탐색을 이용하면 시간 복잡도는 O(M * logC)

    • DFS로 도착지에 옮길 수 있는 지 확인

    • 한 번에 옮길 수 있는 물품의 최대값을 이진탐색으로 찾아낸다.

  • 풀이

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    from collections import deque

    n, m = map(int, input().split())
    adj = [[] for _ in range(n + 1)]

    def bfs(c):
    queue = deque([start_node])
    visited = [False] * (n + 1)
    visited[start_node] = True
    while queue:
    x = queue.popleft()
    for y, weight in adj[x]:
    if not visited[y] and weight >= c:
    visited[y] = True
    queue.append(y)
    return visited[end_node]

    start = 1000000000
    end = 1

    for _ in range(m):
    x, y, weight = map(int, input().split())
    adj[x].append((y, weight))
    adj[y].append((x, weight))
    start = min(start, weight)
    end = max(end, weight)

    start_node, end_node = map(int, input().split())

    result = start
    while(start <= end):
    mid = (start + end) // 2
    if bfs(mid):
    result = mid
    start = mid + 1
    else:
    end = mid - 1

    print(result)

Comment and share

백준 2110 : 문제링크

  • 문제유형 :

    • 탐색

    • 이진탐색

  • 설명

    • 집의 개수 N의 최대는 200,000 이고 집의 좌표 X의 최대 1,000,000,000이다

    • 이진탐색을 이용하면 시간 복잡도는 O(N * logX)

    • 집 간 거리 Gap에 대해서 이진탐색을 한다.

  • 풀이

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    n, c = list(map(int, input().split(' ')))`

    array = []
    for _ in range(n):
    array.append(int(input()))
    array = sorted(array)

    start = array[1] - array[0]
    end = array[-1] - array[0]
    result = 0

    while(start <= end): // 시간 복잡도 : O(logX)
    mid = (start + end) // 2
    value = array[0]
    count = 1
    for i in range(1, len(array)): // 시간 복잡도 : O(N)
    if array[i] >= value + mid:
    value = array[i]
    count += 1
    if count >= c:
    start = mid + 1
    result = mid
    else:
    end = mid - 1

    print(result)

Comment and share

Yechan Kim

author.bio


author.job