1.线程同步(加锁)这里针对上一节的卖票程序做改进,就是线程的同步
多线程并发访问的时候回出现数据安全问题:
* 解决方式:
* 1、同步代码块
* synchronized(共享资源、共享对象,需要是object的子类){具体执行的代码块}
* 2、同步方法
* 将核心的代码逻辑定义成一个方法,使用synchronized关键字进行修饰,此时不需要指定共享对象
同步代码块:
*
* 使用接口的方式,每次只创建了一个共享对象,所有的线程能够实现资源共享
* 1、数据不一致的问题
* 解决方法:线程同步(同步代码块)
*
*/
public class TicketRunnable2 implements Runnable {
private int ticket = 5;
@Override
public void run() {
for (int i = 0; i < 100; i++) {
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (this){
if (ticket > 0) {
System.out.println(Thread.currentThread().getName() + "正在出售第" + (ticket--) + "张票");
}
}
}
}
public static void main(String[] args) {
TicketRunnable2 ticket = new TicketRunnable2();
Thread t1 = new Thread(ticket,"A");
Thread t2 = new Thread(ticket,"B");
Thread t3 = new Thread(ticket,"C");
Thread t4 = new Thread(ticket,"D");
t1.start();
t2.start();
t3.start();
t4.start();
}
同步方法:
**
* 使用接口的方式,每次只创建了一个共享对象,所有的线程能够实现资源共享
* 1、数据不一致的问题
* 解决方法:线程同步
*/
public class TicketRunnable3 implements Runnable {
private int ticket = 5;
@Override
public void run() {
for (int i = 0; i < 100; i++) {
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
this.sale();
}
}
/*
* 使用同步方法解决多线程数据安全的问题
* */
public synchronized void sale() {
if (ticket > 0) {
System.out.println(Thread.currentThread().getName() + "正在出售第" + (ticket--) + "张票");
}
}
public static void main(String[] args) {
TicketRunnable3 ticket = new TicketRunnable3();
Thread t1 = new Thread(ticket, "A");
Thread t2 = new Thread(ticket, "B");
Thread t3 = new Thread(ticket, "C");
Thread t4 = new Thread(ticket, "D");
t1.start();
t2.start();
t3.start();
t4.start();
}
2.线程同步小结:
▪ 同步监视器
– synchronized(obj){}中的obj称为同步监视器
– 同步代码块中同步监视器可以是任何对象,但是推荐使用共享资源作为同步监 视器
– 同步方法中无需指定同步监视器,因为同步方法的监视器是this,也就是该对象 本身
▪ 同步监视器的执行过程
– 第一个线程访问,锁定同步监视器,执行其中代码
– 第二个线程访问,发现同步监视器被锁定,无法访问
– 第一个线程访问完毕,解锁同步监视器
– 第二个线程访问,发现同步监视器未锁,锁定并访问
生产者:
package com.mashibing.Thread.pc3;
public class Producer implements Runnable {
private Goods goods;
public Producer(Goods goods) {
this.goods = goods;
}
@Override
public void run() {
for (int i=0;i<10;i++){
if (i%2==0){
goods.set("娃哈哈","矿泉水");
}else {
goods.set("旺仔","小馒头");
}
}
}
}
消费者:
package com.mashibing.Thread.pc3;
public class Comsumer implements Runnable {
private Goods goods;
public Comsumer(Goods goods) {
this.goods = goods;
}
@Override
public void run() {
for (int i=0;i<10;i++){
goods.get();
}
}
}
共享对象:
package com.mashibing.Thread.pc3;
public class Goods {
private String brand;
private String name;
//默认是不存在商品的,如果值等于true的话,代表有商品
private boolean flag = false;
public String getBrand() {
return brand;
}
public void setBrand(String brand) {
this.brand = brand;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
//生产者生产商品
public synchronized void set(String brand,String name){
//当生产者抢占到cpu资源之后会判断当前对象是否有值,如果有的话,以为着消费者还没有消费,需要提醒消费者消费,同时
//当前线程进入阻塞状态,等待消费者取走商品之后,再次生产,如果没有的话,不需要等待,不需要进入阻塞状态,直接生产即可
if (flag){
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
this.setBrand(brand);
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
this.setName(name);
System.out.println("生产者生产了"+this.getBrand()+"--"+this.getName());
//如果代码执行到此处,意味着已经生产完成,需要将flag设置为true
flag = true;
//唤醒消费者去进行消费
notify();
}
//消费者获取商品
public synchronized void get(){
/*
* 如果flag等于false的话,意味着生产者没有生产商品,此时消费者无法消费,需要让消费者线程进入到阻塞状态,等待生产者生产,当
* 有商品之后,再开始消费
* */
if (!flag){
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("消费者取走了"+this.getBrand()+"--"+this.getName());
flag = false;
//唤醒生产者去进行生产
notify();
}
}
测试类:
package com.mashibing.Thread.pc3;
public class Test {
public static void main(String[] args) {
Goods goods = new Goods();
Producer producer = new Producer(goods);
Comsumer comsumer = new Comsumer(goods);
Thread thread = new Thread(producer);
Thread thread1 = new Thread(comsumer);
thread.start();
thread1.start();
}
}
具体的版本更替从pc1-pc4详细讲本机代码;
生产者:
package com.mashibing.Thread.pc4;
import java.util.concurrent.BlockingDeque;
import java.util.concurrent.BlockingQueue;
public class ProducerQueue implements Runnable {
private BlockingQueue<Goods> blockingQueue;
public ProducerQueue(BlockingDeque<Goods> blockingDeque) {
this.blockingQueue = blockingDeque;
}
@Override
public void run() {
for (int i=0;i<10;i++){
Goods goods = null;
if (i%2==0){
goods = new Goods("哇哈哈","矿泉水");
}else {
goods = new Goods("旺仔","小馒头");
}
System.out.println("生产者开始生产商品:"+goods.getBrand()+"--"+goods.getName());
try {
blockingQueue.put(goods);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
消费者:
package com.mashibing.Thread.pc4;
import java.util.concurrent.BlockingDeque;
import java.util.concurrent.BlockingQueue;
public class ConsumerQueue implements Runnable {
private BlockingQueue<Goods> blockingQueue;
public ConsumerQueue(BlockingDeque blockingDeque) {
this.blockingQueue = blockingDeque;
}
@Override
public void run() {
for (int i=0;i<10;i++){
try {
Goods goods = blockingQueue.take();
System.out.println("消费者消费的商品是:"+goods.getBrand()+"--"+goods.getName());
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
共享对象:
package com.mashibing.Thread.pc4;
public class Goods {
private String brand;
private String name;
public Goods(String brand, String name) {
this.brand = brand;
this.name = name;
}
public String getBrand() {
return brand;
}
public void setBrand(String brand) {
this.brand = brand;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
测试类:
package com.mashibing.Thread.pc4;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
public class Test {
public static void main(String[] args) {
BlockingQueue<Goods> queue1 = new ArrayBlockingQueue<Goods>(5);
ProducerQueue producerQueue = new ProducerQueue(queue1);
ConsumerQueue consumerQueue = new ConsumerQueue(queue1);
new Thread(producerQueue).start();
new Thread(consumerQueue).start();
}