文章目录
synchronized 关键字及线程同步
解决线程安全问题的二种方案:
使用同步代码块
使用步骤:
1.把访问了共享数据的代码块取出来,放到一个方法中
2.在方法上添加synchronized修饰符
有时运行一些线程需要共享数据,例如两个线程同时存取一个数据流,其中一个对数据进行了修改,而另外一个线程使用的是原来的数据,这就带来了数据不一致的问题,如果多线程同时操作一个对象,则称该对象不是线程安全的。为了使多线程机制能够正常运转,需要采取一些措施来防止两个线程访问相同资源的冲突。特别是在关键的时期,为了防止出现这样的冲突,需要在线程使用一个资源时为其加锁。访问资源的第一个线程加上锁以后,其他线程就不能再使用这个资源了除非第一个线程被解锁。
对一种特殊的资源,Java提供了内建的机制来防止它们的冲突。这种机制就是对相关的方法使用关键字synchronized
为了协调和同步线程对共享数据的操作,java.lang.Obiect类中提供了wait()方法和notify()方法实现线程的同步。这两个方法始终在循环中使用:不带参数的wait()方法一直等待,单位为毫秒,到达参数指定的时间后,线程又成为就绪状态;如果没有到达指定的时
间,则需要其他线程调用notify()方法将其唤醒。
使用wait()方法和notify()方法,同时配合条件检查可以有效控制多线程之间的运行
顺序,实现多线程之间的同步。
解决线程安全问题的二种方案:使用lock锁
使用步骤:
- void lock()获取锁
- void unlock()释放锁
代码及运行结果
package demo01;
public class Demo01Ticket {
public static void main(String[] args) {
RunnableImpl run =new RunnableImpl();
Thread t0=new Thread(run);
Thread t1=new Thread(run);
Thread t2=new Thread(run);
t0.start();
t1.start();
t2.start();
}
}
package demo01;
/*
* 解决线程安全问题的二种方案:使用同步代码块
* 使用步骤:
* 1.把访问了共享数据的代码块取出来,放到一个方法中
* 2.在方法上添加synchronized修饰符
* */
public class RunnableImpl implements Runnable {
private int ticket =100;
@Override
public void run() {
while(true) {
while(true) {
payTicket();}
}
}
public synchronized void payTicket() {
if(ticket>0) {
try {
Thread.sleep(10);
}catch(InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"-->正在卖第"+ticket+"张票");
ticket--;
}
}
}
package demo02;
public class Demo02Ticket {
public static void main(String[] args) {
RunnableImpl run =new RunnableImpl();
Thread t0=new Thread(run);
Thread t1=new Thread(run);
Thread t2=new Thread(run);
t0.start();
t1.start();
t2.start();
}
}
package demo02;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/*
* 解决线程安全问题的二种方案:使用lock锁
* 使用步骤:
* 1. void lock()获取锁
* 2. void unlock()释放锁
*
* */
public class RunnableImpl implements Runnable {
private int ticket =100;
//1.在成员位置创建一个ReentrantLock对象
Lock l=new ReentrantLock();
@Override
public void run() {
while(true) {
//2.在可能会出现线程安全的地方,调用Lock接口中的lock方法,获取锁
l.lock();
if(ticket>0) {
try {
Thread.sleep(10);
System.out.println(Thread.currentThread().getName()+"-->正在卖第"+ticket+"张票");
ticket--;
}catch(InterruptedException e) {
e.printStackTrace();
}finally {
//3.在可能会出现线程安全的地方,调用unLock 释放锁
l.unlock();
}
}
}
}
}
package demo03;
/*
* 等待唤醒案例:
* 创建一个顾客线程,告知老板要的包子种类,用wait()方法等待,放弃cpu执行
* 创建一个老板线程,花五秒做一个包子,做好之后,用notif通知顾客吃包子
*
* */
public class WaitAndNotify{
public static void main(String[] args) {
Object obj=new Object();
//创建第一个消费者
new Thread() {
@Override
public void run() {
while(true) {
synchronized(obj){
System.out.println("消费者1,告知老板包子要的种类");
try {
obj.wait();
}catch(InterruptedException e) {
e.printStackTrace();
}
System.out.println("消费者1,包子已经做好了,开吃");
System.out.println("=========");
}
}
}
}.start();
new Thread() {
@Override
public void run() {
while(true) {
synchronized(obj){
System.out.println("消费者2,告知老板包子要的种类");
try {
Thread.sleep(2000);
}catch(InterruptedException e) {
e.printStackTrace();
}
System.out.println("消费者2,包子已经做好了,开吃");
System.out.println("=========");
}
}
}
}.start();}
}