一个多线程的程序如果是通过 Runnable 接口实现的,则意味着类中的属性将被多个线程共享,那么这就会造成,如果多个线程要操作同一资源就有可能出现资源的同步问题。
一、问题的引出
class MyThread implements Runnable{
private int ticket = 5;//一共5张票
public void run(){
for(int i=0;i<10;i++){
if(ticket>0){//判断是否有剩余票
try{
Thread.sleep(10);//加入延迟
}catch (InterruptedException e){
e.printStackTrace();
}
System.out.println("卖票:ticket = " + ticket--);
}
}
}
}
public class Root{
public static void main(String[] args) {
MyThread mt = new MyThread();//定义线程对象
Thread t1 = new Thread(mt);//定义 Thread 对象
Thread t2 = new Thread(mt);//定义 Thread 对象
Thread t3 = new Thread(mt);//定义 Thread 对象
t1.start();//启动线程
t2.start();//启动线程
t3.start();//启动线程
}
}
由于程序中加入了延迟操作,所以在运行的最后出现了负数。
对于票数的操作步骤如下:
(1)判断票数是否大于 0,大于 0 则表示还有票可以卖
(2)如果票数大于 0 ,则将票卖出
但是在上面程序中,在步骤 (1)和步骤(2)之间加入了延迟操作,那么一个线程就有可能在还没有对票数进行减操作之前,其他线程就已经将票数减少了,这就出现了票数为负的情况。
想要解决这样的问题,就必须使用同步,也就是多个操作在同一时间段内只能有一个线程进行,其他线程要等待此线程完成之后才可以继续执行
二、使用同步解决问题
解决资源共享的同步操作,可以使用同步代码块和同步方法两种方式完成
1. 同步代码块
我们已知所谓的代码块就是指用“{}”括起来的一段代码,根据其位置和声明的不同,可以分为普通代码块、构造块、静态块3种,如果在代码块加上 synchronized 关键字,则此代码块就称为同步代码块
synchronized(同步对象){
需要同步的代码;
}
- 1
- 2
- 3
在使用同步代码块时必须指定一个需要同步的对象,但一般都将当前对象(this)设置成同步对象
class MyThread implements Runnable{
private int ticket = 5;//一共5张票
public void run(){
for(int i=0;i<10;i++){
synchronized (this){//设置需要同步的操作
if(ticket>0){//判断是否有剩余票
try{
Thread.sleep(10);//加入延迟
}catch (InterruptedException e){
e.printStackTrace();
}
System.out.println("卖票:ticket = " + ticket--);
}
}
}
}
}
public class Root{
public static void main(String[] args) {
MyThread mt = new MyThread();//定义线程对象
Thread t1 = new Thread(mt);//定义 Thread 对象
Thread t2 = new Thread(mt);//定义 Thread 对象
Thread t3 = new Thread(mt);//定义 Thread 对象
t1.start();//启动线程
t2.start();//启动线程
t3.start();//启动线程
}
}
- 28
- 29
2. 同步方法
除了可以将需要的代码设置成同步代码外,也可以使用 synchronized 关键字将一个方法声明成同步方法。
synchronized 方法返回值 方法名称(参数列表){
}
- 1
- 2
class MyThread implements Runnable{
private int ticket = 5;
public void run(){//覆写 run() 方法
for(int i=0;i<100;i++){
this.sale();
}
}
public synchronized void sale(){//声明同步方法
if(ticket>0){
try{
Thread.sleep(10);//加入延迟
}catch (InterruptedException e){
e.printStackTrace();
}
System.out.println("卖票:ticket = " + ticket--);
}
}
}
public class Test{
public static void main(String[] args) {
MyThread mt = new MyThread();
Thread t1 = new Thread(mt);//定义 Thread 对象
Thread t2 = new Thread(mt);//定义 Thread 对象
Thread t3 = new Thread(mt);//定义 Thread 对象
t1.start();//启动线程
t2.start();//启动线程
t3.start();//启动线程
}
}
- 28
- 29
- 30