전체 글 (162)
2024-10-03 00:31:58

Servlet

- JSP 표준이 나오기 전에 만들어진 표준

- Java에서 웹 어플리케이션을 개발 할 수 있도록 하기 위해 만들어짐

- Java 클래스를 뭽에서 호출 및 실행할 수 있도록 한 표준

- Javax.Servlet.HttpSerVlet을 상속받아 작성

 

SerVlet 호출 과정

GET

  • 주소에 매개변수가 붙어서 호출하는 방식 (?으로 구분)
  • <a> 태그를 이용해서 페이지를 호출하는 경우
  • 자바스크립트를 이용해서 호출하는 경우
  • <form> 태그에서 명시적으로 GET방식으로 호출하는 경우
  • 데이터는 최대 255자 이내이며, 보안성이 취약
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
 
<br>
<h1>get 방식으로 부르는 페이지 입니다.</h1>
<br>
 
<form action = "HelloWorld" method = "get">
    <input type = "submit" value ="확인"> 
</form>
 
 
 
</body>
</html>
cs

POST

  • = <form> 태그에서 명시적으로 POST방식으로 호출하는 경우
  • = 주소에 매개변수가 붙어있지 않아 보안성이 우수
  • = 데이터의 크기에 제한이 없음.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
 
<h1>post 방식으로 부르는 페이지입니다.</h1>
<br>
 
<form action="HelloWorld" method="post">
    <input type="submit" value="확인">
</form>
 
</body>
</html>
cs

 


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
package week2;
 
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
 
@WebServlet("/HelloWorld"// action이 여기에 있는 내용을 찾음
public class HelloWorld extends HttpServlet {
    private static final long serialVersionUID = 1L;
       
    public HelloWorld() {
        super();
    }
 
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // get 방식으로 호출한 경우 실행하는 메소드
        response.setContentType("text/html; charset=utf-8");
        PrintWriter out = response.getWriter();
        out.println("<html><body>");
        out.println("<h1>Hello World Servlet doGet() 페이지입니다.</h1>");
        out.println("</body></html>");
        out.close();
    }
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
       // post 방식으로 호출한 경우 실행하는 메소드
        response.setContentType("text/html; charset=utf-8");
        PrintWriter out = response.getWriter();
        out.println("<html><body>");
        out.println("<h1>Hello World Servlet doPost() 페이지입니다.</h1>");
        out.println("</body></html>");
        out.close();
    }
}
cs

 

결과

 

 

response.setContentType("text/html;Charset=utf-8"); 

request.setCharacterEncoding("utf-8");

// Post방식으로 매개변수를 줄 때 반드시 써줘야 한다.

 

 

 

'JSP공부' 카테고리의 다른 글

표현 언어 Expression Language  (0) 2024.10.04
JavaBeans 자바빈  (0) 2024.10.04
JSP 액션 태그  (1) 2024.10.03
JSP  (1) 2024.10.03
JSP 개요  (1) 2024.10.02
2024-10-02 23:31:23

클라이언트와 서버 구상도

구성요소 역할
웹 서버 웹 브라우저의 요청을 받아서 결과를 반환.
프로그램 처리가 필요하다면 웹 어플리케이션 서버를 이용하거나 프로그램을 호출하여 처리.
HTML, CSS, JavaScript를 웹 브라우저에게 반환.
어플리케이션 서버 필요한 기능을 수행하고, 웹 서버에게 결과를 전달.
데이터 베이스 데이터를 저장함.

 

웹 서비스 요청 / 응답

HTTP 프로토콜 이용

- TCP/IP 기반으로 웹에서 사용하는 프로토콜로, 요청(request)과 응답(response)데이터를 전송하는 방식

 

HTTP 요청 구성 요소

- HTTP 메소드(실행할 액션)

- 접근하고자 하는 URL

- form  파라미터

 

HTTP 응답 구성 요소

- 상태 코드

- 컨텐츠 타입 및 내용

 

URL

클라이언트가 웹 서버에 존재하는 자원(정보, 파일)을 검색하고 해석하는데 필요한 네트워크 서비스의 표현식

[프로토콜]://[호스트][:포트]/[경로][파일명][.확장자][쿼리문자열]

ex) http://naver.com:80/index.html

포트 번호는 프로토콜의 기본 포트인 경우 생략이 가능하다.

 

어플리케이션 프로그램 실행     CGI VS WAS

 

CGI 방식 

- 웹 서버가 어플리케이션을 직접 호출하는 구조.

- 하나의 요청에 하나의 프로세스를 만들고 처리하는 구조.

 

WAS 방식

- 웹 서버가 직접 어플리케이션 프로그램을 처리하는  게 아닌, 웹 어플리케이션 서버(was)에 처리를 넘기고, was가 프로그램을 처리.

- 여러명의 사용자가 같은 페이지를 요청하면, 하나의 어플리케이션에 하나의 프로세스만 생성하여, 사용자의 요청을 스레드로 처리함.

 

JSP

기능 

- DBMS와 같은 Back-End Server와 연동하여 데이터를 가공한 결과를 웹 상의 최종 사용자에게 전달 가능

- 여러 조건에 따라 표출 할 수 있도록 동적 처리 기능 제공

 

특징

- 객체지향적

- 플랫폼 독립적

- 네트워크 지향적

- 강력한 보안성

- 친숙한 코드

-멀티 스레드 기능

'JSP공부' 카테고리의 다른 글

표현 언어 Expression Language  (0) 2024.10.04
JavaBeans 자바빈  (0) 2024.10.04
JSP 액션 태그  (1) 2024.10.03
JSP  (1) 2024.10.03
Servlet  (0) 2024.10.03
2024-09-13 18:40:05

 

이거도 나중에 써야지

 

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
using System;
using System.Collections.Generic;
using System.Text;
using System.Linq;
using System.Threading.Channels;
 
namespace real연습
{
    internal class Program
    {
        static void Main(string[] args)
        {
            int sugar = int.Parse(Console.ReadLine());
 
            int[] k = { 35 };
 
            int[] dp = new int[sugar + 1];
            for (int i = 1; i <= sugar; i++)
            {
                dp[i] = int.MaxValue;
            }
 
 
            for (int i = 0; i < 2; i++)
            {
                for (int j = k[i]; j <= sugar; j++)
                {
                    if (dp[j - k[i]] != int.MaxValue)
                        dp[j] = Math.Min(dp[j], 1 + dp[j - k[i]]);
                }
            }
            Console.WriteLine(dp[sugar] == int.MaxValue ? -1 : dp[sugar]);
        }
    }
}
cs

 

bfs로도 풀린다.

그래프 탐색은 신이고 무적이다

 

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
using System;
using System.Collections.Generic;
using System.Text;
using System.Linq;
using System.Threading.Channels;
 
namespace real연습
{
    internal class Program
    {
        static void Main(string[] args)
        {
            int sugar = int.Parse(Console.ReadLine());
 
            int[] k = { 35 };
 
            int[] graph = new int[sugar + 1];
 
            Queue<int> q = new Queue<int>();
            HashSet<int> visited = new HashSet<int>();
            q.Enqueue(0);
            visited.Add(0);
 
            while (q.Count != 0)
            {
                int current = q.Dequeue();
                for (int i = 0; i < 2; i++)
                {
                    int delta = current + k[i];
                    if (visited.Contains(delta) == false)
                    {
                        if (delta >= 0 && delta <= sugar)
                        {
                            graph[delta] = 1 + graph[current];
                            q.Enqueue(delta);
                            visited.Add(delta);
                        }
                    }
                }
                if (graph[sugar] != 0)
                {
                    break;
                }
            }
            Console.WriteLine(graph[sugar] == 0 ? -1 : graph[sugar]);
 
        }
    }
}
cs

'코끼리' 카테고리의 다른 글

[백준] 2293 동전 1 [DP]  (0) 2024.09.12
2024-09-12 13:25:40

 

이번 단계가 저번 단계의 결과값을 포함하고 있음을 이용한 문제이다

수업듣고 써야지

 

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
using System;
using System.Collections.Generic;
using System.Text;
using System.Linq;
using System.Threading.Channels;
 
namespace real연습
{
    internal class Program
    {
        static void Main(string[] args)
        {
            int[] arr = Array.ConvertAll(Console.ReadLine().Trim().Split(), int.Parse);
            int[] dp = new int[arr[1+ 1];
            dp[0= 1;
 
            for (int i = 0; i < arr[0]; i++)
            {
                int coin = int.Parse(Console.ReadLine());
                for (int j = coin; j <= arr[1]; j++)
                {
                    dp[j] += dp[j - coin];
                }
            }
            Console.WriteLine(dp[arr[1]]);
        }
    }
}
cs

'코끼리' 카테고리의 다른 글

[백준] 2839 : 설탕 배달 [DP]  (0) 2024.09.13
2024-08-28 01:44:57

 

문제의 조건 자체는 엄청 쉬워보였다.

일반적인 bfs문제에 그저 블록 파괴 기능만 추가하면 의외로 쉽게 풀릴 줄 알았다... 그 때는...

접근 1)  더이상 순회 불가능 시 벽 파괴해보기

우선 그래프를 순회하며 만난 벽들을 모두 리스트에 담아 기록해둔다.

이후 큐의 모든 원소를 소모하였을 때, 도착점에 도달하지 못했다면, 리스트에서 벽을 하나씩 꺼내어 해당 벽을 부수고, 다시 순회를 해 주었다.

문제 1)  너무 비효율적인 순회

접근법 자체는 빠르게 떠올렸지만, 그리 좋은 접근법은 아니었다. 입력대로 초기화 될때의 상태를 간직하는 원본 그래프를 준비하고, 순회 시 마다 복사본 그래프를 만들어 순회를 해주었는데, 이건 메모리 측면에서나, 실행시간 측면에서나 너무 비효율적이었다.

문제 2)  한번에 순회를 완료했을때 문제.

이게 가장 큰 문제였다. 예제들은 모두 한번에 순회가 불가능했기  때문에 벽을 한번 부수고 순회하는 것이 최단거리였다.

하지만  https://www.acmicpc.net/board/view/147795  이 예제대로 막힌 벽 없이 한번에 순회가 완료되면 바로 메소드가 종료되어 올바른 답을 얻을 수 없었다.

때문에 다른 접근법을 찾아보기로 했다.

보완 1)  사용하는 그래프를 2개로 제한해보기

기존에 순회할 때 마다 무한정 늘어나는 그래프의 수를 제한하고자, 벽을 부수지 않는 경우, 벽을 부수는 경우 둘로 나누어 두 그래프만 사용해보기로 했다.

두 그래프를 유기적으로 묶어, 일반 그래프가 벽을 만나면, 벽을 부수는 그래프의 값을 증가시키고, 다시 벽을 부수는 그래프가 일반 그래프에 영향을 주어 순회를 하는 방식으로 해보았다.

또한 계속해서 벽을 뚫는것을 방지하기 위해, 벽이 벽을 만나면 순회하지않고, 건너뛰기로 했다.

해당 알고리즘을 그힘으로 표현

문제 3)  벽을 한번 이상 부셔버림

알고리즘 특성상 연속된 벽은 통과 못하지만, 띄엄띄엄 존재하는 벽이라면 무한정으로 뚫고 지나가버리는 치명적인 문제점이 있었다. 따라서 벽을 몇번 뚫었는지 저장해두어야 한다는걸 깨닳았다.

문제 4) 벽을 부순건지 아닌지의 모호함

첫번째 칸이 0으로 되어있기 때문에, 첫번째 칸이 벽을 만나 벽을 부수게 되었을 때, 값이 1로 되어 벽을 부순 것인지, 벽을 부수지 않은 것인지 판별을 할 수 없는 문제가 있었다.

보완 2)  그래프를 하나로, 방문처리를 set이 아닌 배열로

그래프의 값을 변조하는 방식이 그다지 효과적이지 않다는 것을 깨달았다. 대신에, 기존에 방문처리를 해주던 HashSet 대신 bool 배열을 이용하여 해당 칸을 방문처리를 대신해 주었다. 이럼으로써 4번 문제가 깔끔히 해결되었다.

보완 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
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
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
using System;
using System.Collections.Generic;
using System.Text;
 
namespace real연습
{
    internal class Program
    {
        static void Main(string[] args)
        {
            StreamReader reader = new StreamReader(Console.OpenStandardInput());
            int[] arr = Array.ConvertAll(reader.ReadLine().Trim().Split(), int.Parse);
            Graph g = new Graph(arr[0], arr[1]);
            for (int i = 0; i < arr[0]; i++)
            {
                string str = reader.ReadLine();
                foreach (var a in str)
                {
                    g.graph[i].Add(a - '0');
                }
            }
            Console.WriteLine(g.bfs());
        }
    }
    class Graph
    {
        int width;
        int height;
        public List<List<int>> graph;
        public Graph(int h, int w)
        {
            this.height = h;
            this.width = w;
            graph = new List<List<int>>();
            for (int i = 0; i < height; i++)
            {
                graph.Add(new List<int>());
            }
        }
        public int bfs()
        {
            int[] deltaY = { 10-10 };
            int[] deltaX = { 010-1 };
 
            bool[,,] visited = new bool[height, width,2];
            Queue<Tuple<intintintint>> qu = new Queue<Tuple<intintintint>>();
            qu.Enqueue(new Tuple<intintintint>(0001));
            // y, x, 벽 부순 횟수, 거리
            visited[000= true;
            while(qu.Count!= 0)
            {
                Tuple<intintintint> current = qu.Dequeue();
                if (current.Item1 == height - 1 && current.Item2 == width - 1)
                {
                    return current.Item4;
                }
                for (int i = 0; i < deltaX.Length; i++)
                {
                    int movedY = current.Item1 + deltaY[i];
                    int movedX = current.Item2 + deltaX[i];
                    if (movedY < 0 || movedY >= height || movedX < 0 || movedX >= width)
                    {
                        continue;
                    }
                    if (visited[movedY, movedX, current.Item3] == true)
                    {
                        continue;
                    }
                    if (graph[movedY][movedX] == 0)
                    {
                        qu.Enqueue(new Tuple<intintintint>(movedY, movedX, current.Item3, current.Item4 + 1));
                        visited[movedY, movedX, current.Item3] = true;
                    }
                    else if (current.Item3 == 0 && graph[movedY][movedX] == 1)
                    {
                        qu.Enqueue(new Tuple<intintintint>(movedY, movedX, current.Item3 + 1, current.Item4 + 1));
                        visited[movedY, movedX, current.Item3 + 1= true;
 
                    }
                }
            }
 
            return -1;
        }
 
    }
}
 
cs

 

2024-06-27 00:45:05

오예

2024-06-27 00:41:58

 

dfs던 bfs던 아무 그래프 순회 알고리즘을 사용하여 1번 정점으로부터 방문 가능한 모든 정점의 개수를 요구하는 문제이다.

주의해야 할 점은 '1번 컴퓨터' 로 부터 감염될 수 있는 컴퓨터의 수를 구하는 문제이기 때문에, 1번 정점은 빼주고 개수를 구해주어야 한다.

 

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
using System.Collections.Generic;
 
namespace real연습
{
    internal class Program
    {
        static void Main(string[] args)
        {
            int edges = int.Parse(Console.ReadLine());
            int linkes = int.Parse(Console.ReadLine());
            Graph graph = new Graph(edges);
 
            for (int i = 0; i < linkes; i++)
            {
                int[] input = Array.ConvertAll(Console.ReadLine().Split(), int.Parse);
                graph.graph[input[0]].Add(input[1]);
                graph.graph[input[1]].Add(input[0]);
            }
            int result = 0;
            result = dfs_search(graph);
            Console.WriteLine(result);
        }
        static int dfs_search(Graph graph)
        {
            int result = 0;
 
            Stack<int> stack = new Stack<int>();
            stack.Push(1);
            List<int> visited = new List<int>();
            
            while (stack.Count != 0)
            {
                int current = stack.Pop();
 
                if (visited.Contains(current) == false)
                {
                    if (current != 1)
                    {
                        result++;
                    }
                    visited.Add(current);
                }
                for (int i = graph.graph[current].Count - 1; i >= 0; i--)
                {
                    if (visited.Contains(graph.graph[current][i]) == false)
                    {
                        stack.Push(graph.graph[current][i]);
                    }
                }
            }
 
            return result;
        }
        private class Graph
        {
            int _size;
            public List<int>[] graph;
            public Graph(int size)
            {
                this._size = size;
                this.graph = new List<int>[_size + 1];
                for (int i = 1; i <= _size; i++)
                {
                    graph[i] = new List<int>();
                }
            }
        }
    }
}
 
cs

 

2024-06-27 00:07:45

이런식으로 서로 간선으로 연결되어있는 정점들의 집합을 하나의 연결 요소라고 한다.

이 주어진 그래프를 토대로 몇개의 연결 요소가 나오는지 구하는 문제이다.

 

문제에서 주어지는 정점의 개수는 1000개이다. 이를 일반적인 인접행렬로 표현하려면 1000 * 1000의 배열이 필요하므로, 메모리도 아끼고 실행 시간도 줄일 겸 각 정점이 어느 정점과 연결되어있는지 표현하는 인접행렬로 구현하였다.

 

문제를 풀기 위해서 dfs를 사용하여 순회하지 않았던 연결 요소라면 true를 반환하여 결과값에 1을 더하고, 순회하였던 연결 요소라면 false를 반환하도록 해주었다.

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
72
73
74
75
76
77
78
79
80
namespace real연습
{
    internal class Program
    {
        static void Main(string[] args)
        {
            int[] input = Array.ConvertAll(Console.ReadLine().Split(), int.Parse);
            int edge = input[0];
            int linkes = input[1];
 
            Graph graph = new Graph(edge);
 
            for (int i = 0; i < linkes; i++)
            {
                int[] link = Array.ConvertAll(Console.ReadLine().Split(), int.Parse);
                graph.graph[link[0]].Add(link[1]);
                graph.graph[link[1]].Add(link[0]);
            }
            HashSet<int> visited = new HashSet<int>();
 
            int result = 0;
            for (int i = 1; i <= edge; i++) // 1번 정점부터 순회 시작
            {
                if (dfs_search(i, visited, graph) == true) // 순회한적 없다면
                {
                    result += 1; // 결과값 1 증가
                }
            }
            Console.WriteLine(result);
 
        }
        static bool dfs_search(int start, HashSet<int> visited, Graph graph)
        {
            bool result = false;
            Stack<int> stack = new Stack<int>();
            if (visited.Contains(start) == false) // 방문하지 않았던 정점일때만 실행
            {
               stack.Push(start); // stack에 순회를 시작하는 값을 미리 넣어둠
 
                while (stack.Count != 0)
                {
                    int current = stack.Pop(); // 스택의 맨 뒤에있는 값을 뽑아 사용
                    if (visited.Contains(current) == false) // 해당 정점을 방문하지 않았다면
                    {
                       visited.Add(current); // 방문처리
                    }
                    for (int vertex = graph.graph[current].Count - 1; vertex >= 0 ; vertex--)
                   { // 스택을 맨 위의 값부터 뽑아서 쓰기 때문에, 마지막 index부터 첫번쨰 index까지 역순회
                        if (visited.Contains(graph.graph[current][vertex]) == false)
                       {
                            stack.Push(graph.graph[current][vertex]);
                        }
                    }
 
                }
                result = true;
            }
 
 
            return result;
        }
        private class Graph=
        {
            int _size;
            public List<int>[] graph;
            public Graph(int size)
            {
                this._size = size;
                this.graph = new List<int>[size + 1];
                for (int i = 1; i < size + 1; i++)
                {
                    graph[i] = new List<int>();
                }
            }
        }
 
    }
    
}
 
cs

 

 

2024-06-01 16:57:32

프로세스 (process)

- 실행 중인 하나의 프로그램

- 사용자가 애플리케이션을 실행하면 운영체제(O/S)로부터 실행에 필요한 메모리를 할당받아 애플리케이션의 코드를 실행하는 것.

- 하나의 프로그램이 다중 프로세스를 만들기도 한다.

 

멀티 태스킹 (multi tasking)

- 두가지 이상의 작업을 동시에 처리하는 것.

 

멀티 프로세스   -   어플리케이션 단위의 멀티 태스킹

- 독립적으로 프로그램을 실행하고 여러가지 작업을 처리

- 운영체제에서 할당받은 자신의 메모리를 가지고 실행하여, 서로 독립적

- 하나의 프로세스에 오류가 발생해도 다른 프로세스에 영향을 미치지 않는다

 

멀티 스레드    -   한 개의 어플리케이션 내부에서의 멀티 태스킹

- 스레드(thread) : 한 가지 작업을 실행하기 위해 순차적으로 실행할 코드

- 한 개의 프로그램을 실행하고 내부적으로 여러 가지 작업 처리

- 하나의 프로세스 내에서 여러가지 작업을 처리

- 하나의 스레드에서 오류가 발생하면 다른 스레드도 같이 종료되기 때문에, 예외처리를 잘 해야함.

 

메인 (main) 스레드

모든 자바 프로그램은 메인 스레드가 main() 메소드를 실행하며 시작되며, main() 메소드의 첫 코드부터 아래로 순차적으로 실행된다.

마지막 코드가 실행되거나, return 문을 만나면 종료된다.

싱글 스레드는 메인 스레드가 종료하면 프로세스도 종료되며, 

멀티 스레드는 실행중인 스레드가 하나라도 있다면, 프로세스가 종료되지 않는다.

 

작업 스레드 생성 방법

 

- Thread 클래스로부터 직접 생성

Runnable 인터페이스를 매개값으로 갖는 Thread 생성자 호출

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class BeepTask implements Runnable {
    //인터페이스를 구현하는 구현 클래스
    
    @Override
    public void run() {
        // 스레드가 실행할 명령문을 기술
        for (int i = 0; i < 5; i++)
        {
            System.out.println("삐~");
            try{
            Thread.sleep(1000);
            }
            catch(InterruptedException e )
            {
                
            }
        }
        
    }
    
}
 
cs

 

<main>

Runnable beepTask = new DeepTask();

Thread thread = new Thread(BeepTask);

thread.start(); // 스레드 실행 메소드

 

- Thread 하위 클래스로부터 생성

Thread 클래스 상속 후 run 메소드를 재정의해 스레드가 실행할 코드 작성

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
 
public class BeepThread extends Thread {
    // Thread를 상속받아서 run() 메소드를 재정의해서 사용
    
    @Override
    public void run()
    {
        for (int i = 0;i < 5; i++)
        {
            System.out.println("삐~");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }
}
 
cs

<main>

Thread thread = new BeepThread();

thread.start();

<익명 함수 이용>

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
 
public class BeepTaskEx2 {
    public static void main(String[] args)throws InterruptedException {
        // 익명 객체 (인터페이스)를 바로 대입해서 스레드 생성
        Thread thread = new Thread(new Runnable() {
            @Override
            public void run()
            {
                // 스레드가 실행할 명령문을 기술
                for (int i = 0 ; i < 5; i ++) {
                    System.out.println("삐~");
                    try {
                    Thread.sleep(1000);
                    } catch(InterruptedException e)
                    { }
                }
            }
        });
        thread.start();
 
        new Thread() // 인스턴스 없이 사용하기
        {
            @Override
            public void run()
            {
                for (int i= 0; i < 3; i++) {
                    System.out.println("응애");
                    try {
                        Thread.sleep(1000);
                    }catch (InterruptedException e) {}
                }
            }
        }.run();
    }
}
 
cs

 

스레드의 이름

메인 스레드의 이름 : main

작업 스레드의 이름 (자동 설정) : Thread-n

스레드 이름 확인 : thread.getName();

스레드 이름 변경 : thread.setName("스레드 이름");

코드 실행하는 현재 스레드 객체의 참조 얻기

Thread thread = Thread.currentThread();

반환 내용 : Thread[호출스레드명, 우선운위, 스레드그룹명]

 

스레드 우선 순위

동시성 (Concurrentcy)

멀티 작업을 위해 하나의 코어에서 멀티 스레드가 번갈아 가며 실행하는 성질

병렬성 (parallelis)

멀티 작업을 위해 멀티 코어에서 개별 스레드를 동시에 실행하는 성질

 

스레드 스케줄링

스레드의 개수가 코어의 수보다 많을 경우, 스레드를 어떤 순서로 동시성으로 실행할 것인가 결정하기 위해 사용

스케줄링에 의해 스레드들은 번갈아 가며 run() 메소드를 조금씩 실행

 

우선순위 방식 (Priority) - 코드로 제어 가능

우선 순위가 높은 스레드가 실행 상태를 더 많이 가지도록 스케줄링

1 ~ 10까지 가질 수 있으며 기본은 5.

하지만 우선순위가 높다고 해서 항상 먼저 실행되는 것은 아님.

우선실행될 확률이 올라가는 것일 뿐...

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package schedule;
 
public class LoopThreadEx {
    public static void main(String[] args)
    {
        for (int i = 1; i <= 10; i++)
        {
            Thread thread = new LoopThread("thread_" + i);
            
            if (i != 10)
                thread.setPriority(Thread.MIN_PRIORITY);
            else
                thread.setPriority(Thread.MAX_PRIORITY);
            
            thread.start();
        }
    }
}
cs

 

순환 할당 (Round-Robin) - 코드로 제어할 수 없음

시간 할당량 (Time-Slice) 정해서 하나의 스레드를 정해진 시간만큼 실행

 

동기화 메소드와 동기화 블록

동유객체를 사용할 때, 멀티 스레드가 하나의 객체를 공유하면 문제가 생길 수 있다.

스레드가 사용 중인 객체를 다른 스레드가 변경하는 일을 방지하기 위해서 synchronized 키워드를 사용한다.

이 키워드를 사용하면 단 하나의 스레드만 메소드 혹은 블록을 실행할 수 있어, 다른 스레드는 메소드나 블록 실행이 끝날 때 까지 대기해야한다.

동기화 메소드

public synchronized void method() {

}

동기화 블록

synchronized(공유 객체) {

// 임계영역 : 단 하나의 스레드만 실행 가능

}

// 여러 스레드가 실행 가능한 영역

 

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
package control;
 
public class DataBox {
    private String data;
    
    public synchronized String getData()
    {
        if (this.data == null) {
            // 데이터 값이 존재하지 않으면 
            try {
                wait(); // 대기상태로 전환
            } catch(InterruptedException e) { }
        }
    
        String returnValue = data;
        System.out.println("ConSumerThread가 읽은 데이터 : " + returnValue);
        data = null// 데이터를 반환한 다음 데이터 초기화
        notify(); // 대기상태인 스레드 실행대기로 전환
        return returnValue;
    }
    public synchronized void setData(String data) {
        if (this.data != null) {
            // 데이터가 존재한다면
            try {
                wait(); // 대기상태로 전환
            } catch (InterruptedException e) {} 
        }
        this.data = data;
        System.out.println("ProducerThread가 생성한 데이터 : " + data);
        notify(); // 일시 정지 상태인 스레드를 실행대가로 전환
    }
    
    // 스레드의 실행 순서는 예측이 불가능하다.
    // 만약 처음에 ConsumerThread가 세번 연속 실행이 되었을 경우,
    // 데이터가 존재하지 않기 때문에, 첫번째 ConsumerThread는 wait()에 의해 대기상태가 된다.
    // 두번째,세번째 ConsumerThread는 synchronized 키워드에 의하여 getData을 실행할 수 없다.
    // 이후 ProducerThread에 의해 데이터가 생성되고, notify()에 의해 첫번째 소비자 스레드가 실행된다.
    // 이후 흐름 동일.
}
 
cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package control;
 
public class ProducerThread extends Thread {
    private DataBox dataBox;
    public ProducerThread(DataBox dataBox) {
        this.dataBox = dataBox;
    }
    
    @Override
    public void run()
    {
        for (int i = 1; i <= 3; i++)
        {
            String data = "Data" + i;
            dataBox.setData(data);
        }
    }
}
 
cs

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package control;
 
public class ConsumerThread extends Thread {
    private DataBox dataBox;
    public ConsumerThread(DataBox dataBox)
    {
        this.dataBox = dataBox;
    }
    @Override
    public void run()
    {
        for (int i = 1; i <= 3; i++)
        {
            String data = dataBox.getData();
        }
    }
}
 
cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package control;
 
public class ControlThread {
    public static void main(String[] args)
    {
        DataBox dataBox = new DataBox();
        
        ProducerThread prodThread = new ProducerThread(dataBox);
        ConsumerThread consThread = new ConsumerThread(dataBox);
        
        prodThread.start();
        consThread.start();
    }
}
 
cs

 

 

스레드 상태

 

구분 메소드 설명
일시 정지로 보냄 sleep(long millis) 주어진 시간(밀리세컨드 단위)동안 스레드를 일시 정지 상태도 만듦.
시간이 지나면 자동으로 실행상태로 전환
join() join() 메소드를 호출한 스레드는 일시 정지 상태.
join 메소드를 가진 스레드가 종료되면 실행 대기 상태로 전환
wait() 동기화 블록 내에서 스레드를 일시 정지 상태로 만듦
일시정지에서 벗어남 interrupt() 일시 정지 상태일 경우, exception을 발생시켜 ready() 혹은 종료 상태로 전환
notify(), notifyAll() wait() 메소드로 일시 정지 상태인 스레드를 실행 대기 상태로 만듦
실행 대기로 보냄 yield() 실행 상태에서 다른 스레드에게 실행을 양보하고 실행 대기 상태로 전환

 

sleep() : 얼마동안 멈출것인지 밀리 세컨드 단위로 지정.

일시 정지 상태에서 정지가 끝나기 전에 interrupt() 메소드가 호출되면 interruptException 발생. 따라서 예외처리가 필요함.

 

 

데몬 스레드 (daemon)

주 스레드의 작업을 돕는 보조적인 역할을 수행하는 스레드.

주 스레드가 종료되면 데몬 스레드는 강제적으로 자동 종료됨.

ex) 워드프로세서의 자동저장, 미디어플레이어의 동영상 및 음악 재생, 가비지 컬렉터 등...

 

스레드를 데몬 스레드로 만들기

주 스레드가 데몬이 될 스레드의 setDaemon(true) 호출

반드시 start() 메소드 호출 전에 setDaemon(true)를 호출해야 함. 그렇지 않으면 IllegalThreadStateException 발생.

isDaemon() 메소드의 리턴값 조사 - true면 데몬 스레드.

2024-06-01 03:58:37

인터페이스는 객체의 사용방법을 정의한 타입이다.

객체의 교환성을 높여주기 때문에 다형성을 구현하는 매우 중요한 역할을 한다.

 

인터페이스의 역할

1. 개발 코드가 객체에 종속되지 않게 함 -> 객체 교체할 수 있도록 하는 역할

2. 개발 코드 변경 없이 리턴값 또는 실행 내용이 다양해 질 수 있음

 

인터페이스 선언

[public] interface 인터페이스명 { ... }

 

인터페이스의 구성 멤버

1. 상수 필드     -    모든 필드는 public static final이 자동적으로 붙는다 (안붙이면 컴파일러가 붙임), 선언과 동시에 초기화 해줘야 함    

2. 추상 메소드      - public abstract가 자동으로 붙음

3. 디폴트 메소드        - 실행블록을 가지고 있는 메소드 (구현클래스에서 재정의하지 않아도 상관없음)

4. 정적 메소드

(인터페이스는 인스턴스 필드를 가질 수 없다!!!)

 

 

구현 클래스 선언

implement 키워드로 명시한다.

public class 구현클래스명 implements 인터페이스명 { ... }

 

익명 구현 객체

명시적인 구현 클래스 작성은 생략하고 바로 구현 객체를 얻는 방법

이름없는 구현 클래스 선언과 동시에 객체 생성

ex) new Thread(new Runnable (public void run() {...} )).start();