写在前:多个线程访问同一资源的时候一定要考虑到同步的问题,但是过多的同步会带来死锁问题。
同步:
所谓的同步问题指的是多个线程操作同一资源时所带来的信息的安全性问题
例如五个线程卖六张票
package cn.itcast.thread3;
//同步问题
class MyThread1 implements Runnable{
private int ticket = 6;
@Override
public void run() {
// TODO Auto-generated method stub
for(int x = 0; x < 10;x++) {
if(this.ticket > 0) {
try {
Thread.sleep(1000);
}catch(InterruptedException e){
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+ " 卖票,ticket = " + this.ticket--);
}
}
}
}
public class TestDemo3 {
public static void main(String[] args) {
MyThread1 mt = new MyThread1();
new Thread(mt,"A").start();
new Thread(mt,"B").start();
new Thread(mt,"C").start();
new Thread(mt,"D").start();
new Thread(mt,"E").start();
}
}
运行结果:
E 卖票,ticket = 6
B 卖票,ticket = 5
C 卖票,ticket = 3
A 卖票,ticket = 4
D 卖票,ticket = 2
D 卖票,ticket = -2
C 卖票,ticket = -1
E 卖票,ticket = -1
A 卖票,ticket = 1
B 卖票,ticket = 0
原因分析:
如果说现在只剩下最后一张票了,一个线程判断条件满足,但是再它还没有修改票数之后,其他线程
也同时通过了 if 判断,所以最终修改票数的时候就变成了负数 。
解决方法1——同步代码块
使用 synchronized 关键字定义的代码块就称为同步代码块,但是在进行同步的操作之中必须设置一个要同步的对象,而这个对象应该理解为当前对象:this。
package cn.itcast.thread3_1;
//争夺问题解决-同步代码块
class MyThread1 implements Runnable {
private int ticket = 6;
@Override
public void run() {
// TODO Auto-generated method stub
for (int x = 0; x < 10; x++) {
synchronized (this) {
if (this.ticket > 0) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " 卖票,ticket = " + this.ticket--);
}
}
}
}
}
public class TestDemo4 {
public static void main(String[] args) {
MyThread1 mt = new MyThread1();
new Thread(mt, "A").start();
new Thread(mt, "B").start();
new Thread(mt, "C").start();
new Thread(mt, "D").start();
new Thread(mt, "E").start();
}
}
解决方法2——同步方法
package cn.itcast.thread3_1;
//争夺问题解决——同步方法
class MyThread2 implements Runnable {
private int ticket = 6;
@Override
public void run() {
// TODO Auto-generated method stub
for (int x = 0; x < 10; x++) {
this.sale();
}
}
public synchronized void sale() {
if (this.ticket > 0) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " 卖票,ticket = " + this.ticket--);
}
}
}
public class TestDemo5 {
public static void main(String[] args) {
MyThread1 mt = new MyThread1();
new Thread(mt, "A").start();
new Thread(mt, "B").start();
new Thread(mt, "C").start();
new Thread(mt, "D").start();
new Thread(mt, "E").start();
}
}
根据执行时间发现:加入同步之后明显比不加入同步慢许多,所以同步的代码性能会很低,但是数据的安全性会高。
死锁
死锁具体可参考https://www.jianshu.com/p/d254b138de03
同步是一个线程等待另一个线程执行完才会继续执行的一种操作方式,如果在一个操作之中线程都是在互相等着的话,那么就会出现死锁问题。
package cn.itcast.deadlock;
class YuShi {
public synchronized void say(FuXie f) {
System.out.println("玉史:给我 30 亿欧圆,放了你儿子。");
f.get();
}
public synchronized void get() {
System.out.println("玉史终于得到了赎金,放了儿子,为了下次继续绑架。");
}
}
class FuXie {
public synchronized void say(YuShi y) {
System.out.println("付谢:放了我儿子,我给你 30 亿欧圆,不见人不给钱。");
y.get();
}
public synchronized void get() {
System.out.println("付谢救回了自己的儿子,于是开始哭那 30 亿。");
}
}
public class DeadLock implements Runnable {
static YuShi ys = new YuShi();
static FuXie fs = new FuXie();
@Override
public void run() {
// TODO Auto-generated method stub
fs.say(ys);
}
public static void main(String[] args) {
DeadLock dl = new DeadLock();
new Thread(dl).start();
ys.say(fs);
}
}