线程安全
一、线程安全
线程安全概念:
当多个线程访问某一个类(对象或方法)时,这个类(对象或方法)都能表现出正确的行为,那么这个类(对象或方法)就是线程安全的。
二、同一个对象的锁
1.对同一个对象的对象锁 synchronized
synchronized 锁是对象级别的
package com.study.current.thread.day01; /** * 线程安全概念:当多个线程访问某一个类(对象或方法)时,这个类(对象或方法)都能表现出正确的行为,那么这个类(对象或方法)就是线程安全的。 */ public class MyThread extends Thread { private int count = 5 ; /** * synchronized 的作用: 加锁,防止并发方法 * * synchronized:可以在任意对象及方法上加锁,而加锁的这段代码称为“互斥区”或“临界区”。 */ public synchronized void run(){ /** * 线程执行的顺序不是代码的顺序,而是CPU分配的顺序决定的 当过个线程访问myThread的run方法时,以排队的方式进行处理 排队是按照CPU分配的先后顺序而定的 一个线程想要执行synchronized修饰的方法里的代码,首先是尝试获得锁,如果拿到锁,执行 synchronized代码体内容 拿不到锁,这个线程就会不断地尝试获得这把锁,直到拿到为止 而且是多个线程同时去竞争这把锁 */ this.count -- ; System.out.println(Thread.currentThread().getName()+" count:"+count); } public static void main(String[] args) { MyThread thread = new MyThread(); Thread t1 = new Thread(thread,"t1"); Thread t2 = new Thread(thread,"t2"); Thread t3 = new Thread(thread,"t3"); Thread t4 = new Thread(thread,"t4"); Thread t5 = new Thread(thread,"t5"); t1.start(); t2.start(); t3.start(); t4.start(); t5.start(); } }
2.例子分析
不加synchronized 运行结果:
t2 count:3
t3 count:2
t4 count:1
t1 count:3
t5 count:0
与预期的结果:count值按照顺序输出,相差很大
--------------------------------------------
添加synchronized 运行结果:
t1 count:4
t3 count:3
t2 count:2
t4 count:1
t5 count:0
count 值按照顺序输出,但t1 ~ t5 不是按照顺序,即线程执行的顺序不是代码的位置顺序
而是谁先抢到CPU的执行权,那么谁就先执行
二、多个对象的对象锁
1.对多个对象的对象锁 synchronized
多个线程多个锁:多个线程,每个线程都可以拿到自己指定的锁,分别获得锁之后,执行synchronized的方法体的内容
package com.study.current.thread.day01; /** * 关键字synchronized 取得的锁都是对象锁,而不是把一段代码或方法当做锁,所以实例代码中的那个线程先执行 synchronized关键字的方法,那个线程就持有该方法所属对象的锁Lock,两个对象,线程获得的就是两个不同的锁 他们互不影响 有一种情况则是相同的锁,即在静态方法上加 synchronized 关键字,表示锁定.class类,类一级别的锁,独占.class类 * */ public class MultiThread extends Thread { public int num = 0 ; // 初始化 public synchronized void printNum(String tag) throws InterruptedException{ if("a".equals(tag)){ num = 100 ; System.out.println("taga num = 100 "); Thread.sleep(1000); }else{ num = 200 ; System.out.println("tagb num = 200 "); } System.out.println("tag : "+tag + " num : "+num); } public static void main(String[] args) { final MultiThread thread1 = new MultiThread(); final MultiThread thread2 = new MultiThread(); Thread t1 = new Thread(new Runnable() { public void run() { try { thread1.printNum("a"); } catch (InterruptedException e) { e.printStackTrace(); } } }); Thread t2 = new Thread(new Runnable() { public void run() { try { thread2.printNum("b"); } catch (InterruptedException e) { e.printStackTrace(); } } }); t1.start(); t2.start(); } }
2.例子分析
运行结果:
taga num = 100
tagb num = 200
tag : b num : 200
tag : a num : 100
与预期 taga num = 100 后执行 tag : a num : 100 不同
是因为两个线程 t1 t2 分别获得了 两个不同的对象 thread1 thread2 的对象锁,互相不影响,所以运行结果与预期不同。
3.如果要保持进程间的锁
添加 class 级别的锁
package com.study.current.thread.day01; /** * 关键字synchronized 取得的锁都是对象锁,而不是把一段代码或方法当做锁,所以实例代码中的那个线程先执行 synchronized关键字的方法,那个线程就持有该方法所属对象的锁Lock,两个对象,线程获得的就是两个不同的锁 他们互不影响 有一种情况则是相同的锁,即在静态方法上加 synchronized 关键字,表示锁定.class类,类一级别的锁,独占.class类 * */ public class MultiThread extends Thread { public static int num = 0 ; // 初始化 /** * 添加 static 关键字,是类级别的锁 * 避免不同的对象间并发执行 * 不同的线程获取不同的对象的锁,运行时需要相互等待 * * 不加 static 关键字,是对象级别的锁 * 不同的线程获取不同的对象的锁,互相执行不影响 * * @param tag * @throws InterruptedException */ public static synchronized void printNum(String tag) throws InterruptedException{ if("a".equals(tag)){ num = 100 ; System.out.println("taga num = 100 "); Thread.sleep(1000); }else{ num = 200 ; System.out.println("tagb num = 200 "); } System.out.println("tag : "+tag + " num : "+num); } public static void main(String[] args) { final MultiThread thread1 = new MultiThread(); final MultiThread thread2 = new MultiThread(); Thread t1 = new Thread(new Runnable() { public void run() { try { thread1.printNum("a"); } catch (InterruptedException e) { e.printStackTrace(); } } }); Thread t2 = new Thread(new Runnable() { public void run() { try { thread2.printNum("b"); } catch (InterruptedException e) { e.printStackTrace(); } } }); t1.start(); t2.start(); } }
运行结果:
taga num = 100
tag : a num : 100
tagb num = 200
tag : b num : 200
四:同步及异步
1.概念
同步: synchronized
同步的概念就是共享,如果不是共享的资源,就没有必要共享
异步: asynchronized
异步的概念就是独立,相互之间不受到任何制约。
同步的目的就是为了线程安全
线程安全的特性
原子性(同步)
可见性
2.列子
package com.study.current.thread.day01; public class MyObject extends Thread { public synchronized void method1(){ System.out.println(this.currentThread().getName()); try { Thread.sleep(4000); } catch (InterruptedException e) { e.printStackTrace(); } } /** * A线程先持有object 对象的Lock 锁 B线程如果再这个时候调用对象中的同步 synchronized 方法则需等待。就是同步 B线程可以以异步的方式调用对象中的非 synchronized 修饰的方法 若method2 不加 synchronized ,则 t1 t2 同时输出,不需要获取 method2的锁 反之,t1 输出后需等待 4 s 再输出 t2 */ public synchronized void method2(){ System.out.println(this.currentThread().getName()); } public static void main(String[] args) { final MyObject myO = new MyObject(); Thread t1 = new Thread(new Runnable() { public void run() { myO.method1(); } },"t1"); Thread t2 = new Thread(new Runnable() { public void run() { myO.method2(); } },"t2"); t1.start(); t2.start(); } }
3.分析
如果 method2 不加 synchronized 修饰
运行结果: t1 t2 同时输出
因为 method2 上无锁,不需要获取锁,即异步执行
添加收,t1 输出4s 后 t2 输出