直接看代码:
背景:模仿多线程抢票
package com.example.institution01.instiServic;import java.util.ArrayList;
import java.util.List;public class ThreadTest {/**总票数**/static int a = 1;public static void method() {/**抢票t1**/Thread t1 = new Thread(new Runnable() {@Overridepublic void run() {String sell = sell();System.out.println(sell);}});/**抢票t2**/Thread t2 = new Thread(new Runnable() {@Overridepublic void run() {String sell = sell();System.out.println(sell);}});t1.start();t2.start();}public static void main(String[] args) {for (int i = 0; i < 10; i++) {method();}}/**出票系统**/public static String sell(){a = a - 1;if (a >= 0) return Thread.currentThread().getName()+"抢到票===剩余=" + a;elsereturn Thread.currentThread().getName()+"无票";}}
控制台结果:
Thread-0抢到票===剩余=-1
Thread-4无票
Thread-2无票
Thread-3无票
Thread-1无票
Thread-6无票
Thread-5无票
Thread-7无票
Thread-8无票
Thread-10无票
Thread-11无票
Thread-9无票
Thread-14无票
Thread-15无票
Thread-13无票
Thread-12无票
Thread-17无票
Thread-16无票
Thread-18无票
Thread-19无票
可以看到有一个Thread-0抢到票===剩余=-1的线程:
A线程:a=1 a-1 a=0;
B线程:a=0 a-1 a=-1;
B线程在A线程没有结束的时候改变了a的值,所以A线程输出剩余-1张票;
1.加 synchronized 关键字后:
package com.example.institution01.instiServic;import java.util.ArrayList;
import java.util.List;public class ThreadTest {/**总票数**/static int a = 1;public static void method() {/**抢票t1**/Thread t1 = new Thread(new Runnable() {@Overridepublic void run() {String sell = sell();System.out.println(sell);}});/**抢票t2**/Thread t2 = new Thread(new Runnable() {@Overridepublic void run() {String sell = sell();System.out.println(sell);}});t1.start();t2.start();}public static void main(String[] args) {for (int i = 0; i < 10; i++) {method();}}/**出票系统**/public synchronized static String sell(){a = a - 1;if (a >= 0) return Thread.currentThread().getName()+"抢到票===剩余=" + a;elsereturn Thread.currentThread().getName()+"无票";}}
控制台:
Thread-1无票
Thread-5无票
Thread-4无票
Thread-3无票
Thread-2无票
Thread-6无票
Thread-0抢到票===剩余=0
Thread-7无票
Thread-10无票
Thread-9无票
Thread-11无票
Thread-8无票
Thread-12无票
Thread-13无票
Thread-15无票
Thread-14无票
Thread-17无票
Thread-16无票
Thread-18无票
Thread-19无票
完美解决这个问题!
2.用LOCK锁解决:
package com.example.institution01.instiServic;import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;public class ThreadTest {private static Lock lock = new ReentrantLock();/**总票数**/static int a = 1;public static void method() {/**抢票t1**/Thread t1 = new Thread(new Runnable() {@Overridepublic void run() {try {lock.lock();String sell = sell();System.out.println(sell);} catch (Exception e) {e.printStackTrace();}finally {lock.unlock();}}});/**抢票t2**/Thread t2 = new Thread(new Runnable() {@Overridepublic void run() {try {lock.lock();String sell = sell();System.out.println(sell);} catch (Exception e) {e.printStackTrace();}finally {lock.unlock();}}});t1.start();t2.start();}public static void main(String[] args) {for (int i = 0; i < 10; i++) {method();}}/**出票系统**/public static String sell(){a = a - 1;if (a >= 0) return Thread.currentThread().getName()+"抢到票===剩余=" + a;elsereturn Thread.currentThread().getName()+"无票";}}
synchronized和Lock 的异同:
同:两者都是解决线程安全问题
异:
- synchronized关键字定义的不管是同步代码块还是同步方法在执行完成之后都是自动的释放同步监视器(这是一种隐式锁,出了作用域隐式锁将自动的释放)
- lock需要手动的上锁启动同步和手动解锁结束同步;
- Lock只有代码块锁,没办法对某一方法上锁
- Lock锁比较灵活,JVM只需要花费较小的代价来实现线程的调度,性能更好并且有更好的扩展性;