一、前言
解决线程同步问题有三种方式:同步代码块、同步方法、锁(JDK5新增)
使用synchronized
解决线程同步问题要时时刻刻注意要使用同一个同步监视器,特别是在继承创建多线程是要格外注意。
二、同步代码块
1、语法
synchronized(同步监视器){
需要被同步的代码
}
2、对语法的说明
需要被同步的代码就是操作共享数据的代码
同步监视器可以是任何类的对象,但必须是唯一的
实现创建多线程的案例中,通常用this作为同步监视器。继承创建多线程中,通常用 类名.class
充当同步监视器
3、代码演示
代码背景:创建两个窗口卖票,总票数是100张,通过实现Runnable接口创建多线程
public class Ticket implements Runnable {
private int ticket = 100;
@Override
public void run() {
while (true) {
//ticket是共享数据,所以在此处包起来
//同步监视器可以是任何类的对象,但必须是唯一的,
//因为实现Runnable的方式只会new一个对象,所以此处用this充当同步监视器
synchronized (this) {
if (ticket > 0) {
System.out.println(Thread.currentThread().getName() + "卖出票号为:" + ticket);
ticket--;
} else {
break;
}
}
}
}
}
//测试类
public class Test {
public static void main(String[] args) {
Ticket ticket = new Ticket();
Thread t1 = new Thread(ticket);
Thread t2 = new Thread(ticket);
t1.start();
t2.start();
}
}
4、总结
这样处理可以做到线程安全,但是在同步代码块中线程是单线程执行,效率会降低
三、同步方法
1、语法
public synchronized void 方法名(){
//操作共享数据的代码
}
2、对语法的说明
同步方法仍然涉及到同步监视器,只是不需要我们显式的声明
非静态的同步方法,同步监视器是this
静态的同步方法,同步监视器是当前类本身
3、代码演示
public class Ticket extends Thread{
private static int ticket = 100;
@Override
public void run() {
while (true){
demo();
if (ticket == 0){
break;
}
}
}
//处理共享数据的方法正好在这个方法里面,所以将此方法声明为synchronized的
//因为同步监视器必须唯一,所以将此方法声明为static的
private static synchronized void demo(){
if (ticket > 0){
System.out.println(Thread.currentThread().getName() + "的票号是:" + ticket);
ticket--;
}
}
}
四、Lock方法
1、步骤
① 实例化ReentrantLock对象
② 在try块中开启锁
③ 在finally块中关闭锁
2、代码演示
public class Ticket extends Thread {
private static int ticket = 100;
//实例化ReentrantLock对象
private static ReentrantLock lock = new ReentrantLock();
@Override
public void run() {
while (true) {
try {
//开启锁
lock.lock();
if (ticket > 0) {
System.out.println(getName() + "票号为:" + ticket);
ticket--;
} else {
break;
}
} finally {
//关闭锁
lock.unlock();
}
}
}
}
五、总结
1、synchronized 和 lock 的异同
同:都能解决线程安全问题
不同:①、Lock需要手动的开启和关闭锁,而synchronized 是出了作用域自动关闭锁
②、使用Lock锁,JVM将花费较少的时间来调度线程,性能更好,并且具有良好的拓展性