English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية
O Java oferece suporte integrado para programação multithreading. Uma thread é uma única sequência de controle de fluxo dentro de um processo, um processo pode concurrir várias threads, cada uma executando diferentes tarefas em paralelo.
A multithreading é uma forma especial de multitarefa, mas utiliza menor consumo de recursos.
Aqui é definido outro termo relacionado às threads - Um processo inclui o espaço de memória alocado pelo sistema operacional, contendo uma ou mais threads. Uma thread não pode existir isoladamente, ela deve fazer parte de um processo. Um processo continua rodando até que todas as threads não-funcionais terminem de executar.
Multithreading can enable programmers to write efficient programs to fully utilize the CPU.
A thread is a dynamic execution process, and it also has a process from birth to death.
The following figure shows the complete life cycle of a thread.
New state:
using new keyword and Thread After a thread object is created by a class or its subclass, the thread object is in the new state. It remains in this state until the program start() this thread.
Ready state:
After the thread object calls the start() method, the thread enters the ready state. Threads in the ready state are in the ready queue, waiting for the scheduler in the JVM to schedule them.
Running state:
If a thread in the ready state acquires CPU resources, it can execute run()At this time, the thread is in the running state. Threads in the running state are the most complex, as they can change to the blocked state, ready state, and terminated state.
Blocked state:
If a thread executes methods such as sleep(sleep)、suspend(suspend), it enters the blocked state from the running state after losing the occupied resources. It can re-enter the ready state after the sleep time has elapsed or after obtaining the device resources. It can be divided into three types:
Waiting blocking: A thread in the running state executes the wait() method, causing the thread to enter the waiting blocked state.
Synchronized blocking: The thread fails to acquire the synchronized synchronization lock (because the synchronization lock is held by another thread).
Other blocking: An I was sent by calling the thread's sleep() or join() method./O When a request is made, the thread enters the blocked state. When the sleep() state times out, the join() waits for the thread to terminate or times out, or I/O After the processing is complete, the thread re-enters the ready state.
Terminated state:
When a thread in a running state completes a task or another termination condition occurs, the thread switches to the terminated state.
Each Java thread has a priority, which helps the operating system determine the scheduling order of threads.
The priority of Java threads is an integer with a value range of 1 (Thread.MIN_PRIORITY ) - 10 (Thread.MAX_PRIORITY )
By default, each thread is assigned a priority NORM_PRIORITY(5)。
A thread with a higher priority is more important for the program and should be allocated processor resources before threads with lower priority. However, thread priority cannot guarantee the order of thread execution and is highly dependent on the platform.
Criar uma thread
Java oferece três métodos para criar threads:
Criando através da implementação da interface Runnable
Criando através da herança da classe Thread
Criando uma thread através da implementação da interface Runnable
A maneira mais simples de criar uma thread é criar uma classe que implementa a interface Runnable.
Para implementar Runnable, uma classe precisa apenas chamar um método run(), declarado da seguinte forma:
public void run()
Você pode reimplementar este método, importante entender que o run() pode chamar outros métodos, usar outras classes e declarar variáveis, como a thread principal.
Após criar uma classe que implementa a interface Runnable, você pode criar uma instância de um objeto de thread dentro da classe.
A Thread define várias formas de construtor, a seguinte é a que mais utilizamos:
Thread(Runnable threadOb, String threadName);
Aqui, threadOb é um exemplo de uma classe que implementa a interface Runnable, e threadName especifica o nome da nova thread.
Após a criação da nova thread, você deve chamá-la através do método start() para que ela execute.
void start();
A seguir, há um exemplo de criar uma thread e iniciá-la: private Thread t; private String threadName; class RunnableDemo implements Runnable { threadName = name; System.out.println("Creating " + threadName); } public void run() { System.out.println("Running " + threadName); try { for (int i = 4; i > 0; i--) { System.out.println("Thread: " + threadName + ", " + i); // Dormir a thread por um tempo Thread.sleep(50); } } System.out.println("Thread " + threadName + " interrupted."); } System.out.println("Thread " + threadName + " exiting."); } public void start() { System.out.println("Starting " + threadName); if (t == null) { t = new Thread(this, threadName); t.start(); } } } public class TestThread { public static void main(String args[]) { RunnableDemo(String name) {1 RunnableDemo R-1"); R = new RunnableDemo("Thread1.start(); RunnableDemo(String name) {2 RunnableDemo R-2"); R = new RunnableDemo("Thread2.start(); } }
Os resultados da compilação e execução do programa acima são como follows:
Criando Thread-1 Thread começando-1 Criando Thread-2 Thread começando-2 Thread em execução-1 Thread: Thread-1, 4 Thread em execução-2 Thread: Thread-2, 4 Thread: Thread-1, 3 Thread: Thread-2, 3 Thread: Thread-1, 2 Thread: Thread-2, 2 Thread: Thread-1, 1 Thread: Thread-2, 1 Thread Thread-1 exiting. Thread Thread-2 exiting.
A segunda maneira de criar uma thread é criar uma nova classe que herda a classe Thread, e então criar uma instância dessa classe.
A classe herdada deve reimplementar o método run(), que é o ponto de entrada da nova thread. Ele também deve chamar o método start() para executar.
Este método, embora listado como um modo de implementação multithread, é essencialmente um exemplo de implementação da interface Runnable.
class ThreadDemo extends Thread { private Thread t; private String threadName; ThreadDemo(String name) { threadName = name; System.out.println("Creating " + threadName); } public void run() { System.out.println("Running " + threadName); try { for (int i = 4; i > 0; i--) { System.out.println("Thread: " + threadName + ", " + i); // Dormir a thread por um tempo Thread.sleep(50); } } System.out.println("Thread " + threadName + " interrupted."); } System.out.println("Thread " + threadName + " exiting."); } public void start() { System.out.println("Starting " + threadName); if (t == null) { t = new Thread(this, threadName); t.start(); } } } public class TestThread { public static void main(String args[]) { ThreadDemo T1 = new ThreadDemo("Thread-1"); T1.start(); ThreadDemo T2 = new ThreadDemo("Thread-2"); T2.start(); } }
Os resultados da compilação e execução do programa acima são como follows:
Criando Thread-1 Thread começando-1 Criando Thread-2 Thread começando-2 Thread em execução-1 Thread: Thread-1, 4 Thread em execução-2 Thread: Thread-2, 4 Thread: Thread-1, 3 Thread: Thread-2, 3 Thread: Thread-1, 2 Thread: Thread-2, 2 Thread: Thread-1, 1 Thread: Thread-2, 1 Thread Thread-1 exiting. Thread Thread-2 exiting.
A tabela a seguir lista alguns métodos importantes da classe Thread:
序号 | 方法描述 |
---|---|
1 | public void start() Fazer com que a thread comece a executar;Java A máquina virtual chama o método run da thread. |
2 | public void run() Se a thread for construída usando um objeto Runnable independente, chama o método run do objeto Runnable; caso contrário, o método não executa nenhuma operação e retorna. |
3 | public final void setName(String name) Mudar o nome da thread para coincidir com o parâmetro name. |
4 | public final void setPriority(int priority) Alterar a prioridade da thread. |
5 | public final void setDaemon(boolean on) Marcar a thread como daemon ou thread de usuário. |
6 | public final void join(long millisec) 等待该线程终止的时间最长为 millis 毫秒。 |
7 | public void interrupt() 中断线程。 |
8 | public final boolean isAlive() 测试线程是否处于活动状态。 |
测试线程是否处于活动状态。 上述方法是被 Thread 对象调用的。下面的方法是 Thread 类的静态方法。
序号 | 方法描述 |
---|---|
1 | public static void yield() 暂停当前正在执行的线程对象,并执行其他线程。 |
2 | public static void sleep(long millisec) 在指定的毫秒数内让当前正在执行的线程休眠(暂停执行),此操作受到系统计时器和调度程序精度和准确性的影响。 |
3 | public static boolean holdsLock(Object x) 当且仅当当前线程在指定的对象上保持监视器锁时,才返回 true。 |
4 | public static Thread currentThread() 返回对当前正在执行的线程对象的引用。 |
5 | public static void dumpStack() 将当前线程的堆栈跟踪打印至标准错误流。 |
如下的 ThreadClassDemo 程序演示了 Thread 类的一些方法:
// 文件名 : DisplayMessage.java // 通过实现 Runnable 接口创建线程 public class DisplayMessage implements Runnable { private String message; public DisplayMessage(String message) { this.message = message; } public void run() { while(true) { System.out.println(message); } } }
// 文件名 : GuessANumber.java // 通过继承 Thread 类创建线程 public class GuessANumber extends Thread { private int number; public GuessANumber(int number) { this.number = number; } public void run() { int contador = 0; int guess = 0; do { guess = (int) (Math.random() * 100 + 1); System.out.println(this.getName() + "guesses" + guess); contador++; } while(guess != number); System.out.println("** Correto!" + this.getName() + "em" + contador + "guesses.**"); } }
// Nome do arquivo: ThreadClassDemo.java public class ThreadClassDemo { public static void main(String [] args) { Runnable hello = new DisplayMessage("Hello"); Thread thread1 = new Thread(hello); thread1.setDaemon(true); thread1.setName("hello"); System.out.println("Iniciando thread de saudação..."); thread1.start(); Runnable bye = new DisplayMessage("Goodbye"); Thread thread2 = new Thread(bye); thread2.setPriority(Thread.MIN_PRIORITY); thread2.setDaemon(true); System.out.println("Iniciando thread de despedida..."); thread2.start(); System.out.println("Iniciando thread");3...); Thread thread3 = new GuessANumber(27); thread3.start(); try { thread3.join(); catch(InterruptedException e) { System.out.println("Thread interrompido."); } System.out.println("Iniciando thread");4...); Thread thread4 = new GuessANumber(75); thread4.start(); System.out.println("main() está a terminar..."); } }
Os resultados da execução são os seguintes, e os resultados variam a cada execução.
Iniciando thread de olá... Iniciando thread de adeus... Olá Olá Olá Olá Olá Olá Adeus Adeus Adeus Adeus Adeus .......
1. Crie uma classe que implementa a interface Callable, implementando o método call(), que será o corpo da thread e retornará um valor.
2. Crie um exemplo de classe que implementa Callable, usando FutureTask para embalar o objeto Callable, o qual encapsula o valor de retorno do método call() da Callable.
3. Use o objeto FutureTask como o target para criar e iniciar uma nova thread.
4. Chame o método get() do objeto FutureTask para obter o valor de retorno após a conclusão da sub-thread.
public class CallableThreadTest implements Callable<Integer> { public static void main(String[] args) { CallableThreadTest ctt = new CallableThreadTest(); FutureTask<Integer> ft = new FutureTask<>(ctt); for(int i = 0; i < 100;i++) { System.out.println(Thread.currentThread().getName());+" ";+i); if(i==20) { new Thread(ft, "Thread com valor de retorno").start(); } } try { System.out.println("O valor de retorno da sub-thread:");+ft.get()); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } } @Override public Integer call() throws Exception { int i = 0; for(;i<100;i++) { System.out.println(Thread.currentThread().getName());+" ";+i); } return i; } }
1. Quando se cria threads usando a implementação de interfaces Runnable, Callable, a classe de thread apenas implementa a interface Runnable ou Callable, e ainda pode herdar outras classes.
2. Quando se cria threads usando a herança da classe Thread, é simples de escrever, e, se precisar acessar a thread atual, não é necessário usar o método Thread.currentThread(), diretamente this pode obter a thread atual.
Ao programar em multi-thread, você precisa entender os seguintes conceitos:
Sincronização de threads
Comunicação entre threads
Deadlock de threads
Controle de threads: suspender, parar e restaurar
A chave para o uso eficaz de threads é entender que o programa é executado de forma concorrente, não sequencial. Por exemplo: se há dois subsistemas que precisam ser executados de forma concorrente, neste caso, é necessário usar programação de threads.
Através do uso de threads, pode-se escrever programas muito eficientes. No entanto, tenha em mente que, se você criar muitos threads, a eficiência da execução do programa realmente diminui, em vez de aumentar.
Lembre-se, a sobrecarga de contexto também é importante, se você criar muitos threads, o tempo gasto no contexto da CPU será maior do que o tempo gasto na execução do programa!