java多线程并发------共享变量安全

先看一个多线程卖票的demo

卖票程序  piao

package thread.example.saletickets;

public class piao implements Runnable {
	@Override
	public void run() {
		 int count = 10;
		while (count > 0) {
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
				if (count > 0) {
					count--;
					System.out.println(Thread.currentThread().getName() + "卖出一张票,票还剩" + count);
			}

		}

	}

} 

测试程序

package thread.example.saletickets;

public class chuangkou {
public static void main(String[] args) {
	piao piao =new piao();
	
	Thread thread1 =new Thread(piao,"窗口1");
	Thread thread2 =new Thread(piao,"窗口2");
	Thread thread3 =new Thread(piao,"窗口3");
	Thread thread4 =new Thread(piao,"窗口4");
	thread1.start();
	thread2.start();
	thread3.start();
	thread4.start();
}  
}

运行结果

窗口4卖出一张票,票还剩9
窗口2卖出一张票,票还剩9
窗口3卖出一张票,票还剩9
窗口1卖出一张票,票还剩9
窗口2卖出一张票,票还剩8
窗口4卖出一张票,票还剩8
窗口1卖出一张票,票还剩8
窗口3卖出一张票,票还剩8
窗口4卖出一张票,票还剩7
窗口2卖出一张票,票还剩7
窗口3卖出一张票,票还剩7
窗口1卖出一张票,票还剩7
窗口4卖出一张票,票还剩6
窗口2卖出一张票,票还剩6
窗口3卖出一张票,票还剩6
窗口1卖出一张票,票还剩6
窗口2卖出一张票,票还剩5
窗口4卖出一张票,票还剩5
窗口3卖出一张票,票还剩5
窗口1卖出一张票,票还剩5
窗口2卖出一张票,票还剩4
窗口4卖出一张票,票还剩4
窗口1卖出一张票,票还剩4
窗口3卖出一张票,票还剩4
窗口1卖出一张票,票还剩3
窗口4卖出一张票,票还剩3
窗口2卖出一张票,票还剩3
窗口3卖出一张票,票还剩3
窗口4卖出一张票,票还剩2
窗口3卖出一张票,票还剩2
窗口1卖出一张票,票还剩2
窗口2卖出一张票,票还剩2
窗口2卖出一张票,票还剩1
窗口4卖出一张票,票还剩1
窗口3卖出一张票,票还剩1
窗口1卖出一张票,票还剩1
窗口2卖出一张票,票还剩0
窗口1卖出一张票,票还剩0
窗口3卖出一张票,票还剩0
窗口4卖出一张票,票还剩0

结果 每个窗口卖出了十张票,所以一共卖出四十张票,不符合业务场景。

为什么会出现这种情况,因为int count = 10;是局部变量,所以每个线程互不干扰,各自初始化10张票。

这里涉及到:类变量和实例变量的区别在于:类变量是所有对象共有,其中一个对象将它值改变,其他对象得到的就是改变后的结果;而实例变量则属对象私有,某一个对象将其值改变,不影响其他对象; 参考文章:https://blog.csdn.net/Mint6/article/details/80876180

下面修改为共享变量,也就是类变量。

修改卖票程序就行,把局部变量int count =10 改为 private static int count = 10;

package thread.example.saletickets;

public class piao implements Runnable {
	private static int count = 10;

	@Override

	public void run() {
		while (count > 0) {
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
				if (count > 0) {
					count--;
					System.out.println(Thread.currentThread().getName() + "卖出一张票,票还剩" + count);
			}

		}

	}

}

结果  变量是共享了,但是多线程情况下执行混乱,还是不满足场景

窗口3卖出一张票,票还剩7
窗口4卖出一张票,票还剩7
窗口2卖出一张票,票还剩7
窗口1卖出一张票,票还剩7
窗口2卖出一张票,票还剩6
窗口3卖出一张票,票还剩4
窗口4卖出一张票,票还剩5
窗口1卖出一张票,票还剩6
窗口4卖出一张票,票还剩2
窗口2卖出一张票,票还剩0
窗口3卖出一张票,票还剩1
窗口1卖出一张票,票还剩1

把程序修改为同步的,加上同步代码块,让卖票行为是同步的,当一个线程执行时,其他线程等待。

package thread.example.saletickets;

public class piao implements Runnable {
	private static int count = 10;

	@Override

	public void run() {
		while (count > 0) {
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			synchronized (this) {
				if (count > 0) {
					count--;
					System.out.println(Thread.currentThread().getName() + "卖出一张票,票还剩" + count);
				}

			}

		}

	}

}

结果正确

窗口3卖出一张票,票还剩9
窗口4卖出一张票,票还剩8
窗口2卖出一张票,票还剩7
窗口1卖出一张票,票还剩6
窗口3卖出一张票,票还剩5
窗口2卖出一张票,票还剩4
窗口4卖出一张票,票还剩3
窗口1卖出一张票,票还剩2
窗口4卖出一张票,票还剩1
窗口3卖出一张票,票还剩0

本节介绍了多线程情况下,类变量会导致的安全问题。

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 多线程的读者-写者问题是一个经典的同步问题,涉及到多个读线程和写线程对共享数据的访问。以下是一个可能的Java多线程读者-写者问题的实现代码: ```java class ReaderWriter { int readers = 0; int writers = 0; int writeRequests = 0; public synchronized void startRead() throws InterruptedException { while (writers > 0 || writeRequests > 0) { wait(); } readers++; } public synchronized void endRead() { readers--; notifyAll(); } public synchronized void startWrite() throws InterruptedException { writeRequests++; while (readers > 0 || writers > 0) { wait(); } writeRequests--; writers++; } public synchronized void endWrite() { writers--; notifyAll(); } } class Reader implements Runnable { ReaderWriter readerWriter; public Reader(ReaderWriter readerWriter) { this.readerWriter = readerWriter; } public void run() { try { readerWriter.startRead(); // 读取共享数据的操作 Thread.sleep((int) (Math.random() * 1000)); readerWriter.endRead(); } catch (InterruptedException e) { e.printStackTrace(); } } } class Writer implements Runnable { ReaderWriter readerWriter; public Writer(ReaderWriter readerWriter) { this.readerWriter = readerWriter; } public void run() { try { readerWriter.startWrite(); // 写入共享数据的操作 Thread.sleep((int) (Math.random() * 1000)); readerWriter.endWrite(); } catch (InterruptedException e) { e.printStackTrace(); } } } public class Main { public static void main(String[] args) { ReaderWriter readerWriter = new ReaderWriter(); for (int i = 0; i < 5; i++) { new Thread(new Reader(readerWriter)).start(); } for (int i = 0; i < 5; i++) { new Thread(new Writer(readerWriter)).start(); } } } ``` 在上面的代码中,`ReaderWriter`类是一个用于管理读者和写者访问共享数据的同步类。`startRead()`和`endRead()`方法用于读者访问共享数据的开始和结束,`startWrite()`和`endWrite()`方法用于写者访问共享数据的开始和结束。在每个方法中使用`synchronized`关键字来保证同一时间只有一个线程可以执行。`notifyAll()`方法用于唤醒其他正在等待的线程。 `Reader`类和`Writer`类分别是读者和写者线程的实现。在`run()`方法中,先调用对应的`startRead()`或`startWrite()`方法来获取访问权限,然后执行读取或写入操作,最后调用对应的`endRead()`或`endWrite()`方法来释放访问权限。 在`main()`方法中创建了5个读者线程和5个写者线程,并启动它们。 以上代码是一种可能的多线程读者-写者问题的实现,但并不是唯一的解决方案。在实际应用中,还需要考虑更多的场景和线程同步的细节,确保共享数据的一致性和线程的安全执行。 ### 回答2: Java多线程读者-写者问题可以通过使用synchronized关键字和wait()、notify()方法来实现。下面是一个简单的示例代码: ``` public class ReaderWriterProblem { private static final int MAX_READERS = 5; private static final int MAX_WRITERS = 2; private static int activeReaders = 0; private static boolean writerActive = false; public static void main(String[] args) { for (int i = 1; i <= MAX_READERS; i++) { new Thread(new Reader(i)).start(); } for (int i = 1; i <= MAX_WRITERS; i++) { new Thread(new Writer(i)).start(); } } static class Reader implements Runnable { private final int readerId; public Reader(int readerId) { this.readerId = readerId; } @Override public void run() { while (true) { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } read(); } } private void read() { synchronized (ReaderWriterProblem.class) { while (writerActive) { // 如果有写者在执行 try { ReaderWriterProblem.class.wait(); // 等待写者完成 } catch (InterruptedException e) { e.printStackTrace(); } } activeReaders++; // 增加活跃读者数量 } // 执行读操作 System.out.println("Reader " + readerId + "正在执行读操作"); synchronized (ReaderWriterProblem.class) { activeReaders--; // 减少活跃读者数量 if (activeReaders == 0) { // 如果没有其他读者 ReaderWriterProblem.class.notifyAll(); // 唤醒所有等待的线程 } } } } static class Writer implements Runnable { private final int writerId; public Writer(int writerId) { this.writerId = writerId; } @Override public void run() { while (true) { try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } write(); } } private void write() { synchronized (ReaderWriterProblem.class) { while (activeReaders > 0 || writerActive) { // 如果有读者在执行或者有写者在执行 try { ReaderWriterProblem.class.wait(); // 等待其他线程完成操作 } catch (InterruptedException e) { e.printStackTrace(); } } writerActive = true; } // 执行写操作 System.out.println("Writer " + writerId + "正在执行写操作"); synchronized (ReaderWriterProblem.class) { writerActive = false; ReaderWriterProblem.class.notifyAll(); // 唤醒所有等待的线程 } } } } ``` 在上述代码中,我们使用了一个整型变量`activeReaders`来记录当前活跃的读者数量,当读者开始读操作时,会先判断是否有写者在执行,如果有,则等待写者完成;然后增加`activeReaders`;接着执行读操作。读操作完成后,减少`activeReaders`,如果没有其他读者,则调用`notifyAll()`方法唤醒其他等待的线程。写者也类似,当写者开始写操作时,会先判断是否有其他读者或写者在执行,如果有,则等待其他线程完成;然后执行写操作;最后,设置`writerActive`为false,并调用`notifyAll()`方法唤醒其他等待的线程。 这种方式实现的读者-写者问题可以保证同一时间只能有一个写者或多个读者执行操作,从而保证数据的一致性和并发访问的正确性。 ### 回答3: Java多线程读者-写者问题可以通过使用synchronized关键字和wait()、notify()方法实现。 首先,读者-写者问题是指多个读者线程可以同时读取数据,但写者线程只能在没有读者线程时才能写入数据。 下面是使用Java实现的一个基本读者-写者问题的代码示例: ```java public class ReaderWriter { private int readers; private boolean isWriting; public ReaderWriter() { readers = 0; isWriting = false; } public synchronized void startRead() throws InterruptedException { while (isWriting) { wait(); } readers++; } public synchronized void endRead() { readers--; if (readers == 0) { notifyAll(); } } public synchronized void startWrite() throws InterruptedException { while (isWriting || readers > 0) { wait(); } isWriting = true; } public synchronized void endWrite() { isWriting = false; notifyAll(); } } ``` 上面的代码中,ReaderWriter类用于管理读者和写者的访问。其中,startRead()方法用于读者开始读取数据,endRead()方法用于读者结束读取;startWrite()方法用于写者开始写入数据,endWrite()方法用于写者结束写入。 在startRead()和startWrite()方法中,使用synchronized关键字修饰,确保同一时间只能有一个线程访问该方法。在读者读取数据时,如果有写者在写入,则调用wait()方法使读者线程进入等待状态。在写者写入数据时,如果有其他读者在读取,也调用wait()方法使写者线程进入等待状态。 在endRead()和endWrite()方法中,对读者和写者的数量进行更新,并使用notifyAll()方法唤醒等待的线程。当所有读者都结束读取时,唤醒可能等待的写者线程;当写者结束写入时,唤醒可能等待的读者线程。 通过以上方法的实现,能够实现读者-写者问题的多线程并发访问。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值