线程学习二(线程同步机制和线程通信)
线程安全问题存在的原因
当一个线程在操作共享资源的时候,未执行完毕的情况下,另一个线程也参与进来,进行共享资源的操作,此时就有可能出现线程安全问题。
如何解决线程安全问题
当一个线程在操作共享资源的时候,其他线程必须等待,只有当该线程执行完对共享资源的操作之后,其他线程才有机会操作共享资源。
为了解决线程安全问题,可以使用线程的同步机制
方式一:同步代码块
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();
}
}

浙公网安备 33010602011771号