JAVA学习笔记之多线程专题(一):线程同步安全处理

来源:互联网 发布:java 选择汉字验证码 编辑:程序博客网 时间:2024/05/25 13:33

关于多线程操作,我相信大家都不陌生,如何开启一个线程之类我想就不用太详细的去描述,今天我们就来讲讲线程同步的安全的问题。

对于线程同步安全问题,一般是一个多线程对同一个资源同时操作的时候,会出现资源同时操作造成线程不安全的问题。那么这个时候我们需要去对公共资源进行同步保护。这个时候有三种情况

1、同步代码块,这个同步的锁是任意一个对象;

2、方法同步,这个同步的锁就是该方法所在的类;

3、静态方法同步,这个同步的锁是该方法所在类的字节码。

接下来,我们举一个例子来说明多线程对同一个资源进行操作的时候,如何达到资源同步安全的问题。我们以生产消费的例子,生产一个商品,消费一个商品的循环。同时讲解线程同步中线程等待和唤醒的用法。

请看代码:

public class ThreadTest {public static void main(String[] args) {// 资源Resources r = new Resources();// 生产工厂Producer pro = new Producer(r);// 消费者Consumer con = new Consumer(r);// 四个线程同时进行生产和消费,实现生产一个消费一个,避免出现对资源的重复混乱应用Thread t1 = new Thread(pro);Thread t2 = new Thread(pro);Thread t3 = new Thread(con);Thread t4 = new Thread(con);t1.start();t2.start();t3.start();t4.start();}public static class Resources {public Resources() {};// 产品名称private String name;// 产品序号private int count = 1;// 循环标记位private boolean flag = false;// 生产方法public synchronized void produce(String name) {// 循环标记位while (flag) {try {this.wait();} catch (Exception e) {}}// 生产出一个产品this.name = name + "--" + count++;System.out.println(Thread.currentThread().getName() + "制造一个商品"+ this.name);// 标记位转换,进入循环flag = true;// 为避免唤醒的线程队列中的第一个线程不是消费线程,我们选择全部唤醒this.notifyAll();}// 消费方法public synchronized void consume() {while (!flag) {try {this.wait();} catch (Exception e) {}}System.out.println(Thread.currentThread().getName() + "消耗一个商品"+ this.name);// 标记位转换,进入循环flag = false;// 为避免唤醒的线程队列中的第一个线程不是生产线程,我们选择全部唤醒this.notifyAll();}}// 生产者static class Producer implements Runnable {private Resources res;Producer(Resources res) {this.res = res;}public void run() {while (true) {res.produce("商品");}}}// 消费者static class Consumer implements Runnable {private Resources res;Consumer(Resources res) {this.res = res;}public void run() {while (true) {res.consume();}}}}

我们发现,在上述过程中,我们会唤醒所有的等待线程,然后在根据循环标示为去控制生产与消费。这样总给是非常不好的感觉。当线程多的时候,会造成线程浪费。在JDK5.0之后,提供了线程同步锁Lock类来解决这一个问题。使用Lock类可以替代同步关键字来达到对公共资源的保护。我看直接看代码:

import java.util.concurrent.locks.Condition;import java.util.concurrent.locks.Lock;import java.util.concurrent.locks.ReentrantLock;public class ThreadTest2 {public static void main(String[] args) {Resource r = new Resource();Producer pro = new Producer(r);Consumer con = new Consumer(r);Thread t1 = new Thread(pro);Thread t2 = new Thread(pro);Thread t3 = new Thread(con);Thread t4 = new Thread(con);t1.start();t2.start();t3.start();t4.start();}static class Resource {// 新建一个锁private Lock lock = new ReentrantLock();private String name;private int count = 1;private boolean flag = false;// 拿到对应的唤醒条件private Condition proCondition = lock.newCondition();private Condition conCondition = lock.newCondition();public void product(String name) {// 上锁lock.lock();try {while (flag) {// 等待proCondition.await();}this.name = name + "--" + count++;System.out.println(Thread.currentThread().getName() + "生产者"+ this.name);flag = true;// 唤醒消费线程conCondition.signal();} catch (Exception e) {} finally {// 解锁lock.unlock();}}public void consume() {// 上锁lock.lock();try {while (!flag) {conCondition.wait();}System.out.println(Thread.currentThread().getName() + "消费者----"+ this.name);flag = false;// 唤醒生产者proCondition.signal();} catch (Exception e) {} finally {// 解锁lock.unlock();}}}static class Producer implements Runnable {private Resource res;Producer(Resource res) {this.res = res;}public void run() {while (true) {res.product("商品");}}}static class Consumer implements Runnable {private Resource res;Consumer(Resource res) {this.res = res;}public void run() {while (true) {res.consume();}}}}

关于Lock类的更多使用方法大家可以参照JDK文档。

以上就是线程同步安全的内容。




0 0
原创粉丝点击