Java(线程安全)
为什么要使用线程同步?
当使用多个线程访问同一资源的时候,且多个线程对资源有写的操作,就容易出现线程安全问题,在Java中提供了同步机制(synchronized)来解决
一、完成线程同步的三种方法
1、同步代码块
2、同步方法
3、锁机制
1、同步代码块
1.1概念
synchronized关键字可以用于方法中的某个区块中,表示只对这个区块的资源实行互斥访问;
1.2格式
synchronized(同步锁){
需要同步操作对的代码(访问了共享数据的代码)
}
注意:
1、通过代码块中的锁对象,可以使用任意的对象
2、但是必须保证多个线程使用的锁对象是同一个
3、锁对象作用:
把同步代码块锁住,只让一个线程在同步代码块中执行
1.3同步锁
对象的同步锁:
锁对象,可以是任意类型
多个线程的:使用同一把锁
注意:在任何时候,最多允许一个线程拥有同步锁,谁拿到锁就进入代码块,其余线程在外等待;
1.4 代码实现
//创建Runnableimpl 实现类
public class Runnableimpl implements Runnable {
//定义一个多个线程共享的票原
private int ticker = 100;
//创建一个锁对象
Object obj = new Object();
//设置线程任务
@Override
public void run() {
//死循环,卖票操作重复执行
while (true) {
//同步代码块
synchronized (obj) {
if (ticker > 0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
//票存在,卖票
System.out.println(Thread.currentThread().getName()
+ "->正在卖第" + ticker + "张票");
ticker--;
}
}
}
}
}
/**
* 模拟卖票案例
* 创建三个线程,同时开启,对共享的票进行出售
* <p>
* 卖票出现了线程安全问题,
* 卖出了不存在和重复的票
* <p>
* 解决方案一:使用同步代码块
*/
public class Test {
public static void main(String[] args) {
//创建Runnable接口的实现类对象
Runnableimpl run = new Runnableimpl();
//创建Thread类对象,构造方法中传递Runnable接口的实现类对
Thread t0 = new Thread(run);
Thread t1 = new Thread(run);
Thread t2 = new Thread(run);
//调用start方法 。开启多线程
t0.start();
t1.start();
t2.start();
}
}
1.5同步技术的原理
使用了一个锁对象,这个锁对象叫做对象锁,也叫对象监视器
3个线程一起抢夺cpu的执行权,谁抢到了谁执行run方法,进行卖票
t0抢到了cpu的执行权,执行run方法,遇到synchronized代码块
这时t0会检查synchronized代码块是否有锁对象
发现有,就会获取到锁对象,进入到同步代码块中执行
t1抢到了cpu的执行权,执行run方法,遇到synchronized代码块
t1检查查synchronized代码块是否有锁对象
发现没有,t1就会进入到阻塞状态,会一直等待t0线程归还锁对象
一直到t0线程执行完同步中的代码,会把锁对象归还给同步代码块
t1才能获取锁对象进入到同步中执行
总结:
1、同步中的线程,没有执行完毕不会释放同步锁,同步外的线程没有锁进不去同步
2、同步保证了只能有一个线程在同步中执行共享数据
3、同步保证了安全
4、程序频繁的判断锁,释放锁,程序的效率会降低
2、同步方法
2.1概念
使用synchronized修饰的方法,就叫同步方法,
保证A线程执行该方法的时候,其他线程只能在方法外等着
2.2格式
修饰符 synchronized 返回值类型 方法名(参数列表){
可能会产生线程安全问题的代码
}
2.3实现步骤
1、把访问了共享数据的代码抽取出来,方法一个方法中
2、在方法上添加synchronized修饰符
2.4代码实现
//创建实现Runnable接口的类
public class Runnableimpl implements Runnable {
//定义一个多个线程共享的票原
private int ticker = 100;
//创建一个锁对象
Object obj = new Object();
//设置线程任务
@Override
public void run() {
//死循环,卖票操作重复执行
while (true) {
payTicket();
}
}
//定义一个同步方法
//同步方法会把方法内部的代码锁住,只让一个线程执行
//同步方法的锁对象是谁?是实现类对象 new Runnableimple() 也就是this
/*
public synchronized void payTicket() {
//先判断票是否存在
if (ticker > 0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
//票存在,卖票
System.out.println(Thread.currentThread().getName()
+ "->正在卖第" + ticker + "张票");
ticker--;
}
}
*/
//或者
public void payTicket() {
synchronized (this) {
if (ticker > 0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
//票存在,卖票
System.out.println(Thread.currentThread().getName()
+ "->正在卖第" + ticker + "张票");
ticker--;
}
}
}
}
/*
* 解决方案二、:使用同步方法
*/
public class Test {
public static void main(String[] args) {
//创建Runnable接口的实现类对象
Runnableimpl run = new Runnableimpl();
//创建Thread类对象,构造方法中传递Runnable接口的实现类对
Thread t0 = new Thread(run);
Thread t1 = new Thread(run);
Thread t2 = new Thread(run);
//调用start方法 。开启多线程
t0.start();
t1.start();
t2.start();
}
}
附:静态的同步方法
锁对象是谁?
不能是this,this是创建对象之后产生的,静态方法优先于对象
静态方法的锁对象是本类的class属性-->class文件对象(反射)
锁对象使用:Runnbaleimpl.class
3、Lock锁
3.1概念
java.util.concurrent.locks.lock接口
锁机制提供了比synchronized代码块和syschronized方法更广泛的锁定操作。
3.2常用方法
void lock()获得锁。
void unlock()释放锁。
3.3使用步骤
java.util.concurrent.locks.ReentrantLock implements lock接口
1、在成员位置创建一个ReentrantLock对象
2、在可能会出现安全问题的代码前调用lock接口中的方法lock获取锁
3、在可能会出现安全问题的代码后调用lock接口中的方法unlock释放锁
3.4实现代码
public class Runnableimpl implements Runnable {
//定义一个多个线程共享的票原
private int ticker = 100;
//1\在成员位置创建一个Reentrantlock对象
Lock l = new ReentrantLock();
//设置线程任务
@Override
public void run() {
//死循环,卖票操作重复执行
while (true) {
//2\在可能出现安全问题的代码前调用lock接口中的方法lock获取锁
l.lock();
if (ticker > 0) {
try {
Thread.sleep(100);
//票存在,卖票
System.out.println(Thread.currentThread().getName()
+ "->正在卖第" + ticker + "张票");
ticker--;
} catch (InterruptedException e) {
e.printStackTrace();
}finally {//无论是否出现异常,都会释放锁
//3\在可能出现安全问题的代码后调用lock接口中的方法unlock释放锁
l.unlock();
}
}
}
}
}
/*
* 解决方案三、:使用Lock锁
*/
public class Test {
public static void main(String[] args) {
//创建Runnable接口的实现类对象
Runnableimpl run = new Runnableimpl();
//创建Thread类对象,构造方法中传递Runnable接口的实现类对
Thread t0 = new Thread(run);
Thread t1 = new Thread(run);
Thread t2 = new Thread(run);
//调用start方法 。开启多线程
t0.start();
t1.start();
t2.start();
}
}

浙公网安备 33010602011771号