实际上所谓的同步指的就是多个线程访问同一资源时所要考虑到的情况。
观察非同步情况下的操作:
class MyThread implements Runnable{
private int tickets = 5 ;//总共的票数
@Override
public void run() {
for(int x = 0 ; x < 20 ; x ++){
if(this.tickets > 0)
System.out.println(Thread.currentThread().getName() + ":卖票" + this.tickets --);
}
}
}
public class Demo {
public static void main(String[] args) {
MyThread m1 = new MyThread() ;
Thread t1 = new Thread(m1 , "黄牛1") ;
Thread t2 = new Thread(m1 , "黄牛2") ;
Thread t3 = new Thread(m1 , "黄牛3") ;
Thread t4 = new Thread(m1 , "黄牛4") ;
t1.start();
t2.start();
t3.start();
t4.start();
}
}
执行结果:
黄牛1:卖票5
黄牛3:卖票3
黄牛3:卖票1
黄牛2:卖票4
黄牛1:卖票2
现在四个对象一起卖票,此时没有出现问题是因为是在一个JVM进程下运行的,并且没有受到任何的影响,
如果想要观察到问题,可以增加休眠时间。
观察问题:
class MyThread implements Runnable{
private int tickets = 5 ;//总共的票数
@Override
public void run() {
for(int x = 0 ; x < 20 ; x ++){
if(this.tickets > 0)
try {
Thread.sleep(100);//休眠100毫秒
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + ":卖票" + this.tickets --);
}
}
}
public class Demo {
public static void main(String[] args) {
MyThread m1 = new MyThread() ;
Thread t1 = new Thread(m1 , "黄牛1") ;
Thread t2 = new Thread(m1 , "黄牛2") ;
Thread t3 = new Thread(m1 , "黄牛3") ;
Thread t4 = new Thread(m1 , "黄牛4") ;
t1.start();
t2.start();
t3.start();
t4.start();
}
}
运行结果:
黄牛4:卖票3
黄牛2:卖票4
黄牛1:卖票2
黄牛3:卖票5
黄牛4:卖票1
黄牛1:卖票0
黄牛2:卖票-3
黄牛2:卖票-5
黄牛4:卖票-1
此时执行之后发现操作的结果出现了负数,那么这就叫不同步的情况,到底是如何造成不同步的呢?
整个的卖票的步骤分为两步:
|—第一步:判断是否有剩余的票数;
|—第二步:卖票,改变剩余的票数。
分析以上代码,假如现在还剩余1张票,第一个线程发现还有剩余的票数,进去卖票,理论上来说,现在应该马
上修改剩余的票数,但是因为增加了休眠,那么就不能马上改变剩余的票数,所以外面看来此时的票数还有一张,
第二个线程对象一看,还有一张票,也进来准备买票,但是因为休眠,也不能及时改变剩余的票数,所以就出现了负
数。
实现同步:
在java里面想要实现线程的同步,就要使用synchronized(java最长的关键字)关键字。可以通过两种方式实现:
|—一种是同步代码块:synchronized(对象名,一般为this){要执行的代码};
|—一种是同步方法。public synchronized 返回值 方法名(){方法体}。
在java里面有四种代码块:普通代码块、构造块、静态块、同步块。
synchornized关键字的原理是,当一个线程对象进入到同步代码块或者同步方法里面时,就给同步代码块或同步方法
加上一把锁,后面的线程对象还像进入,就必须等到当前线程对象执行完这个代码块或者方法才能够进入。
观察同步块:
class MyThread implements Runnable {
private int tickets = 50;// 总共的票数
@Override
public void run() {
for (int x = 0; x < 200; x++) {
synchronized (this) {
if (this.tickets > 0) {
try {
Thread.sleep(100);// 休眠100毫秒
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + ":卖票"
+ this.tickets--);
}
}
}
}
}
public class Demo {
public static void main(String[] args) {
MyThread m1 = new MyThread();
Thread t1 = new Thread(m1, "黄牛1");
Thread t2 = new Thread(m1, "黄牛2");
Thread t3 = new Thread(m1, "黄牛3");
Thread t4 = new Thread(m1, "黄牛4");
t1.start();
t2.start();
t3.start();
t4.start();
}
}
运行结果:
黄牛1:卖票49
黄牛1:卖票48
黄牛1:卖票47
黄牛3:卖票46
黄牛1:卖票45
同步方法:
class MyThread implements Runnable {
private int tickets = 50;// 总共的票数
@Override
public void run() {
for (int x = 0; x < 200; x++) {
this.sal() ;
}
}
public synchronized void sal() {
if (this.tickets > 0) {
try {
Thread.sleep(100);// 休眠100毫秒
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + ":卖票"
+ this.tickets--);
}
}
}
public class Demo {
public static void main(String[] args) {
MyThread m1 = new MyThread();
Thread t1 = new Thread(m1, "黄牛1");
Thread t2 = new Thread(m1, "黄牛2");
Thread t3 = new Thread(m1, "黄牛3");
Thread t4 = new Thread(m1, "黄牛4");
t1.start();
t2.start();
t3.start();
t4.start();
}
}
运行结果:
黄牛1:卖票49
黄牛2:卖票48
黄牛1:卖票47
黄牛3:卖票46
黄牛3:卖票45
同步操作与异步操作相比,异步操作的执行速度要高于同步操作,但是同步操作时数据的安全性较高,属于安全的线程操作。