1、多窗口卖票
- 三个窗口卖100张票 ```json public class Main { public static void main(String[] args) {
MyRunnable myRunnable = new MyRunnable();new Thread(myRunnable).start();new Thread(myRunnable).start();new Thread(myRunnable).start();}static class MyRunnable implements Runnable {private int ticket = 100;@Overridepublic void run() {while (true) {if (ticket > 0) {try {Thread.sleep(10);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName() + "正在卖第" + ticket + "张票");ticket--;} else {System.out.println(Thread.currentThread().getName() + "卖完了");break;}}}}
}
- CPU在不同线程间切换,共享数据不是安全的;会出现卖了同一张票、不存在的票问题- 最终原因:CPU在三个线程间轮流切换执行(例如CPU先执行线程1,执行到某一行代码,CPU又去执行了线程2,这期间的共享数据就是不准确的)<br />```jsonThread-0正在卖第3张票Thread-2正在卖第2张票Thread-1正在卖第2张票Thread-1卖完了Thread-2正在卖第0张票Thread-2卖完了Thread-0正在卖第-1张票Thread-0卖完了
这些线程安全问题,可以通过添加synchronized同步来解决
2、同步代码块
测试类 ```json public class Main { public static void main(String[] args) {
MyRunnable runnable = new MyRunnable();new Thread(runnable, "SyncThread1").start();new Thread(runnable, "SyncThread2").start();/**SyncThread1:0SyncThread1:1SyncThread1:2SyncThread1:3SyncThread1:4SyncThread2:5SyncThread2:6SyncThread2:7SyncThread2:8SyncThread2:9*/// 使用的不同MyRunnable对象,所以不能同步MyRunnable r1 = new MyRunnable();MyRunnable r2 = new MyRunnable();new Thread(r1, "SyncThread1").start();new Thread(r2, "SyncThread2").start();/**SyncThread1:1SyncThread2:0SyncThread2:3SyncThread1:2SyncThread2:4SyncThread1:5SyncThread1:6SyncThread2:6SyncThread2:7SyncThread1:7*/}static class MyRunnable implements Runnable {// 此处是静态变量,归类所有,所有的实例访问是同一个public static int count;public MyRunnable() {count = 0;}public void run() {// 此处锁的是MyRunnable对象synchronized (this) {for (int i = 0; i < 5; i++) {try {System.out.println(Thread.currentThread().getName() + ":" + (count++));Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}}}}}
}
- 结论- synchronized (this):锁的是当前MyRunnable对象- 例子一结论:一个线程访问一个对象中的synchronized(this)同步代码块时,其他试图访问该对象的线程将被阻塞<br />- 例子二结论:synchronized锁定的是对象,这时会有两把锁分别锁定r1对象和r2对象,而这两把锁是互不干扰的,不形成互斥,所以两个线程可以同时执行<br /><a name="1cAMQ"></a>### 3、同步非静态方法- synchronized修饰一个非静态方法,锁的是Runnable的一个对象<br />- synchronized修饰方法和修饰一个代码块类似,只是作用范围不一样,修饰代码块是大括号括起来的范围,而修饰方法范围是整个函数<br />```jsonpublic class Main {public static void main(String[] args) {MyRunnable runnable = new MyRunnable();new Thread(runnable, "SyncThread1").start();new Thread(runnable, "SyncThread2").start();/**SyncThread1:0SyncThread1:1SyncThread1:2SyncThread1:3SyncThread1:4SyncThread2:5SyncThread2:6SyncThread2:7SyncThread2:8SyncThread2:9*/// 使用的不同MyRunnable对象,所以不能同步MyRunnable r1 = new MyRunnable();MyRunnable r2 = new MyRunnable();new Thread(r1, "SyncThread1").start();new Thread(r2, "SyncThread2").start();/**SyncThread2:0SyncThread1:1SyncThread1:2SyncThread2:2SyncThread2:3SyncThread1:3SyncThread1:4SyncThread2:5SyncThread1:6SyncThread2:6*/}static class MyRunnable implements Runnable {public static int count;public MyRunnable() {count = 0;}public synchronized void run() {// 此处锁的是MyRunnable对象for (int i = 0; i < 5; i++) {try {System.out.println(Thread.currentThread().getName() + ":" + (count++));Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}}}}}
结论
synchronized修饰一个静态方法,锁的是Runnable类
thread1和thread2是SyncThread的两个对象,但在thread1和thread2并发执行时却保持了线程同步。这是因为run中调用了静态方法method,而静态方法是属于类的,所以thread1和thread2相当于用了同一把锁
public class Main {public static void main(String[] args) {MyRunnable runnable = new MyRunnable();new Thread(runnable, "SyncThread1").start();new Thread(runnable, "SyncThread2").start();/**SyncThread1:0SyncThread1:1SyncThread1:2SyncThread1:3SyncThread1:4SyncThread2:5SyncThread2:6SyncThread2:7SyncThread2:8SyncThread2:9*/MyRunnable r1 = new MyRunnable();MyRunnable r2 = new MyRunnable();new Thread(r1, "SyncThread1").start();new Thread(r2, "SyncThread2").start();/**SyncThread1:0SyncThread1:1SyncThread1:2SyncThread1:3SyncThread1:4SyncThread2:5SyncThread2:6SyncThread2:7SyncThread2:8SyncThread2:9*/}static class MyRunnable implements Runnable {public static int count;public MyRunnable() {count = 0;}public void run() {method();}// 此处锁的是MyRunnable类,包含所有类实例private synchronized static void method() {for (int i = 0; i < 5; i++) {try {System.out.println(Thread.currentThread().getName() + ":" + (count++));Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}}}}}
结论
- r1和r2是MyRunnable的两个对象,但在thread1和thread2并发执行时却保持了线程同步。这是因为run中调用了静态方法method,而静态方法是属于类的,所以syncThread1和syncThread2相当于用了同一把锁
- r1和r2是MyRunnable的两个对象,但在thread1和thread2并发执行时却保持了线程同步。这是因为run中调用了静态方法method,而静态方法是属于类的,所以syncThread1和syncThread2相当于用了同一把锁
5、同步类
- synchronized作用于一个类T时,是给这个类T加锁,T的所有对象用的是同一把锁
```json public class Main { public static void main(String[] args) {
// MyRunnable runnable = new MyRunnable(); // new Thread(runnable, “SyncThread1”).start(); // new Thread(runnable, “SyncThread2”).start();
/**SyncThread1:0SyncThread1:1SyncThread1:2SyncThread1:3SyncThread1:4SyncThread2:5SyncThread2:6SyncThread2:7SyncThread2:8SyncThread2:9*/MyRunnable r1 = new MyRunnable();MyRunnable r2 = new MyRunnable();new Thread(r1, "SyncThread1").start();new Thread(r2, "SyncThread2").start();/**SyncThread1:0SyncThread1:1SyncThread1:2SyncThread1:3SyncThread1:4SyncThread2:5SyncThread2:6SyncThread2:7SyncThread2:8SyncThread2:9*/}static class MyRunnable implements Runnable {public static int count;public MyRunnable() {count = 0;}public void run() {synchronized(MyRunnable.class){for (int i = 0; i < 5; i++) {try {System.out.println(Thread.currentThread().getName() + ":" + (count++));Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}}}}}
}
<a name="c3d3b779"></a>### 5、synchronized使用限制- synchronized关键字不能继承<br />- 如果在父类中的某个方法使用了synchronized关键字,而在子类中覆盖了这个方法,在子类中的这个方法默认情况下并不是同步的,而必须显式地在子类的这个方法中加上synchronized关键字才可以<br />```jsonclass Parent {public synchronized void method() { }}class Child extends Parent {public synchronized void method() { }}
当然,还可以在子类方法中调用父类中相应的方法,这样虽然子类中的方法不是同步的,但子类调用了父类的同步方法,因此,子类的方法也就相当于同步了
class Parent {public synchronized void method() { }}class Child extends Parent {public void method() { super.method(); }}
在定义接口方法时不能使用synchronized关键字
interface MyClick{// 此处不允许使用修饰符synchronizedsynchronized void add();}
构造方法不能使用synchronized关键字,但可以使用synchronized代码块来进行同步
