问题:线程安全问题的主要诱因是什么?
---》
1、存在共享数据(也称临界资源)
2、存在多条线程共同操作这些共享数据
解决问题的根本方法:
同一时刻有且只有一个线程在操作共享数据,其他线程必须等到该线程处理完数据后再对共享数据进行操作(串行)
互斥锁的特性
1、互斥性:在同一时间只允许一个线程持有某个对象锁,通过这种特性来实现多线程的协调机制,这样在同一实际只有一个线程对学院同步的代码块(复合操作)进行访问。互斥性也成为操作的原子性。
2、可见性:必须确保在锁被释放之前,对共享变量所做的修改,对于随后获得该锁的另一个线程是可见的(则在获得锁时应获得最新共享变量的值),否则另一个线程可能是在本地缓存的某个副本上继续操作,从而引起不一致
注意的是:synchronized锁的不是代码,锁的都是对象
根据获取的锁的分类:获取对象锁和获取类锁
获取对象锁的两种用法:
1、同步代码块(synchronized(this),synchronized(类实例对象)),锁时小括号中的实例对象
2、同步非静态方法(synchronized method), 锁时当前对象的实例对象
获取类锁的两种方法:
1、同步代码块(synchronized(类.class)),锁是小括号()中的类对象(Class对象)
2、同步静态方法(synchronized static method) ,锁是当前对象的类对象(Class对象)
public class SyncThread implements Runnable { @Override public void run() { String threadName = Thread.currentThread().getName(); if (threadName.startsWith("A")) { async(); } else if (threadName.startsWith("B")) { syncObjectBlock1(); } else if (threadName.startsWith("C")) { syncObjectMethod1(); } else if (threadName.startsWith("D")) { syncClassBlock1(); } else if (threadName.startsWith("E")) { syncClassMethod1(); }else if(threadName.startsWith("F")){ syncObjectMethod2(); } } /** * 异步方法 */ private void async() { try { System.out.println(Thread.currentThread().getName() + "_Async_Start: " + new SimpleDateFormat("HH:mm:ss").format(new Date())); Thread.sleep(1000); System.out.println(Thread.currentThread().getName() + "_Async_End: " + new SimpleDateFormat("HH:mm:ss").format(new Date())); } catch (InterruptedException e) { e.printStackTrace(); } } /** * 方法中有 synchronized(this|object) {} 同步代码块 */ private void syncObjectBlock1() { System.out.println(Thread.currentThread().getName() + "_SyncObjectBlock1: " + new SimpleDateFormat("HH:mm:ss").format(new Date())); synchronized (this) { try { System.out.println(Thread.currentThread().getName() + "_SyncObjectBlock1_Start: " + new SimpleDateFormat("HH:mm:ss").format(new Date())); Thread.sleep(1000); System.out.println(Thread.currentThread().getName() + "_SyncObjectBlock1_End: " + new SimpleDateFormat("HH:mm:ss").format(new Date())); } catch (InterruptedException e) { e.printStackTrace(); } } } /** * synchronized 修饰非静态方法 */ private synchronized void syncObjectMethod1() { System.out.println(Thread.currentThread().getName() + "_SyncObjectMethod1: " + new SimpleDateFormat("HH:mm:ss").format(new Date())); try { System.out.println(Thread.currentThread().getName() + "_SyncObjectMethod1_Start: " + new SimpleDateFormat("HH:mm:ss").format(new Date())); Thread.sleep(1000); System.out.println(Thread.currentThread().getName() + "_SyncObjectMethod1_End: " + new SimpleDateFormat("HH:mm:ss").format(new Date())); } catch (InterruptedException e) { e.printStackTrace(); } } private synchronized void syncObjectMethod2() { // 注意这里的syncObjectMethod2方法和syncObjectMethod1方法锁的对象实例都是同一个!!! System.out.println(Thread.currentThread().getName() + "_SyncObjectMethod2: " + new SimpleDateFormat("HH:mm:ss").format(new Date())); try { System.out.println(Thread.currentThread().getName() + "_SyncObjectMethod2_Start: " + new SimpleDateFormat("HH:mm:ss").format(new Date())); Thread.sleep(1000); System.out.println(Thread.currentThread().getName() + "_SyncObjectMethod2_End: " + new SimpleDateFormat("HH:mm:ss").format(new Date())); } catch (InterruptedException e) { e.printStackTrace(); } } private void syncClassBlock1() { System.out.println(Thread.currentThread().getName() + "_SyncClassBlock1: " + new SimpleDateFormat("HH:mm:ss").format(new Date())); synchronized (SyncThread.class) { try { System.out.println(Thread.currentThread().getName() + "_SyncClassBlock1_Start: " + new SimpleDateFormat("HH:mm:ss").format(new Date())); Thread.sleep(1000); System.out.println(Thread.currentThread().getName() + "_SyncClassBlock1_End: " + new SimpleDateFormat("HH:mm:ss").format(new Date())); } catch (InterruptedException e) { e.printStackTrace(); } } } private synchronized static void syncClassMethod1() { System.out.println(Thread.currentThread().getName() + "_SyncClassMethod1: " + new SimpleDateFormat("HH:mm:ss").format(new Date())); try { System.out.println(Thread.currentThread().getName() + "_SyncClassMethod1_Start: " + new SimpleDateFormat("HH:mm:ss").format(new Date())); Thread.sleep(1000); System.out.println(Thread.currentThread().getName() + "_SyncClassMethod1_End: " + new SimpleDateFormat("HH:mm:ss").format(new Date())); } catch (InterruptedException e) { e.printStackTrace(); } } }
public class SyncDemo { public static void main(String... args) { SyncThread syncThread = new SyncThread(); Thread A_thread1 = new Thread(syncThread, "A_thread1"); Thread A_thread2 = new Thread(syncThread, "A_thread2"); Thread B_thread1 = new Thread(syncThread, "B_thread1"); Thread B_thread2 = new Thread(syncThread, "B_thread2"); Thread C_thread1 = new Thread(syncThread, "C_thread1"); Thread C_thread2 = new Thread(syncThread, "C_thread2"); Thread D_thread1 = new Thread(syncThread, "D_thread1"); Thread D_thread2 = new Thread(syncThread, "D_thread2"); Thread E_thread1 = new Thread(syncThread, "E_thread1"); Thread E_thread2 = new Thread(syncThread, "E_thread2"); Thread F_thread1 = new Thread(syncThread, "F_thread1"); Thread F_thread2 = new Thread(syncThread, "F_thread2"); A_thread1.start(); A_thread2.start(); B_thread1.start(); B_thread2.start(); C_thread1.start(); C_thread2.start(); D_thread1.start(); D_thread2.start(); E_thread1.start(); E_thread2.start(); F_thread1.start(); F_thread2.start(); } }
总结:
1、有线程访问对象的同步代码块时,另外的线程可以访问该对象的非同步代码块
2、若锁住的是同一个对象,一个线程在访问对象的同步代码块时,另一个访问对象的同步代码块的线程会被阻塞
3、若锁住的是同一个对象,一个线程在访问对象的同步方法时,另一个访问对象同步方法的线程会被阻塞
4、若锁住的是同一个对象,一个线程在访问对象的同步代码块时,另一个访问对象同步方法的线程会被阻塞,反之亦然
5、同一个类的不同对象的对象锁互不干扰
6、类锁由于也是一种特殊的对象锁,因此表现和上述1,2,3,4一致,而由于一个类只有一把对象锁,所以同一个类的不同对象使用类锁将会是同步的
7、类锁和对象锁互补干扰
原文:https://www.cnblogs.com/vingLiu/p/10665696.html