Параллельное программирование на Java (1) Thread Подробное объяснение

Java задняя часть GitHub Oracle

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/Срочно…


Справочная документация

docs.Oracle.com/java-color/8/do…