线程安全
- 什么是线程安全:
- synchronize两种用法:
- 1,同步代码块
- 2,同步方法
- lock的用法:
- Lock锁的API
- lock方法的使用
- tryLock()方法的使用
- tryLock(long time, TimeUnit unit)方法的使用
什么是线程安全:
在多线程环境下,线程安全是避免不了的,在Java中可以使用synchronize关键字来解决线程安全问题。
线程不安全的实例:
public class Example09 {
public static void main(String[] args) {
BusTicket3 busTicket = new BusTicket3();
//开启四个线程
new Thread(busTicket, "窗口1").start();
new Thread(busTicket, "窗口2").start();
new Thread(busTicket, "窗口3").start();
new Thread(busTicket, "窗口4").start();
}
}
class BusTicket3 implements Runnable {
private int ticket = 10;//车票数量
public void run() {
while (ticket > 0) {
//获取线程的名字
String threadName = Thread.currentThread().getName();
System.out.println(threadName + "正在发第" + ticket-- + "张票");
}
}
}
运行结果:
从上面的运行结果可以看出来,四个售票窗口同时卖十张车票,正常是同一张车票不会卖出去两次。正是用于线程不安全导致这同一张车票售出重复问题。线程安全并不是每次都会发生,不发生并不代表不存在安全问题。我们使用synchronize关键字解决这个问题。
synchronize两种用法:
1,同步代码块
public class Example09 {
public static void main(String[] args) {
BusTicket3 busTicket = new BusTicket3();
//开启四个线程
new Thread(busTicket, "窗口1").start();
new Thread(busTicket, "窗口2").start();
new Thread(busTicket, "窗口3").start();
new Thread(busTicket, "窗口4").start();
}
}
class BusTicket3 implements Runnable {
private int ticket = 10;//车票数量
Object lock = new Object();//定义一个对象,作为同步代码块的锁
public void run() {
while (ticket > 0) {
synchronized (lock) {//定义同步代码块
try {
Thread.sleep(500);
} catch (Exception e) {
e.printStackTrace();
}
if (ticket > 0) {
//获取线程的名字
String threadName = Thread.currentThread().getName();
System.out.println(threadName + "正在发第" + ticket-- + "张票");
} else {
break;
}
}
}
}
}
可自己运行结果看效果
2,同步方法
public class Example09 {
public static void main(String[] args) {
BusTicket3 busTicket = new BusTicket3();
//开启四个线程
new Thread(busTicket, "窗口1").start();
new Thread(busTicket, "窗口2").start();
new Thread(busTicket, "窗口3").start();
new Thread(busTicket, "窗口4").start();
}
}
class BusTicket3 implements Runnable {
private int ticket = 10;//车票数量
public void run() {
while (ticket > 0) {
try {
sellTicket();
if (ticket <= 0) break;
}catch (Exception e){
e.printStackTrace();
}
}
}
//使用synchronized修饰卖票的方法
private synchronized void sellTicket() throws Exception{
if (ticket > 0){
Thread.sleep(500);
//获取线程的名字
String threadName = Thread.currentThread().getName();
System.out.println(threadName + "正在发第" + ticket-- + "张票");
}
}
}
lock的用法:
Lock是在Java1.6被引入进来的,Lock的引入让锁有了可操作性,什么意思?就是我们在需要的时候去手动的获取锁和释放锁,甚至我们还可以中断获取以及超时获取的同步特性,但是从使用上说Lock明显没有synchronized使用起来方便快捷。
Lock锁的API
lock方法的使用
实例:
public class Example10 {
public static void main(String[] args) {
BusTicket4 busTicket = new BusTicket4();
//开启两个线程
new Thread(busTicket, "窗口1").start();
new Thread(busTicket, "窗口2").start();
}
}
//BusTicket4实现Runnable接口
class BusTicket4 implements Runnable {
private Lock lock = new ReentrantLock(); // ReentrantLock是Lock的子类
private int ticket = 10;//车票数量
public void run() {
lock.lock();//加锁
for (int i = 1; i <= 5; i++) {
System.out.println(Thread.currentThread().getName() + "在出售第" + i + "张");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
lock.unlock();//释放锁
}
}
运行结果:
tryLock()方法的使用
tryLock(),锁在空闲的才能获取锁(未获得锁不会等待,lock()未获得锁会进入阻塞状态 ),返回值为boolean类型,获取锁则为 true ,反之为 false。
实例:
public class Example11 {
public static void main(String[] args) {
BusTicket5 busTicket = new BusTicket5();
//开启两个线程
new Thread(busTicket, "窗口1").start();
new Thread(busTicket, "窗口2").start();
}
}
//BusTicket4实现Runnable接口
class BusTicket5 implements Runnable {
private Lock lock = new ReentrantLock(); // ReentrantLock是Lock的子类
private int ticket = 10;//车票数量
public void run() {
if (lock.tryLock()) {//lock.tryLock()返回值是一个Boolean值
System.out.println(Thread.currentThread().getName() + ":获得锁");
for (int i = 1; i <= ticket; i++) {
System.out.println(Thread.currentThread().getName() + "在出售第" + i + "张");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
lock.unlock();
}else {
System.out.println(Thread.currentThread().getName() + ":未获得锁");
}
}
}
运行结果:
tryLock(long time, TimeUnit unit)方法的使用
如果锁定可用,则此方法立即返回值true。如果锁不可用,则当前线程将被禁用以进行线程调度,并且在发生以下三种情况之一之前处于休眠状态:
- 当前线程获取锁。
- 其他一些线程中断当前线程。
- 等待时间过去了,返回false
实例:
public class Example12 {
public static void main(String[] args) {
BusTicket6 busTicket = new BusTicket6();
//开启两个线程
new Thread(busTicket, "窗口1").start();
new Thread(busTicket, "窗口2").start();
new Thread(busTicket, "窗口3").start();
}
}
//BusTicket4实现Runnable接口
class BusTicket6 implements Runnable {
private Lock lock = new ReentrantLock(); // ReentrantLock是Lock的子类
private int ticket = 10;//车票数量
public void run() {
try {
if (lock.tryLock(1000, TimeUnit.MICROSECONDS)) {
System.out.println(Thread.currentThread().getName() + ":获得锁");
for (int i = 1; i <= ticket; i++) {
System.out.println(Thread.currentThread().getName() + "在出售第" + i + "张");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
lock.unlock();
} else {
System.out.println(Thread.currentThread().getName() + ":未获得锁");
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
加油吧!!!