![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vvOqmEjv-1629284758491)(C:\Users\王东梁\AppData\Roaming\Typora\typora-user-images\image-20210818161532498.png)]](https://img-blog.csdnimg.cn/273f6bff9f7841dbabc0afaf51c9ce7b.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQ1NzgzNjYw,size_16,color_FFFFFF,t_70)
方式一(同步代码块)
synchronized(同步监视器){
 //需要被同步的代码
 }
说明:1.操作共享数据的代码,即为需要被同步的代码。 -->不能包含代码多了,也不能包含代码少了。
 2.共享数据:多个线程共同操作的变量。比如:ticket就是共享数据。
 3.同步监视器,俗称:锁。任何一个类的对象,都可以充当锁。
 要求:多个线程必须要共用同一把锁。
 补充:在实现Runnable接口创建多线程的方式中,我们可以考虑使用this充当同步监视器。
package com.atguigu.java;class Window1 implements Runnable{private int ticket = 100;
//    Object obj = new Object();
//    Dog dog = new Dog();@Overridepublic void run() {
//        Object obj = new Object();while(true){synchronized (this){//此时的this:唯一的Window1的对象   //方式二:synchronized (dog) {if (ticket > 0) {try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName() + ":卖票,票号为:" + ticket);ticket--;} else {break;}}}}
}public class WindowTest1 {public static void main(String[] args) {Window1 w = new Window1();Thread t1 = new Thread(w);Thread t2 = new Thread(w);Thread t3 = new Thread(w);t1.setName("窗口1");t2.setName("窗口2");t3.setName("窗口3");t1.start();t2.start();t3.start();}}class Dog{}
package com.atguigu.java;class Window2 extends Thread{private static int ticket = 100;private static Object obj = new Object();@Overridepublic void run() {while(true){//正确的
//            synchronized (obj){synchronized (Window2.class){//Class clazz = Window2.class,Window2.class只会加载一次//错误的方式:this代表着t1,t2,t3三个对象
//              synchronized (this){if(ticket > 0){try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(getName() + ":卖票,票号为:" + ticket);ticket--;}else{break;}}}}
}public class WindowTest2 {public static void main(String[] args) {Window2 t1 = new Window2();Window2 t2 = new Window2();Window2 t3 = new Window2();t1.setName("窗口1");t2.setName("窗口2");t3.setName("窗口3");t1.start();t2.start();t3.start();}
}方式二(同步方法)
如果操作共享数据的代码完整的声明在一个方法中,我们不妨将此方法声明同步的。
关于同步方法的总结:
- 同步方法仍然涉及到同步监视器,只是不需要我们显式的声明。
- 非静态的同步方法,同步监视器是:this
 静态的同步方法,同步监视器是:当前类本身
package com.atguigu.java;class Window3 implements Runnable {private int ticket = 100;@Overridepublic void run() {while (true) {show();}}private synchronized void show(){//同步监视器:this//synchronized (this){if (ticket > 0) {try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName() + ":卖票,票号为:" + ticket);ticket--;}//}}
}public class WindowTest3 {public static void main(String[] args) {Window3 w = new Window3();Thread t1 = new Thread(w);Thread t2 = new Thread(w);Thread t3 = new Thread(w);t1.setName("窗口1");t2.setName("窗口2");t3.setName("窗口3");t1.start();t2.start();t3.start();}}package com.atguigu.java;/*** 使用同步方法处理继承Thread类的方式中的线程安全问题** @author shkstart* @create 2019-02-15 上午 11:43*/
class Window4 extends Thread {private static int ticket = 100;@Overridepublic void run() {while (true) {show();}}private static synchronized void show(){//同步监视器:Window4.class//private synchronized void show(){ //同步监视器:t1,t2,t3。此种解决方式是错误的if (ticket > 0) {try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName() + ":卖票,票号为:" + ticket);ticket--;}}
}public class WindowTest4 {public static void main(String[] args) {Window4 t1 = new Window4();Window4 t2 = new Window4();Window4 t3 = new Window4();t1.setName("窗口1");t2.setName("窗口2");t3.setName("窗口3");t1.start();t2.start();t3.start();}
}
方式三(Lock锁)—JDK5.0新增
1. 面试题:synchronized 与 Lock的异同?相同:二者都可以解决线程安全问题不同:synchronized机制在执行完相应的同步代码以后,自动的释放同步监视器Lock需要手动的启动同步(lock()),同时结束同步也需要手动的实现(unlock())2.优先使用顺序:
Lock 
同步代码块(已经进入了方法体,分配了相应资源) 
同步方法(在方法体之外)
package com.atguigu.java1;import java.util.concurrent.locks.ReentrantLock;/*** 解决线程安全问题的方式三:Lock锁  --- JDK5.0新增** 1. 面试题:synchronized 与 Lock的异同?*   相同:二者都可以解决线程安全问题*   不同:synchronized机制在执行完相应的同步代码以后,自动的释放同步监视器*        Lock需要手动的启动同步(lock()),同时结束同步也需要手动的实现(unlock())** 2.优先使用顺序:* Lock  同步代码块(已经进入了方法体,分配了相应资源)  同步方法(在方法体之外)***  面试题:如何解决线程安全问题?有几种方式* @author shkstart* @create 2019-02-15 下午 3:38*/
class Window implements Runnable{private int ticket = 100;//1.实例化ReentrantLockprivate ReentrantLock lock = new ReentrantLock();@Overridepublic void run() {while(true){try{//2.调用锁定方法lock()lock.lock();if(ticket > 0){try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName() + ":售票,票号为:" + ticket);ticket--;}else{break;}}finally {//3.调用解锁方法:unlock()lock.unlock();}}}
}public class LockTest {public static void main(String[] args) {Window w = new Window();Thread t1 = new Thread(w);Thread t2 = new Thread(w);Thread t3 = new Thread(w);t1.setName("窗口1");t2.setName("窗口2");t3.setName("窗口3");t1.start();t2.start();t3.start();}
}