java线程不安全问题_解决线程不安全问题

更多精彩文章欢迎关注公众号“Java之康庄大道”

当多个线程并发访问同一个资源对象时,可能会出现线程不安全的问题,比如现有100个高铁座位,现在有请三个窗口(A,B,C)同时售票.,此时使用多线程技术来实现这个案例.

packagecom.yunqing.ssm.test;/*** 存在线程安全问题*/

public classTestRunnable {public static voidmain(String[] args) {

Ticket tick= newTicket();

Thread t1= new Thread(tick,"A窗口");

Thread t2= new Thread(tick,"B窗口");

Thread t3= new Thread(tick,"C窗口");

t1.start();

t2.start();

t3.start();

}

}class Ticket implementsRunnable{int ticket = 100;

@Overridepublic voidrun() {while (true){try{//模拟网络延迟

Thread.currentThread().sleep(100);

}catch(InterruptedException e) {

e.printStackTrace();

}if (ticket>0)

System.out.println(Thread.currentThread().getName()+"售票,票号为:"+ticket--);

}

}

}

以上代码运行结果:

5400dd3ad3f5e661499382f7a008cca8.png

为什么编号为84的座位号被3个窗口售出了?

当A窗口打印84座位号,还没打印完的时候,其他两个线程就也进入到了84号座位票的分配操作中,所以导致线程安全问题。

要解决上述多线程并发访问多一个资源的安全性问题,就必须得保证打印座位号和座位号总数减1操作,必须同步完成.即是说,A线程进入操作的时候,B和C线程只能在外等着,A操作结束,A和B和C才有机会进入代码去执行.

解决多线程并发访问资源的安全问题,有三种方式:

方式1:同步代码块

方式2:同步方法

方式3:锁机制(Lock)

方式1:同步代码块:

语法:

synchronized(同步锁)

{

需要同步操作的代码

}

同步锁:

为了保证每个线程都能正常执行原子操作,Java引入了线程同步机制.也称为同步监听对象/同步锁/同步监听器/互斥锁。

实际上,对象的同步锁只是一个概念,可以想象为在对象上标记了一个锁,谁拿到锁,谁就可以进入代码块,其他线程只能在代码块外面等着,而且注意,在任何时候,最多允许一个线程拥有同步锁.

Java程序运行可以使用任何对象作为同步监听对象,但是一般的,我们把当前并发访问的共同资源作为同步监听对象.

package com.yunqing.ssm.test;

/**

* 存在线程安全问题

*/

public class TestRunnable {

public static void main(String[] args) {

Ticket tick = new Ticket();

Thread t1 = new Thread(tick,"A窗口");

Thread t2 = new Thread(tick,"B窗口");

Thread t3 = new Thread(tick,"C窗口");

t1.start();

t2.start();

t3.start();

}

}

class Ticket implements Runnable{

int ticket = 100;

@Override

public void run() {

while (true){

try {

//模拟网络延迟

Thread.currentThread().sleep(100);

} catch (InterruptedException e) {

e.printStackTrace();

}

//this代表Ticket对象,Ticket对象是多线程共享资源

synchronized (this){

if (ticket>0)

System.out.println(Thread.currentThread().getName()+"售票,票号为:"+ticket--);

}

}

}

}

方式2:同步方法:

使用synchronized修饰的方法,就叫做同步方法,保证A线程执行该方法的时候,其他线程只能在方法外等着.

Synchronized public void doWork(){

///TODO

}

同步锁是谁:

对于非static方法,同步锁就是this.

对于static方法,我们使用当前方法所在类的字节码对象(Ticket.class).

packagecom.yunqing.ssm.test;/***存在线程安全问题*/public classTestRunnable {

public static voidmain(String[] args) {

Runnable a = newTicket();newThread(a,"A窗口").start();newThread(a,"B窗口").start();newThread(a,"C窗口").start();}

}

classTicket implementsRunnable{

intticket= 100;synchronized private voiddoWork() throwsInterruptedException {

if(ticket>0){

System.out.println(Thread.currentThread().getName()+"售票,票号为:"+ticket);ticket--;Thread.sleep(100);}

}

@Overridepublic voidrun() {

for(inti=1;i<=100;i++){

try{

doWork();} catch(InterruptedException e) {

e.printStackTrace();}

}

}

}

注意:

不要使用synchronized修饰run方法,修饰之后,某一个线程就执行完了所有的功能.好比是多个线程出现串行.

解决方案:把需要同步操作的代码定义在一个新的方法中,并且该方法使用synchronized修饰,再在run方法中调用该新的方法即可.

实际上,同步代码块和同步方法差不了多少,在本质上是一样的,两者都用了一个关键字synchronized,synchronized保证了多线程并发访问时的同步操作,避免线程的安全性问题,但是有一个弊端,就是使用synchronized的方法/代码块的性能比不用要低一些,因此如果要用synchronized,建议尽量减小synchronized的作用域。

方式3:同步锁(锁机制)

Lock机制提供了比synchronized代码块和synchronized方法更广泛的锁定操作,同步代码块/同步方法具有的功能Lock都有,除此之外更强大,更体现面向对象.

packagecom.yunqing.ssm.test;importjava.util.concurrent.locks.Lock;importjava.util.concurrent.locks.ReentrantLock;/***存在线程安全问题*/public classTestRunnable {

public static voidmain(String[] args) {

Runnable a = newTicket();newThread(a,"A窗口").start();newThread(a,"B窗口").start();newThread(a,"C窗口").start();}

}

classTicket implementsRunnable{

intticket= 100;//创建锁对象private finalLock lock= newReentrantLock();private voiddoWork(){

//进入方法,立马加锁lock.lock();try{

if(ticket>0){

System.out.println(Thread.currentThread().getName()+"售票,票号为:"+ticket);ticket--;Thread.sleep(100);}

} catch(InterruptedException e) {

e.printStackTrace();}finally{

lock.unlock();}

}

@Overridepublic voidrun() {

for(inti=1;i<=100;i++){

doWork();}

}

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值