Java多线程(3)

1.判断程序是否会出现线程安全问题

  • 是否是多线程环境

  • 是否存在共享数据

  • 是否多个线程操作共享数据

    public class MyThread01 implements Runnable {
        private int ticket = 10;
    
        @Override
        public void run() {
           while (ticket>0){
               System.out.println(Thread.currentThread().getName() + "正在出售第" + ticket + "张票");
               ticket--;
           }
        }
    }
    class TestMyThread01 {
        public static void main(String[] args) {
            MyThread01 r = new MyThread01();
            Thread t1 = new Thread(r);
            Thread t2 = new Thread(r);
            Thread t3 = new Thread(r);
            t1.setName("窗口1");
            t2.setName("窗口2");
            t3.setName("窗口3");
    
            t1.start();
            t2.start();
            t3.start();
        }
    }
    运行结果:
    窗口2正在出售第10张票
    窗口2正在出售第9张票
    窗口2正在出售第8张票
    窗口2正在出售第7张票
    窗口2正在出售第6张票
    窗口2正在出售第5张票
    窗口2正在出售第4张票
    窗口2正在出售第3张票
    窗口2正在出售第2张票
    窗口2正在出售第1张票
    窗口1正在出售第10张票
    窗口3正在出售第10张票
    

以上是模拟多窗口售票的代码,可以发现在多线程环境下数据产生了问题,第10张票被卖了3次,这显然是很不合理的。

2.如何解决线程安全问题

解决办法:同步机制,同步代码块,同步方法,使任意一个时刻只能有一个线程操作这个资源。

2.1 同步代码
public class MyThread02 implements Runnable {
    private int ticket = 10;

    @Override
    public void run() {
        while (ticket > 0) {
            synchronized (this) {
                if(ticket>0){
                    //System.out.println(this.getClass().getName().toString());
                    System.out.println(Thread.currentThread().getName() + "正在出售第" + ticket-- + "张票");
                }
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

上述代码在操作数据的位置加上可synchronized关键字,格式:

synchronized(锁对象){
	可能会出现线程安全问题的代码;
}

当某一个线程执行同步代码块时,其它线程将无法执行当前同步代码块,会发生阻塞,等当前线程执行完同步代码块后,所有的线程开始抢夺线程的执行权,抢到执行权的线程将进入同步代码块,执行其中的代码。

2.2 同步方法
public class MyThread03 implements Runnable {
    private int ticket = 10;

    @Override
    public void run() {
        while (ticket > 0) {
            sell();
        }
    }

    //在方法上加synchronized关键字
    private synchronized void sell() {
        if (ticket > 0) {
            System.out.println(Thread.currentThread().getName() + "正在出售第" + ticket-- + "张票");
        }
        try {
            Thread.sleep(50);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
2.3 synchronized关键字总结

synchronized关键字的三种应用方式:

  1. 普通同步方法上加锁(实例方法),锁是当前实例对象 ,进入同步代码前要获得当前实例的锁
  2. 在静态方法上加锁,锁是当前类的class对象 ,进入同步代码前要获得当前类对象的锁
  3. 在代码块上加锁,锁是括号里面的对象,可以是class对象,实例对象,或者是其他任意对象。

同步的作用:解决了线程安全问题。
同步的弊端:当线程相当多时,每个线程都会去判断同步上的锁,需要消耗耗费较多资源,降低程序的运行效率。

3.常见的线程安全的类

  • StringBuffer
  • HashTable
  • Vector
  • ConcurrentHashMap

4.Lock锁

4.1 lock锁介绍

JDK5以后提供了一个新的锁,Lock。Lock是一个接口,里面实现的方法如下:

//获取锁
void lock();
//获取锁过程中可以响应中断
void lockInterruptibly() throws InterruptedException;
//非阻塞式响应能够马上返回,获取锁返回true,反之false
boolean tryLock();
//超时获取锁,在超时time内或未中断情况下,可以获取锁
boolean tryLock(long time, TimeUnit unit) throws InterruptedException;


//释放锁
void unlock();
//获取与lock绑定的等待通知组件,当前线程必须获取锁才能进行等待
//等待时释放锁,当再次获取锁才能从等待中返回
Condition newCondition();

lock()、tryLock()、tryLock(long time, TimeUnit unit)和lockInterruptibly()是获取锁的方法。以下是lock()的使用方法:

public class MyThread04 implements Runnable {
    private int ticket = 10;
    
    //Lock接口的实现类
    private Lock lock = new ReentrantLock();

    @Override
    public void run() {
        while (ticket > 0) {
            //获取锁,加锁和解锁处需要通过lock()和unlock()显示指出。
            //释放锁的过程必须手动执行,所以一般会在finally块中写unlock()以防死锁。
            lock.lock();
            try {
                if (ticket > 0) {
                    System.out.println(Thread.currentThread().getName() + "正在出售第" + ticket-- + "张票");
                }
                Thread.sleep(50);
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                //释放锁
                lock.unlock();
            }

        }
    }
}
4.2 synchronized和lock的区别
  1. synchronized是java中的关键字,由jvm执行,Lock是接口,提供了操作锁的代码。
  2. synchronized在发生异常时,会自动释放掉锁,而Lock则不会,一般需要在finally里释放。
  3. Lock能够响应中断,让等待状态的线程停止等待;synchronized不行。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值