线程安全的解释
思路:三个窗口卖票,我们需要设计3条线程表示都实现卖票的操作。并且因为我们的票数是一定的,所以我们需要将票数共享
package com.cook.test;
//实现卖票操作(实现Runnable接口实现)
public class MyThread3 implements Runnable {
//定义票数并共享
public static int ticket = 0;
@Override
public void run() {
while (true){
if(ticket<100){
ticket++;
try {
Thread.sleep(100);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println(Thread.currentThread().getName()+"已经卖了"+ticket+"张票"+"还剩下"+(100-ticket)+"张票");
}else {
break;
}
}
}
}
package com.cook.test;
public class MyThreadTest3 {
public static void main(String[] args) {
MyThread3 t = new MyThread3();
Thread t1 = new Thread(t,"窗口1");
Thread t2 = new Thread(t,"窗口2");
Thread t3 = new Thread(t,"窗口3");
t1.start();
t2.start();
t3.start();
}
}
但是以上程序会导致以下问题:
对于票号重复的解释
当我们的线程有自增后(tick=1)CPU被线程2抢走了,线程2自增后CPU并线程3抢走了(tick=3)然后打印的都是tick=3了
出现超出范围的票的解释
当我们的ticket是99的时候,线程1抢到cpu执行了ticket++(ticket100)的时候cpu被线程2签到也是执行到ticket++(ticket101)的时候cpu被线程3抢走然后执行ticket++(ticket==102)然后3个线程再次抢夺,打印出来的票号都是102了
以上2种原因的本质是:线程执行时,有随机性
解决方法---同步代码块
我们的某一个线程在执行的时候,可能不知道什么时候CPU的执行权就被其他线程抢走了。我们可以设置同步代码块,我们的一个线程在执行同步代码块时,这个同步代码块的锁就会关闭,只有当该线程执行完了锁才会打开,别的线程才能进入执行
package com.cook.test;
//实现卖票操作(实现Runnable接口实现)
public class MyThread3 implements Runnable {
//定义票数并共享
public static int ticket = 0;
@Override
public void run() {
while (true){
synchronized (MyThread3.class){
if(ticket<100){
ticket++;
try {
Thread.sleep(100);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println(Thread.currentThread().getName()+"已经卖了"+ticket+"张票"+"还剩下"+(100-ticket)+"张票");
}else {
break;
}
}
}
}
}
package com.cook.test;
public class MyThreadTest3 {
public static void main(String[] args) {
MyThread3 t = new MyThread3();
Thread t1 = new Thread(t,"窗口1");
Thread t2 = new Thread(t,"窗口2");
Thread t3 = new Thread(t,"窗口3");
t1.start();
t2.start();
t3.start();
}
}
为了确保我们的锁的对象唯一,我们可以时候当前类的字节码文件对象MyThread.class来表示,因为类的字节码文件是唯一的,所以其字节码文件的对象也是唯一的,锁对象的唯一使得我们的每一个线程面对的都是同一把锁,如果锁对象不唯一,线程面对的锁不一样,这其实就和没有写锁一样(锁对象用别的也可以,唯一就行)