线程学习二(线程同步机制和线程通信)

线程安全问题存在的原因

当一个线程在操作共享资源的时候,未执行完毕的情况下,另一个线程也参与进来,进行共享资源的操作,此时就有可能出现线程安全问题。

如何解决线程安全问题

当一个线程在操作共享资源的时候,其他线程必须等待,只有当该线程执行完对共享资源的操作之后,其他线程才有机会操作共享资源。

为了解决线程安全问题,可以使用线程的同步机制

方式一:同步代码块

  synchronized(同步监视器){

    // 需要被同步的代码(操作共享数据的代码)

  }

  共享数据:多个线程共同操作的同一份数据(变量)

  同步监视器:用一个对象来充当,此对象必须在所有线程中只有一份。只有线程获取了这个监视器,才能执行同步代码块

  要求所有的线程必须共有一把锁
   在实现的方式中,如果实现同步,可以使用this关键字来充当锁。

方式二:同步方法

  将操作共享数据的方法声明为synchronized,此方法为同步方法
  能够保证当其中一个线程执行方法时,而其他线程在外等待直到线程执行到此方法

线程通信

关键方法:

  wait():方法使当前线程进入阻塞状态

  notify():唤醒优先级最高的线程

  notifyAll():唤醒所有线程

案例:

问题:

生产者(Productor)将产品交给店员(Clerk),而消费者(Customer)从店员处取走产品,店员一次只能持有固定数量的产品(比如:20),如果生产者试图生产更多的产品,店员会叫生产者停一下,如果店中有空位放产品了再通知生产者继续生产;如果店中没有产品了,店员会告诉消费者等一下,如果店中有产品了再通知消费者来取走产品。

这里可能出现两个问题:

生产者比消费者快时,消费者会漏掉一些数据没有取到。

消费者比生产者快时,消费者会取相同的数据。

 

class Clerk{

int product=20;

 

// 生产产品

public synchronized void addProduct(){

if (product>=20) {

try {

wait();

} catch (InterruptedException e) {

e.printStackTrace();

}

}else{

product++;

System.out.println(Thread.currentThread().getName()+":"+"生产了第"+product+"个产品");

notifyAll();

}

}

// 消费产品

public synchronized void consumeProduct(){

if (product<=0) {

try {

wait();

} catch (InterruptedException e) {

e.printStackTrace();

}

}else {

System.out.println(Thread.currentThread().getName()+":"+"消费了第"+product+"个产品");

product--;

notifyAll();

}

}

 

}

 

// 生产者

class Producer implements Runnable{

Clerk clerk;

 

public Producer(Clerk clerk) {

this.clerk = clerk;

}

 

@Override

public void run() {

 

System.out.println("生产者开始生产产品");

while (true) {

 

try {

Thread.currentThread().sleep(10);

} catch (InterruptedException e) {

e.printStackTrace();

}

// 调用生产的方法

clerk.addProduct();;

}

 

}

}

 

// 消费者

class Consumer implements Runnable{

Clerk clerk;

 

public Consumer(Clerk clerk) {

super();

this.clerk = clerk;

}

 

@Override

public void run() {

 

System.out.println("消费者消费产品");

while (true) {

try {

Thread.currentThread().sleep(10);

} catch (InterruptedException e) {

e.printStackTrace();

}

// 调用消费的方法

clerk.consumeProduct();

}

 

}

 

}

 

public class TestProductor {

public static void main(String[] args) {

// 创建一个店员的对象

Clerk clerk = new Clerk();

 

// 模拟生产者和消费者

Producer p1 = new Producer(clerk);

Consumer c1 = new Consumer(clerk);

 

// 创建生产者线程

Thread t1 = new Thread(p1);

Thread t3 = new Thread(p1);

// 创建消费者的线程

Thread t2 = new Thread(c1);

 

// 设置名称

t1.setName("生产者1");

t2.setName("消费者1");

t3.setName("生产者2");

 

// 启动线程

t1.start();

t2.start();

t3.start();

 

}

}

 

posted on 2019-01-24 17:10  青稞靓仔  阅读(204)  评论(0)    收藏  举报