摘要:java同步代码块,了解到线程安全问题其实就是由多个线程同时处理共享资源所导致的,java 同步代码块。以下是我们为大家整理的,相信大家阅读完后肯定有了自己的选择吧。
通过 14.5.1 小节的学习,了解到线程安全问题其实就是由多个线程同时处理共享资源所
导致的。要想解决例 14-11 中的线程安全问题,必须得保证下面用于处理共享资源代码在任
何时刻只能有一个线程访问。
while (tickets>0) {
try {
Thread.sleep(10); //经过此处的线程休眠 10 毫秒
}catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread();.getName()+”---卖出的票”+tickets--);
为了实现这种限制,Java 中提供了同步机制。当多个线程使用同一个共享资源时,
可以将处理共享资源的代码放置在一个代码块中,使用 synchronized 关键字来修饰,被称
作同步代码块,其语法格式如下:
synchronized (lock) {
操作共享资源代码块
}
上面的代码中,lock 是一个锁对象,它是同步代码块的关键。当线程执行同步代码块时,首
先会检查锁对象的标志位,默认情况下标志位为 1,此时线程会执行同步代码块,同时将锁对
象的标志位置为 0。当一个新的线程执行到这段同步代码块时,由于锁对象的标志位为 0,
新线程会发生阻塞,等待当前线程执行完同步代码块后,锁对象的标志位被置为 1,新线程才
能进入同步代码块执行其中的代码。循环往复。直到共享资源被处理完为止。这个过程就好
比一个公用电话亭,只有前一个人打完电话出来后,后面的人才可以打。
接下来将例 14-11 中售票的代码放到 synchronized 区域中,如例 14-12 所示
例 14-12 Example12.java
//定义 Ticket1 类继承 Runnable 接口
class Ticket1 implements Runnable {
private int tickets = 10; // 定义变量 tickets,并赋值 10
Object lock = new Object(); // 定义任意一个对象,用作同步代码块的锁
public void run() {
while (true) {
synchronized (lock) { // 定义同步代码块
try {
Thread.sleep(10); // 经过的线程休眠 10 毫秒
} catch (InterruptedException e) {
e.printStackTrace();
}
if (tickets > 0) {
System.out.println(Thread.currentThread().getName()
+ "---卖出的票" + tickets--);
} else { // 如果 tickets 小于 0,跳出循环
break;
}
}
}
}
}
public class Example12 {
public static void main(String[] args) {
Ticket1 ticket = new Ticket1(); // 创建 Ticket1 对象
// 创建并开启四个线程
new Thread(ticket, "线程一").start();
new Thread(ticket, "线程二").start();
new Thread(ticket, "线程三").start();
new Thread(ticket, "线程四").start();
}
}
运行结果如图 14-16 所示
例14-12中,将有关tickets变量的操作全部都放到同步代码块中。为了保证线程的持续执行,
将同步代码块放在死循环中,直到 tickets<0 时跳出循环。因此,从图 5-16 所示的运行结果
可以看出,售出的票不再出现 0 和负数的情况,这是因为售票的代码实现了同步,之前出现
的线程安全问题得以解决。运行结果中并没有出现线程三售票的语句,出现这样的现象是很
正常的,这是因为线程在获得锁对象时有一定的随机性,在整个程序的运行期间,线程三始
终未获得锁对象。
注意: 同步代码块中的锁对象可以是任意类型的对象,但多个线程共享的锁对象必须是唯一
的。“任意”说的是共享锁对象的类型。所以,锁对象的创建代码不能放到 run( )方法中,否
则每个线程运行到 run()方法都会创建一个断对象,这样每个线程都会有一个不同的锁,每
个锁都有自己的标志位。线程之间便不能产生同步的效果。