Produtor e consumidor JAVA de alta simultaneidade (JUC)

Desta vez, estamos explicando um exemplo de
problema : Título : dois threads operam em uma variável para perceber que dois threads podem adicionar 1 ao mesmo recurso, e o outro para subtrair 1, e precisa ser implementado alternadamente. O valor inicial da variável é 0 . Ou seja, dois threads executam uma operação alternada de mais um menos um no mesmo recurso.
Não há muito a dizer,
vamos lá. Primeiro, primeiro definimos os recursos da operação e definimos os métodos.

//资源类
class Resource {
    private int number = 0;

    public synchronized void up() throws InterruptedException {
        //1.判断
        if(number != 0) {
            this.wait();
        }
        //2.干活
        number++;
        System.out.println(Thread.currentThread().getName() + "\t" + number);
        this.notifyAll();
    }

    public synchronized void down() throws InterruptedException {
        if(number == 0) {
            this.wait();
        }
        number--;
        System.out.println(Thread.currentThread().getName() + "\t" + number);
        this.notifyAll();
    }
}

Em seguida, escrevemos nossos dois tópicos:

public class ThreadWaitNotifyDemo {
    public static void main(String[] args) {
        Resource resource = new Resource();
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                try {
                    resource.up();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

        }, "A").start();
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                try {
                    resource.down();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "B").start();      
    }
}

O resultado é o seguinte:
Insira a descrição da imagem aqui
Aqui usamos expressões Lambda para criar threads e iniciá-los por meio de classes internas anônimas. Você pode ver um método thread up e outro método thread down.
Primeiro, por exemplo, o thread A primeiro julga se é 0, não 0. Em seguida, adicione 1 operação neste momento. Ao mesmo tempo, o thread B julga que o número é igual a 0 e, em seguida, aguarda. Nesse momento, o thread A é adicionado e, em seguida, outros threads são ativados pelo método notificationAll (), para que seja concluído Além da operação de subtração, aqui para 0 ~ 10 é para garantir que possa ser realizada alternadamente 10 vezes.
Aqui está uma extensão do conhecimento: O método de espera e o método de notificação são o método Thread?
Resposta: errado errado errado.
Olhando para a API, podemos ver que esses dois métodos pertencem aos métodos Object. Wait e notifique devem ser usados ​​em conjunto com a palavra-chave synchronized.
Insira a descrição da imagem aqui
Insira a descrição da imagem aqui
Isso acabou? ?
Não, não, os requisitos mudaram neste momento! Neste momento, o gerente de projeto se aproximou e disse: Xiao Wang, não quero dois threads operando, quero quatro threads operando ao mesmo tempo, dois para somar e dois para subtrair, ou o tempo alternado deve ser 0 e 1 alternadamente. .
Neste momento, pensei, simples, vou adicionar mais dois tópicos!
Portanto, mais dois tópicos são adicionados.
O código é o seguinte e o Recurso permanece inalterado.

public class ThreadWaitNotifyDemo {
    public static void main(String[] args) {
        Resource resource = new Resource();
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                try {
                    resource.up();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

        }, "A").start();
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                try {
                    resource.down();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "B").start();
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                try {
                    resource.up();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "C").start();
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                try {
                    resource.down();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "D").start();
    }
}

Como antes, mais dois tópicos são adicionados, um para adicionar e outro para subtrair.
Em seguida, execute-o e o resultado é o seguinte:
Insira a descrição da imagem aqui
Aqui podemos ver que alguns são 3, qual é a situação? ? ?
Análise: Por exemplo, os threads A e C realizam operações de adição, B e D realizam operações de subtração. Se for 0, os threads B e D são esperados, à direita, A e C são bloqueados por causa do método up. O bloqueio é adicionado, portanto, há apenas um método a ser adicionado. Se A terminar a operação neste momento e, em seguida, notificar todos, o encadeamento C também executará a operação para cima neste momento, porque o encadeamento C está em if (número! = 0) {this.wait ( );} Após ser despertado, outras operações são continuadas, e o número não é julgado.O mesmo é verdade para os fios B e D, então a imagem acima aparecerá!
Então, como essa situação pode ser resolvida? ?
Porque não fizemos um novo julgamento, então o deixamos fazer um novo julgamento é eu!
Modifique o código da classe de recurso da seguinte maneira:

class Resource {
    private int number = 0;

    public synchronized void up() throws InterruptedException {
        //1.判断
        while (number != 0) {
            this.wait();
        }
        //2.干活
        number++;
        System.out.println(Thread.currentThread().getName() + "\t" + number);
        this.notifyAll();
    }

    public synchronized void down() throws InterruptedException {
        while (number == 0) {
            this.wait();
        }
        number--;
        System.out.println(Thread.currentThread().getName() + "\t" + number);
        this.notifyAll();
    }
}

O código para recursos operacionais dos quatro threads permanece inalterado e os resultados são os seguintes:

Insira a descrição da imagem aqui
Como nosso título é JUC , naturalmente usamos JUC para resolver este problema:
primeiro leia a API:
Interface de bloqueio : Interface de
Insira a descrição da imagem aqui
Insira a descrição da imagem aqui
Insira a descrição da imagem aquicondição: Insira a descrição da imagem aqui
ou seja, usamos synchronized, wait, notifique e agora usamos o bloqueio de JUC (ReentrantLock), condição , Condition.await (), condition.signalAll (); O
código é o seguinte:

class Resource {
    private int number = 0;
    private Lock lock = new ReentrantLock();  //可重入锁
    private Condition condition = lock.newCondition();

    public void up() throws InterruptedException{
        lock.lock();
        try {
            while (number != 0) {
                condition.await(); //相当于wait
            }
            number++;
            System.out.println(Thread.currentThread().getName()+"\t"+number);
            condition.signalAll(); //相当于notifyAll
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    public void down() throws InterruptedException{
        lock.lock();
        try {
            while (number == 0) {
                condition.await();
            }
            number--;
            System.out.println(Thread.currentThread().getName()+"\t"+number);
            condition.signalAll();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
}

Os resultados são os seguintes:
Insira a descrição da imagem aqui
Portanto, a questão é: por que devemos usar o JUC em vez disso?
Insira a descrição da imagem aqui
O surgimento de novas tecnologias substituirá coisas que a tecnologia não pode resolver!
Notify desperta qualquer um dos três e notificar desperta todos.
Então veja as vantagens do JUC.
Desta vez, nosso tópico é atualizado:
a sequência de chamada entre vários threads, para alcançar A-> B-> C:
a sequência de inicialização dos três threads é a seguinte:
AA imprime 5 vezes, BB imprime 10 vezes, CC imprime 15 vezes, ... continuar 10 rodadas.
O código é o seguinte: o
método principal:

public class ThreadOrderAccess {
    public static void main(String[] args) {
        ShareResource resource = new ShareResource();
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                resource.printf5();
            }
        }, "A").start();
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                resource.printf10();
            }
        }, "B").start();
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                resource.printf15();
            }
        }, "C").start();
    }
}

Classe de recurso:

class ShareResource {
    private int number = 1;// 1:A  2:B  3:C
    private Lock lock = new ReentrantLock();
    private Condition condition1 = lock.newCondition();
    private Condition condition2 = lock.newCondition();
    private Condition condition3 = lock.newCondition();

    //注意标志位的修改和定位
    //A
    public void printf5() {
        lock.lock();
        try {
            //判断
            while (number != 1) {
                condition1.await();
            }
            //干活
            for (int i = 1; i <= 5; i++) {
                System.out.println(Thread.currentThread().getName() + "\t" + i);
            }
            //通知
            number = 2; //此时B、C一直在wait状态中
            condition2.signal(); //精确打击 唤醒B
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    //B
    public void printf10() {
        lock.lock();
        try {
            while (number != 2) {
                condition2.await();
            }
            for (int i = 1; i <= 10; i++) {
                System.out.println(Thread.currentThread().getName() + "\t" + i);
            }
            number = 3;
            condition3.signal();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    //C
    public void printf15() {
        lock.lock();
        try {
            while (number != 3) {
                condition3.await();
            }
            for (int i = 1; i <= 15; i++) {
                System.out.println(Thread.currentThread().getName() + "\t" + i);
            }
            number = 1;
            condition1.signal();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

}

Observe que a espera e outros métodos a seguir são substituídos por JUC e, em seguida, a parte java.lang.IllegalMonitorStateException
do resultado é a seguinte:
Insira a descrição da imagem aqui
Insira a descrição da imagem aqui
Portanto, esta é a otimização da tecnologia antiga, notificação precisa, despertar preciso e despertar para a condição especificada para atingir o especificado O fio acorda.
Essa é a explicação para esse tempo, caso haja alguma necessidade de melhorias, deixe uma mensagem abaixo!

Acho que você gosta

Origin blog.csdn.net/Pzzzz_wwy/article/details/106008088
Recomendado
Clasificación