【Java语法】线程
你好,我是鲤鱼王。
在本文中,我们将总结“线程”的相关内容。
1. 进程和线程
进程:正在执行的程序。
线程:在进程内执行的流程单元。
多线程:进程内存在多个线程的情况。
2. 使用Java语言的线程方法
1)不使用多线程。
package thread.ex01;
public class ThreadMain01 {
public static void main(String[] args) {
Calculator calculator = new Calculator(0L, 1000000000L);
execute(calculator);
}
public static void execute(Calculator calculator) {
long startTime = System.currentTimeMillis();
System.out.println("StartTime : "+ startTime);
calculator.calc();
long result = (long) calculator.getSum();
long endTime = System.currentTimeMillis();
System.out.println("EndTime : "+ endTime);
System.out.printf("Operating Time : [%.3f] Sec, Result : [%d] \n", (endTime - startTime) / 1000.0, result);
}
}
class Calculator {
private long start;
private long end;
private long sum;
public Calculator(long start, long end) {
setInit(start, end);
}
public void calc() {
long sum = 0;
for (long i = start; i <= end; i++) {
sum += i;
}
this.sum = sum;
}
public long getSum() {
return this.sum;
}
public void setInit(long start, long end) {
this.start = start;
this.end = end;
this.sum = 0;
}
}
// 実行結果
//StartTime : 1643158486533
//EndTime : 1643158487070
//Operating Time : [0.537] Sec, Result : [500000000500000000]
- 指定した始まりの数から最後の数まで重ねて合算するコードです。
使用Thread类。
package thread.ex01;
import java.util.ArrayList;
public class ThreadMain02 {
public static void main(String[] args) {
// 非同期問題をjoinメソッドを使い飛ばす
System.out.println("########## Solved concurrency issue. ##########");
execute(5, 1000000000L, true);
System.out.printf("\n\n");
// 非同期問題のため、合算値が違う
System.out.println("########## Not Solved concurrency issue. ##########");
execute(5, 1000000000L);
}
public static void execute(int worker, long input) {
execute(worker, input, false);
}
public static void execute(int worker, long input, boolean isJoin) {
ArrayList<Calculator2> list = new ArrayList<Calculator2>();
long startTime = System.currentTimeMillis();
System.out.println("StartTime : "+ startTime);
long start = 0;
long end = input / worker;
for (int i=1; i <= worker; i++) {
if (input < end) end = input;
Calculator2 calc = new Calculator2(start , end, i);
calc.start();
list.add(calc);
if (end == input) break;
start = end + 1;
end = end * 2;
}
long result = 0;
for (int i=0; i < list.size(); i++) {
Calculator2 calc = list.get(i);
try {
if (isJoin) {
calc.join();
}
result += calc.getSum();
} catch (Exception e) {}
}
long endTime = System.currentTimeMillis();
System.out.println("EndTime : "+ endTime);
System.out.printf("Operating Time : [%.3f] Sec, Result : [%d] \n", (endTime - startTime) / 1000.0, result);
list.clear();
list = null;
}
}
class Calculator2 extends Thread {
private long start;
private long end;
private long sum;
private int num;
public Calculator2(long start, long end, int num) {
setInit(start, end, num);
}
public void calc() {
System.out.printf("Thread [%d] Before, Num [%d], End Num [%d], sum() : [%d] \n", num, start, end, start);
long sum = 0;
for (long i = start; i <= end; i++) {
sum += i;
}
System.out.printf("Thread [%d] After, Num [%d], End Num [%d], sum() : [%d] \n", num, start, end, sum);
this.sum = sum;
}
public long getSum() {
return this.sum;
}
public void setInit(long start, long end, int num) {
this.start = start;
this.end = end;
this.sum = 0;
this.num = num;
}
@Override
public void run() {
calc();
}
}
// 実行結果
//########## Solved concurrency issue. ##########
//StartTime : 1643159110620
//Thread [4] Before, Num [800000001], End Num [1000000000], sum() : [800000001]
//Thread [1] Before, Num [0], End Num [200000000], sum() : [0]
//Thread [3] Before, Num [400000001], End Num [800000000], sum() : [400000001]
//Thread [2] Before, Num [200000001], End Num [400000000], sum() : [200000001]
//Thread [1] After, Num [0], End Num [200000000], sum() : [20000000100000000]
//Thread [4] After, Num [800000001], End Num [1000000000], sum() : [180000000100000000]
//Thread [2] After, Num [200000001], End Num [400000000], sum() : [60000000100000000]
//Thread [3] After, Num [400000001], End Num [800000000], sum() : [240000000200000000]
//EndTime : 1643159110874
//Operating Time : [0.254] Sec, Result : [500000000500000000]
//
//
//########## Not Solved concurrency issue. ##########
//StartTime : 1643159110876
//EndTime : 1643159110876
//Operating Time : [0.000] Sec, Result : [0]
//Thread [4] Before, Num [800000001], End Num [1000000000], sum() : [800000001]
//Thread [3] Before, Num [400000001], End Num [800000000], sum() : [400000001]
//Thread [2] Before, Num [200000001], End Num [400000000], sum() : [200000001]
//Thread [1] Before, Num [0], End Num [200000000], sum() : [0]
//Thread [1] After, Num [0], End Num [200000000], sum() : [20000000100000000]
//Thread [4] After, Num [800000001], End Num [1000000000], sum() : [180000000100000000]
//Thread [2] After, Num [200000001], End Num [400000000], sum() : [60000000100000000]
//Thread [3] After, Num [400000001], End Num [800000000], sum() : [240000000200000000]
-
- 単一スレッドを使ったコードをマルチスレッドを使い、実装したものです。
- 実行結果をみると単一スレッドより、処理速度が速いのが分かります。
(1) 多线程的优点
长处:
– 使用多线程并行处理可能比单线程处理更快。(根据情况而定)
– 即使其中一个线程停止工作,其他线程仍可正常运行,从而保证程序的功能性。
短所:
– 程序性能可能会下降。
(例1) 为了解决临界区问题而进行不必要的同步。
(例2) 如果创建过多的线程,会消耗资源,如上下文切换和线程实例的生成。
– 可能会出现异步问题。
(例) 将多个线程的工作结果合并为一个结果。
-> 使用Thread类的join方法来解决。
– 可能会出现临界区问题。
(例) 多个线程同时访问同一个变量。
-> 使用synchronized关键字进行同步以解决。
※ 非同步問題的臨海領域問題解決範例程式碼
package thread.ex01;
import java.util.ArrayList;
public class ThreadMain04 {
public static int sharedNum = 0;
public static void main(String[] args) {
SyncData sd = new SyncData(0);
ArrayList<SyncTest> list = new ArrayList<SyncTest>();
for (int i=1; i<=3; i++) {
SyncTest sync = new SyncTest(i, sd);
sync.start();
list.add(sync);
}
for (SyncTest sync : list) {
try {
sync.join();
} catch (InterruptedException e) { e.printStackTrace(); }
}
System.out.println("Test1-Result : "+sd.balance);
System.out.printf("\n\n");
ArrayList<SyncTest2> list2 = new ArrayList<SyncTest2>();
for (int i=1; i<=3; i++) {
SyncTest2 sync2 = new SyncTest2(i);
sync2.start();
list2.add(sync2);
}
for (SyncTest2 sync2 : list2) {
try {
sync2.join();
} catch (InterruptedException e) { e.printStackTrace(); }
}
System.out.println("Test2-Result : "+sharedNum);
}
}
class SyncData {
Integer balance;
public SyncData(int balance) {
this.balance = balance;
}
}
class SyncTest extends Thread{
SyncData syncData;
int number;
public SyncTest(int number, SyncData syncData) {
this.number = number;
this.syncData = syncData;
}
@Override
public void run() {
sum();
}
public void sum() {
System.out.println("sum() Thread ["+number+"] Before : "+syncData.balance);
for (int i=1; i<=3; i++) {
synchronized (this) {
syncData.balance += i;
System.out.println("sum() Thread ["+number+"] After : "+syncData.balance);
}
}
}
}
class SyncTest2 extends Thread {
int number;
public SyncTest2(int number) {
this.number = number;
}
@Override
public void run() {
sum();
}
public synchronized void sum() {
System.out.println("sum() Thread ["+number+"] Before : "+ThreadMain04.sharedNum);
for (int i=1; i<=3; i++) {
ThreadMain04.sharedNum += i;
System.out.println("sum() Thread ["+number+"] After : "+ThreadMain04.sharedNum);
}
}
}
3) 使用Runnable接口
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
public class ThreadMain03 {
public static void main(String[] args) throws InterruptedException {
PrintTime pt = new PrintTime();
Thread t = new Thread(pt);
t.start();
for (int i=0; i<20; i++) {
System.out.println("test"+i);
Thread.sleep(700);
}
}
}
class PrintTime implements Runnable {
static SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss.SSS");
@Override
public void run() {
for (int i=0; i<10; i++) {
System.out.println(sdf.format(new Date()));
try {
Thread.sleep(300);
} catch (InterruptedException e) { e.printStackTrace(); }
}
}
}
如果在Thread类中使用线程,存在无法使用继承的缺点。
如果不想使用Thread类,而是想继承其他类,可以使用Runnable接口。
Thread类是继承了Runnable接口的类。
4) 使用Callable接口
package thread.ex01;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.FutureTask;
public class ThreadMain05 {
public static void main(String[] args) throws InterruptedException, ExecutionException {
GetTime gt = new GetTime();
System.out.println("########## Callable Test (1) ##########");
FutureTask<ArrayList<String>> futureTask = new FutureTask<ArrayList<String>>(gt);
new Thread(futureTask).start();
for (int i=0; i<5; i++) {
System.out.println("test1 wating Count : "+i);
Thread.sleep(500);
}
System.out.println(futureTask.get());
System.out.println("########## Callable Test (2) ##########");
ExecutorService executor = Executors.newSingleThreadExecutor();
Future<ArrayList<String>> future = executor.submit(gt);
for (int i=0; i<5; i++) {
System.out.println("test2 wating Count : "+i);
Thread.sleep(300);
}
System.out.println(future.get());
}
}
class GetTime implements Callable<ArrayList<String>> {
static SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss.SSS");
@Override
public ArrayList<String> call() throws Exception {
ArrayList<String> list = new ArrayList<String>();
for (int i=0; i<5; i++) {
String time = sdf.format(new Date());
System.out.println(time);
list.add(time);
try {
Thread.sleep(300);
} catch (InterruptedException e) { e.printStackTrace(); }
}
return list;
}
}
当实现Runnable接口时,run()方法无法返回值并且无法添加异常处理。如果需要实现对线程的每个操作的返回值和异常处理,可以使用Callable接口。
使用线程池
package thread.ex01;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
public class ThreadMain06 {
public static void main(String[] args) throws InterruptedException{
// 決めた数分スレッドを生成
int workers = 4;
ExecutorService executorService = Executors.newFixedThreadPool(workers);
for (int i=1; i<=10; i++) {
ThreadPoolTest threadPoolTest = new ThreadPoolTest(i);
executorService.submit(threadPoolTest);
}
if (executorService.awaitTermination(5, TimeUnit.SECONDS)) {
System.out.println("All Tasks over");
}
executorService.shutdown();
}
}
class ThreadPoolTest implements Runnable {
int number;
@Override
public void run() {
counting();
}
public ThreadPoolTest(int number) {
this.number = number;
}
public void counting() {
for (int i=0; i<5; i++) {
System.out.printf("Thread [%d], Count [%d] \n", number, i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) { e.printStackTrace(); }
}
}
}
线程池是预先生成一定数量的线程并保持可用,为解决多线程的缺点之一,即当生成过多线程时会导致程序性能下降,可以使用线程池。
以下是一个例示代码:
https://github.com/leeyoungseung/algorithmBasic/tree/master/algorithm/src/thread/ex01
※ 您可以在以下网址上确认韩语帖子。
https://koiking.tistory.com/93