同步问题的引出:
范例:卖票问题
class MyThread implements Runnable {
private int ticket = 5;
@Override
public void run() {
for (int x = 0; x < 20; x++) {
if (ticket > 0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "卖票, 卖出的票数为" + ticket--);
}
}
}
}
public class test {
public static void main(String args[]) throws Exception {
MyThread mt = new MyThread();
new Thread(mt, "票贩子A").start();
new Thread(mt, "票贩子B").start();
new Thread(mt, "票贩子C").start();
new Thread(mt, "票贩子D").start();
}
}
解释:当系统只剩下一张票的时候,票贩子A检测到还剩余一张票随后进入到sleep状态,票贩子B、C、D均是如此,当大家都认为还剩一张票的时候,票贩子A 取走了这张票,ticket执行减减操作;此时剩余票数为0,这时会出现安全问题;
同步处理:
出现上述问题的原因是:判断和执行的操作是分开进行的;
解决办法:将判断剩余票数和票数减一的操作封装起来,是一起执行的,当一个线程在执行操作的时候,其他的线程将会等待;
在Java中如果想实现同步需要使用synchronized关键字 。而这个关键字可以通过2种方式使用:
- 同步代码块;
- 同步方法;
范例:观察同步块
同步块必须要锁一个对象,往往会锁住当前对象;
class MyThread implements Runnable {
private int ticket = 5;
@Override
public void run() {
for (int x = 0; x < 20; x++) {
synchronized(this){ //当前对象每次只允许一个对象进入
if (ticket > 0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "卖票, 卖出的票数为" + ticket--);
}
}
}
}
}
public class test {
public static void main(String args[]) throws Exception {
MyThread mt = new MyThread();
new Thread(mt, "票贩子A").start();
new Thread(mt, "票贩子B").start();
new Thread(mt, "票贩子C").start();
new Thread(mt, "票贩子D").start();
}
}
范例:同步方法解决
class MyThread implements Runnable {
private int ticket = 5;
@Override
public void run() {
for (int x = 0; x < 20; x++) {
this.scale();
}
}
public synchronized void scale(){ //同步方法
if (ticket > 0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "卖票, 卖出的票数为" + ticket--);
}
}
}
public class test {
public static void main(String args[]) throws Exception {
MyThread mt = new MyThread();
new Thread(mt, "票贩子A").start();
new Thread(mt, "票贩子B").start();
new Thread(mt, "票贩子C").start();
new Thread(mt, "票贩子D").start();
}
}
多个线程访问同一资源的时候需要同步操作;同步操作与异步操作比,异步操作比同步操作的速度更快,但是同步操作安全性更高,属于安全的线程操作;
死锁:
同步过多可能会产生死锁;比如本和笔的故事;
面试题:请解释多个资源访问同一资源时需要考虑哪些情况?可能会带来哪些问题?
- 多个线程访问同一资源时要处理好同步关系,可以使用同步代码块或者同步方法完成;
|- 同步代码块: synchronized (锁定对象){代码}
|- 同步方法:public synchronized 返回值 方法名称(){代码}
- 过多使用同步可能会造成死锁;