一、为什么要线程同步?
同步:不是表示线程同步执行,而是表示线程之间协同执行
线程同步:当一个线程在执行时,另一个线程等待该线程执行完,再去抢夺CPU资源
当多线程访问统一资源时且对共享组员有写操作时,很容易出现线程安全,这时就需要线程机制来解决。
二、线程同步实现方式
1、同步代码块
(1)格式:
synchronized(同步锁){
可能会发生线程安全的代码
}
其中,需要创建一把锁,同步锁可以为任意对象,只要保证使用的是同一个锁
(2)代码示例
public class OneThread implements Runnable{
static int tickets=10;
//创建一把锁
Object obj=new Object();
@Override
public void run() {
while(true) {
//使用同步代码块,并指定锁
synchronized (obj) {
//判断是否还有票
if (tickets > 0) {
//执行卖票
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "正在卖" + tickets);
tickets--;
} else {
break;
}
}
}
}
}
(3)同步锁原理:
当线程一开始执行时,遇到了同步代码,这时判断同步锁是否还存在。如果存在,拿到锁开始执行同步代码中的代码。
线程二执行时,遇到了同步代码块,此时,锁在线程一那里,线程二无法获取到锁,无法执行同步代码块中的代码,线程二变为阻塞状态。等到线程一执行完成后,归还锁,这时才有机会拿到锁,执行同步代码块代码。
2、同步方法
(1)格式:
权限修饰符 synchronized 返回值类型. 方法名(参数列表)锁对象默认为this
//同步方法 锁对象:this
public synchronized void test(){
//可能会出现线程安全的代码
//判断是否还有票
if (tickets > 0) {
//执行卖票
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "正在卖" + tickets);
tickets--;
}
}
3、静态同步方法——与同步方法类似
静态资源随着类加载而加载,成员资源在创建对象时才加载。
锁默认为:当前类.class
4、Lock锁
实现步骤:
(1)创建ReentrantLock对象
(2)重写run方法
(3)在可能出现线程问题的代码前,调用lock方法
(4)在可能出现线程问题的代码后,调用unlock方法
//1.创建ReentrantLock对象
ReentrantLock lock=new ReentrantLock();
//2.重写run方法
@Override
public void run() {
while (true){
test();
}
}
public void test(){
//3.
lock.lock();
//判断是否还有票
if (tickets > 0) {
//执行卖票
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "正在卖" + tickets);
tickets--;
}
//4.
lock.unlock();
}