I. Обзор
Прежде чем мы начнем изучать Thread, давайте посмотримОтношения между потоками и процессами:
Поток — это сущность процесса и основная единица планирования и диспетчеризации ЦП. Потоки не могут выполняться независимо и должны зависеть от прикладной программы, а прикладная программа обеспечивает управление выполнением нескольких потоков. Связь между потоками и процессами заключается в том, что потоки принадлежат процессам, потоки выполняются в пространстве процесса, а потоки, созданные одним и тем же процессом, совместно используют одно и то же пространство памяти. очищено.
Из приведенного выше описания можно узнать, что поток является основной единицей планирования ЦП, и только многопоток используется для полного использования многоядерных ресурсов ЦП.
Эта статья основана на JDK 8 (также может называться JDK 1.8).
2. Использование потоков
2.1 Начать тему
Существует четыре способа создания потока:
- Реализовать интерфейс Runnable
- Наследовать от класса Thread
- Лямбда с использованием JDK 8
- Использование callables и фьючерсов
2.1.1 Как создать Runnable
public class MyThread implements Runnable {
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
}
Thread thread = new Thread(new MyThread());
thread.start();
2.1.2 Наследование способа создания потока
public class MyThread extends Thread{
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
}
MyThread thread = new MyThread();
thread.start();
Приведенный выше код имеет более простой способ написания:
Thread thread = new Thread(){
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
};
thread.start();
2.1.3 Метод создания лямбды
new Thread(()-> System.out.println(Thread.currentThread().getName())).start();
2.1.4 Использование Callable и Future
Глядя на исходный код, вы можете узнать, что родительским классом Thread является Runnable, который предоставляется JDK1.0, а Callable, аналогичный Runnable, предоставляется JDK1.5, что компенсирует ситуацию, когда вызывающий поток не имеет возвращаемого значения. Его можно рассматривать как дополнение к Runnable. Следующее Взгляните на реализацию Callable.
public class MyThread implements Callable<String> {
@Override
public String call() throws Exception {
System.out.println(Thread.currentThread().getName());
return Thread.currentThread().getName();
}
}
Callable<String> callable = new MyThread();
FutureTask<String> ft = new FutureTask<>(callable);
new Thread(ft,"threadName").start();
System.out.println(ft.get());
2.1.5 Разница между запуском () и запуском ()
Это метод start(), который фактически запускает поток вместо метода run().Как и обычный метод-член, run() можно использовать повторно, но он не может запускать новый поток.
2.2 Общие методы Thread
Метод класса потока
метод | инструкция |
---|---|
start() | начать нить |
setName(String name) | установить имя потока |
setPriority(int priority) | Установить приоритет потока, по умолчанию 5, значение 1-10 |
join(long millisec) | Приостановить поток на xx миллисекунд, параметры можно не указывать |
interrupt() | убить нить |
isAlive() | Проверить, жив ли поток |
Статический (статический) метод потока
метод | инструкция |
---|---|
yield() | Приостановить выполнение текущего объекта потока и выполнить другие потоки. |
sleep(long millisec)/sleep(long millis, int nanos) | Приостановить поток на xx секунд, параметр нельзя опустить |
currentThread() | Возвращает ссылку на текущий исполняемый объект потока |
holdsLock(Object x) | Владеет ли текущий поток блокировкой |
2.3 Разница между sleep() и wait()
sleep — это метод потока, а wait — метод объекта. Их функции схожи. Самая большая существенная разница заключается в том, что sleep не снимает блокировку, а wait снимает блокировку.
Различия в использовании: сон (миллисекунды) можно автоматически разбудить, указав время. Если времени недостаточно, вы можете вызвать прерывание () только для завершения потока; ожидание () можно разбудить напрямую с помощью notify () / notifyAll ().
Особенности:Код для проверки снятия блокировки с помощью ожидания и сна выглядит следующим образом:
public class SynchronizedTest extends Thread {
int number = 10;
public synchronized void first(){
System.out.println("this is first!");
number = number+1;
}
public synchronized void secord() throws InterruptedException {
System.out.println("this is secord!!");
Thread.sleep(1000);
// this.wait(1000);
number = number*100;
}
@Override
public void run() {
first();
}
}
SynchronizedTest synchronizedTest = new SynchronizedTest();
synchronizedTest.start();
synchronizedTest.secord();
// 主线程稍等10毫秒
Thread.sleep(10);
System.out.println(synchronizedTest.number);
По результатам можно узнать, что:
- Результат выполнения sleep(1000): 1001
- Результат выполнения wait(1000): 1100
Суммировать:Использование SLEEP (1000) не освобождает синхронную блокировку, 10 * 100 + 1 = 1001, Wait (1000) снимает блокировку, порядок выполнения (10 + 1) X100 = 1100, поэтому SLEEP не освобождает замок, подождите, отпустите замок.
3. Статус потока
3.1 Обзор статуса потока
статус темы:
- НОВОЕ не началось
- RUNNABLE выполняется
- BLOCKED заблокирован (заблокирован блокировкой синхронизации или блокировкой ввода-вывода)
- Ожидание постоянного ожидания
- TIMED_WAITING Ожидание указанного времени для повторного пробуждения
- TERMINATED выполнение завершено
Статус потока можно просмотреть с помощью getState(). Для получения дополнительной информации о статусе см. исходный код потока, как показано ниже:
3.2 Реализация кода состояния потока
3.2.1 Состояние NEW не запущено
Thread thread = new Thread() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
};
// 只声明不调用start()方法,得到的状态是NEW
System.out.println(thread.getState()); // NEW
3.2.2 Рабочий статус RUNNABLE
Thread thread = new Thread() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
};
thread.start();
System.out.println(thread.getState()); // RUNNABLE
3.2.3 Заблокированное состояние блокировки
Используя реализацию синхронизированной синхронной блокировки, код выглядит следующим образом:
public class MyCounter {
int counter;
public synchronized void increase() {
counter++;
try {
Thread.sleep(10*1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
MyCounter myCounter = new MyCounter();
// 线程1调用同步线程,模拟阻塞
new Thread(()-> myCounter.increase()).start();
// 线程2继续调用同步阻塞方法
Thread thread = new Thread(()-> myCounter.increase());
thread.start();
// 让主线程等10毫秒
Thread.currentThread().sleep(10);
// 打印线程2,为阻塞状态:BLOCKED
System.out.println(thread.getState());
3.2.4 WAITING постоянное состояние ожидания
public class MyThread extends Thread{
@Override
public void run() {
synchronized (MyThread.class){
try {
MyThread.class.wait();
System.out.println(Thread.currentThread().getName());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
Thread thread = new Thread(new MyThread());
thread.start();
// 主线程挂起200毫秒,等thread执行完成
Thread.sleep(200);
// 输出WAITING,线程thread一直处于被挂起状态
System.out.println(thread.getState());
Разбудите ветку:Вы можете использовать метод notify/notifyAll, код выглядит следующим образом:
synchronized (MyThread.class) {
MyThread.class.notify();
}
Способ сделать нить ОЖИДАНИЕ:
- Ожидание объекта() не устанавливает тайм-аут
- Thread.join() не устанавливает тайм-аут
- Парк LockSupport()
Глядя на исходный код Thread, вы можете узнать метод соединения Thread.Нижний уровень реализован ожиданием объекта, как показано на следующем рисунке:
Уведомление:Глядя на исходный код Object, вы можете видеть, что wait() не передает параметры, что эквивалентно ожиданию (0). Набор «0» не выполняется немедленно, а бесконечно ждет, не выполняется, как показано ниже:
3.2.5 Состояние ожидания тайм-аута TIMED_WAITING
В состоянии TIMED_WAITING нужно только установить время ожидания, код такой:
public class MyThread extends Thread{
@Override
public void run() {
synchronized (MyThread.class){
try {
MyThread.class.wait(1000);
System.out.println(Thread.currentThread().getName());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
Код вызова остается прежним, а именно:
Thread thread = new Thread(new MyThread());
thread.start();
// 主线程挂起200毫秒,等thread执行完成
Thread.sleep(200);
// 输出TIMED_WAITING
System.out.println(thread.getState());
synchronized (MyThread.class) {
MyThread.class.notify();
}
3.2.6 Завершенное состояние завершения
Thread thread = new Thread(()-> System.out.println(Thread.currentThread().getName()));
thread.start();
// 让主线程等10毫秒
Thread.currentThread().sleep(10);
System.out.println(thread.getState());
4. Тупик
Согласно предыдущему знанию, мы знаем, что блокировка не снимается при использовании сна, поэтому мы можем легко написать код взаимоблокировки, используя эту функцию.
код показывает, как показано ниже:
static Object object1 = new Object();
static Object object2 = new Object();
public static void main(String[] args) {
Thread thread = new Thread(){
@Override
public void run() {
synchronized (object1){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (object2){
System.out.println(Thread.currentThread().getName());
}
}
}
};
Thread thread2 = new Thread(){
@Override
public void run() {
synchronized (object2){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (object1){
System.out.println(Thread.currentThread().getName());
}
}
}
};
thread.start();
thread2.start();
Запустив приведенный выше код, программа будет ждать бесконечно долго.
V. Резюме
Согласно приведенному выше содержанию, мы систематически изучали использование Thread, но обучение без размышлений бесполезно Наконец, мы оставляем вопрос: согласно знаниям, представленным в этой статье, как мы можем избежать взаимоблокировки? (Ха-ха, продай, ответ можно получить по комбинации очков знаний в статье)
Загрузка исходного кода:GitHub.com/VIP stone/Срочно…
Справочная документация