上一篇获得了三个喜欢和一个关注,给我了继续写下去的动力,谢谢,谢谢!!!
以前没写过,写的时候发现真的很有挑战,自己也从中收获很多,希望简友们觉得如果觉得有一篇文章真的给自己一点感动,一点帮助等,请给作者一个赞,可能真的会产生蝴蝶效应,再次谢谢你们看我的文章。
以上都是废话,哈哈。接着上篇,继续讨论多线程的数据安全问题
- 为什么会出现重复的数据和负数的数据的情况?
因为多线程操作的是共享数据,mTicket,是三个线程的共享数据。如果每个线程都有自己单独的数据,是没有问题,因为那是单线程了。
重复的数据:System.out.println(Thread.currentThread().getName() + " 出售车票= " + mTicket-- + " 张");这行代码翻译成计算机可以执行的指令,当0号线执行sop(),输出7,但是刚要执行--操作的时候。1号线线程抢到了CPU的执行权,这时候0号线程没有执行 -- 操作,所以mTicket 还是7,所以输出7,出现重复的数据。
负数的数据:当mTicket =1时,0号线程做判断 > 1,进入sleep(10),放弃执行资格和执行权,1号线程判断 >0,进去进入sleep(10),这时候0号线程醒来抢到执行资格,输入0,1号线程醒来,抢到执行资格,输入-1。
- 怎么解决多线程操作共享数据的问题呢?
- 同步代码块 格式 : synchronized(对象){ 需要同步的代码}
把多个语句操作共享数据的代码给锁起来,让任意时刻只能有一个线程执行。
public class RunnableDemo1 implements Runnable {
//定义一百张车票
private int mTicket = 100;
private Object obj = new Object();
@Override
public void run() {
while(true){
synchronized (obj) {
if(mTicket > 0) {
// 模拟卡机 让线程休眠10毫秒
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " 出售车票= " + mTicket-- + " 张");
}
}
}
}
}
用同步代码块就可以解决,这里主要是那个obj,多线程同步的时候,必须是同一把锁。要不然还会出现共享数据的问题。
- 同步方法:就是把关键字synchronized 加在方法上
public class RunnableDemo implements Runnable {
//定义一百张车票
private int mTicket = 100;
@Override
public void run() {
while (true) {
sale();
}
}
private synchronized void sale() {
if (mTicket > 0) {
// 模拟卡机 让线程休眠10毫秒
// try {
// Thread.sleep(10);
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
System.out.println(Thread.currentThread().getName() + " 出售车票= " + mTicket-- + " 张");
}
}
}
非静态方法的锁是本类对象的引用 this。
public class RunnableDemo1 implements Runnable {
//定义一百张车票
private static int mTicket = 100;
@Override
public void run() {
while (true) {
sale();
}
}
private synchronized static void sale() {
if (mTicket > 0) {
// 模拟卡机 让线程休眠10毫秒
// try {
// Thread.sleep(10);
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
System.out.println(Thread.currentThread().getName() + " 出售车票= " + mTicket-- + " 张");
}
}
}
静态方法的锁是本类对象的class 字节码文件。
- 总结下:
- 同步的前提
多个线程使用的是同一个锁对象
多个线程操作共享资源 - 同步的好处
同步的出现解决了多线程的安全问题。 - 同步的弊端
当线程相当多时,因为每个线程都会去判断同步上的锁,这是很耗费资源的,无形中会降低程序的运行效率,但是为了数据的安全也值了,鱼和熊掌不能兼得。
- 两个线程实现交替打印(等待与唤醒机制)
/**
* 两个线程 实现交替打印 一
*
* 输入线程 输出 一
*
* 输出线程 输出 二
*/
public class AlternatePrintThread {
public static boolean mFlag = true;
public static void main(String[] args) {
new Thread(new InputThread()).start();
new Thread(new OutputThread()).start();
}
}
class InputThread implements Runnable {
@Override
public void run() {
while (true) {
synchronized (AlternatePrintThread.class) {
if (AlternatePrintThread.mFlag) {
try {
AlternatePrintThread.class.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
AlternatePrintThread.mFlag = true;
System.out.println("我是一 ");
AlternatePrintThread.class.notify();
}
}
}
}
class OutputThread implements Runnable {
@Override
public void run() {
while (true) {
synchronized (AlternatePrintThread.class) {
if (!AlternatePrintThread.mFlag) {
try {
AlternatePrintThread.class.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
AlternatePrintThread.mFlag = false;
System.out.println(".......我是二 ");
AlternatePrintThread.class.notify();
}
}
}
}
实现思路:
交替打印肯定是两个线程 定义两个线程类
不管你用的什么对象作为锁,连个线程一定是同一把锁
- 多生产与多消费
public class ThreadDemo {
public static void main(String[] args) {
Product p = new Product();
new Thread(new Produce(p)).start();
new Thread(new Produce(p)).start();
new Thread(new Produce(p)).start();
new Thread(new Produce(p)).start();
new Thread(new Consumer(p)).start();
new Thread(new Consumer(p)).start();
new Thread(new Consumer(p)).start();
new Thread(new Consumer(p)).start();
}
}
class Product {
//名字
private String name;
//计数器
private int count;
//标记
private boolean flag = true;
//生产方法,是让生产线程调用
public synchronized void set(String name) {
while (!flag)
try {
this.wait();
} catch (Exception e) {
}
this.name = name + count++;
System.out.println(Thread.currentThread().getName() + "生产第 " + this.name);
flag = false;
this.notify();
}
//消费方法,是让消费线程调用
public synchronized void get() {
while (flag)
try {
this.wait();
} catch (Exception e) {
}
System.out.println(Thread.currentThread().getName() + "消费第... " + this.name);
flag = true;
this.notify();
}
}
//生产者线程
class Produce implements Runnable {
private Product p;
Produce(Product p) {
this.p = p;
}
public void run() {
while (true)
p.set("Iphone");
}
}
//消费者线程
class Consumer implements Runnable {
private Product p;
Consumer(Product p) {
this.p = p;
}
public void run() {
while (true)
p.get();
}
}
notify():唤醒的是等待顺序唤醒(这是的 多生产与多消费不能用这个)
notifyAll():唤醒的是等待的全部线程 (太浪费资源,如果我想唤醒 A线程的按等待顺序的线程的一个 就好了 ,所以Lock接口来了)
线程还有一个特点就是 从那倒下去的 从那起来继续执行(要想让一段代码流转起来,那就是死循环,满足条件才让出去,否则一直转圈)
- jdk since 1.5出现行的管理线程的类
/**
* since JDK1.5 Lock ,Condition 生产者 和 消费者
*/
public class LockDemo {
public static void main(String[] args) {
Resource resource = new Resource();
new Thread(new ConsumeThread(resource)).start();
new Thread(new ConsumeThread(resource)).start();
new Thread(new ConsumeThread(resource)).start();
new Thread(new ConsumeThread(resource)).start();
new Thread(new ProductThread(resource)).start();
new Thread(new ProductThread(resource)).start();
new Thread(new ProductThread(resource)).start();
new Thread(new ProductThread(resource)).start();
}
}
/**
* 资源类
*/
class Resource {
private String productName;
private int count = 0;
private boolean flag = true;
private Lock lock = new ReentrantLock();
// 用锁创建了 两个线程的管理器 用自己的管理器去让自己的管理的线程等待 用对方的管理器去唤醒对方等待的线程
private Condition pro = lock.newCondition();
private Condition con = lock.newCondition();
public void setProductName(String productName) {
lock.lock();
while (!this.flag) {
try {
pro.await();//自己的线程管理器
} catch (InterruptedException e) {
e.printStackTrace();
}
}
this.productName = productName + " " + count++;
System.out.println("生产=" + this.productName);
this.flag = false;// 和 while (this.flag) 方法必须向反 要不 这个线程会无限等待
con.signal();//对方的线程管理器
lock.unlock();
}
public void getProductName() {
lock.lock();
while (this.flag) {
try {
con.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("消费=......" + this.productName);
this.flag = true;
pro.signal();
lock.unlock();
}
}
/**
* 生产线程
*/
class ProductThread implements Runnable {
Resource mResource;
public ProductThread(Resource resource) {
this.mResource = resource;
}
@Override
public void run() {
while (true) {
mResource.setProductName("Iphone100");
}
}
}
/**
* 消费线程
*/
class ConsumeThread implements Runnable {
Resource mResource;
public ConsumeThread(Resource resource) {
this.mResource = resource;
}
@Override
public void run() {
while (true) {
mResource.getProductName();
}
}
}
JDK1.5中,java.util.concurrent.locks包
提供线程的新的管理方式
接口Lock,替代了原有的synchronized的使用,使用灵活广泛
接口中的方法 获取锁lock() 释放锁 unlock()
接口实现类[ReentrantLock]
接口Condition 替代原有监视器方法 wait notify notifyAll
新旧方法的对比
接口中Condition Object类
await() wait()
signal() notify()
signalAll() notifyAll()
获取接口的实现类对象,用Lock接口方法newCondition实现
分解成截然不同对象(线程管理对象)
- 死锁
/**
* 多线程的死锁
*/
public class DeadLockDemo {
public static class DeadRunnable implements Runnable {
private boolean mFlag;
public DeadRunnable(boolean flag) {
this.mFlag = flag;
}
@Override
public void run() {
while (true) {
if (mFlag) {
//A锁
synchronized (ALock.mALock) {
System.out.println("A..............锁");
synchronized (BLock.mBLock) {
System.out.println("B..............锁");
}
}
} else {
//B锁
synchronized (BLock.mBLock) {
System.out.println("B..............锁");
synchronized (ALock.mALock) {
System.out.println("A..............锁");
}
}
}
}
}
}
/**
* A锁
*/
public static class ALock {
public static final ALock mALock = new ALock();
}
/**
* B锁
*/
public static class BLock {
public static final BLock mBLock = new BLock();
}
public static void main(String[] args) {
new Thread(new DeadRunnable(true)).start();
new Thread(new DeadRunnable(false)).start();
}
}
多线程的一种程序的假死状态,同步的嵌套,停了,但是没退出,出现在多线程争抢同一个同步锁的时候,才会出现