线程安全

Posted wurengen

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了线程安全相关的知识,希望对你有一定的参考价值。

什么是线程安全?

如果多个线程同时运行,而这些线程都访问了共享数据。程序每次运行的结果和单线程运行的结果都是一样的。而其他的变量的值和预期的是一样的,这就是线程安全。如果产生的结果和预期的不一样,这样的问题,我们就称之为线程安全问题。线程安全问题都是由全局变量和静态变量引起的。

如何解决线程安全问题?

线程安全问题是不能产生的,我们可以让一个线程在访问,操作共享数据的时候,无论是否失去了cpu的执行权,让其他的线程只能等待,等待当前线程是否了对共享数据的访问。其他线程才能访问,操作共享数据。为了解决线程安全问题。Java中提供了同步机制(synchronized)解决。要怎么样使用?

有下面三种方式:

  • 方式一:同步代码块

格式:

synchronized(锁对象){

     //可能出现线程安全的代码块(访问了共享数据的对象)

}

注意:

  • 通过代码块中的锁对象,可以使用任意的对象
  • 多个线程必须使用的是同一个锁对象
  • 锁对象的作用:只让一个线程在 

 代码举例

public class Ticket implements Runnable {
    //设置票总数
    private int ticket = 25;
    Object object = new Object();

    @Override
    public void run() {
        // 不停的卖票
        while (true) {
            //判断票是否存在
            synchronized (object) {
                if (ticket > 0) {
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    //存在就卖票
                    System.out.println(Thread.currentThread().getName() + "正在卖" + ticket + "票");
                    ticket--;

                }
            }
        }
    }
}

 同步技术的原理:

在任何的时候,最多允许一个线程拥有同步锁,谁拿到同步锁就进入代码块,其他的线程只能在外面等着。

  •  方式二:同步方法

使用synchronized修饰的方法,叫做同步方法。保证一个线程执行该方法的时候,其他的线程在方法外等着

格式:

技术图片

使用步骤:

  1. 把访问了共享数据的代码抽取出来,放在一个方法中
  2. 在方法上添加修饰符synchronized

 代码举例

package demo02;

public class Ticket implements Runnable {
    //设置票总数
    private int ticket = 25;
    @Override
    public void run() {
        // 不停的卖票
        while (true) {
            //判断票是否存在
            method();


        }
    }

    public synchronized void method() {
        if (ticket > 0) {
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            //存在就卖票
            System.out.println(Thread.currentThread().getName() + "正在卖" + ticket + "票");
            ticket--;

        }
    }
}

此方式实现也是 利用了同步锁:

技术图片

 

  •  方式三:Lock锁

java.util.concurrent.locks.Lock  机制,不但具有同步代码块和同步方法具有的功能。 而且更加强大,更能体现面向对象。

Lock锁也称之为同步锁,加锁与释放锁的方法如下:

  • public void lock ():加同步锁
  • public void unlock():释放同步锁

使用步骤:

  1. 因为lock是接口,所以第一步要在成员变量位置获取他的实现类对象ReentrantLock对象
  2. 在可能出现安全问题的代码前调用Lock接口中的方法lock获取锁
  3. 在可能出现安全问题的代码后调用Lock接口中的方法unlock释放锁

 代码举例:

package demo02;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class Ticket implements Runnable {
    //设置票总数
    private int ticket = 25;
    // 多态获取子类对象
    Lock lock = new ReentrantLock();

    @Override
    public void run() {
        // 不停的卖票
        while (true) {
            // 获取锁
            lock.lock();
            //判断票是否存在
            if (ticket > 0) {
                try {
                    Thread.sleep(100);

                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                //存在就卖票
                System.out.println(Thread.currentThread().getName() + "正在卖" + ticket + "票");
                ticket--;
            }
            //释放锁
            lock.unlock();
        }


    }
}

 

以上是关于线程安全的主要内容,如果未能解决你的问题,请参考以下文章

HashMap 和 ConcurrentHashMap 的区别

线程同步-使用ReaderWriterLockSlim类

newCacheThreadPool()newFixedThreadPool()newScheduledThreadPool()newSingleThreadExecutor()自定义线程池(代码片段

多线程 Thread 线程同步 synchronized

活动到片段方法调用带有进度条的线程

第十次总结 线程的异步和同步