【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

广告
将在 10 秒后关闭
bannerAds