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:
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.
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:
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:
Como nosso título é JUC , naturalmente usamos JUC para resolver este problema:
primeiro leia a API:
Interface de bloqueio : Interface de
condição:
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:
Portanto, a questão é: por que devemos usar o JUC em vez disso?
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:
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!