1.什么是线程安全
当多个线程去访问某一个类(对象、方法、变量....)的时候,这个类始终都能表现出正确的行为,那么这个类(对象、方法、变量)就是线程安全的。简单的说就是当多线程访问同一段代码的时候不会产生不确定的结果。
线程安全一般都涉及到synchronized 关键字。
示例如下:
public class MyThread extends Thread { private int i = 5; @Override public synchronized void run() { i--; System.out.println(currentThread().getName()+" i = " + i); } public static void main(String[] args) { MyThread myThread = new MyThread(); Thread t1 = new Thread(myThread); Thread t2 = new Thread(myThread); Thread t3 = new Thread(myThread); Thread t4 = new Thread(myThread); Thread t5 = new Thread(myThread); t1.start(); t2.start(); t3.start(); t4.start(); t5.start(); } }
当多个线程访问myThread 对象的run方法时候 以排队的方式进行等待(这里的等待是按照CPU处理的先后顺序而定的)
一个线程要想执行synchronize修饰的方法里的代码,首先尝试获得锁,如果拿到锁,执行synchronize代码体内容,拿不到锁,这个线程就会不断的尝试获取这把锁,直到拿到为止,而且是多线程同时去竞争这把锁。这里会存在锁的竞争问题
2.锁竞争问题
多个线程去争夺一把锁,造成CPU的使用率过高,可能会存在宕机问题,应该避免锁竞争问题
3.多个线程多个锁问题
多个线程,每个线程都可以拿到自己指定的锁,分别获得锁之后,执行synchronize修饰的代码
示例代码
public class MutilThread { int num = 0; public synchronized void printNum(String tag) { if (tag.equals("a")) { num = 100; System.out.println("a结束了========="); } if (tag.equals("b")) { num = 2000; System.out.println("b结束了========="); } System.out.println("此时的num是==" + num); } public static void main(String[] args) { MutilThread m1 = new MutilThread(); MutilThread m2 = new MutilThread(); new Thread(() -> m1.printNum("a")).start(); new Thread(() -> m2.printNum("b")).start(); } }
关键字synchronize取得的都是对象锁,而不是把一段代码当做锁,所以示例代码中哪个线程先执行synchronize关键字修饰的代码,哪个线程就持有该方法所属的对象的锁(Lock),两个线程,线程获得的就是不同的锁,他们互不影响。
有一种情况是相同的锁,即在静态方法上加synchronize关键字,表示锁定.class类,类一级别的锁(独占.class类)
4.对象锁的同步和异步
同步:synchronized
同步的概念就是共享,关键就是“共享”,如果不是共享资源,就没有必要进行同步
异步:asynchronized
异步的概念就是独立,相互之间不收任何的制约。譬如 http在页面发起ajax请求的时候,我们还可以浏览或者操作页面内动,二者之间没有任何关系。
同步的目的就是就是为了线程安全,其实对于线程安全来说,需要满足两个特性:原子性(同步)和可见性
所谓可见性就是,在多核处理器中,如何多线程对一个变量进行操作,但是这个多线程可能被分配到多个处理器中运行,那么编译器会对代码进行优化,当线程要处理该变量时,多个处理器会将变量从主存复制一份分别存储到自己的片存储器中,等到进行完操作之后,在赋值回主存(这样做的好处是题号运行的速度,因为在处理器过程中多个处理器减少了同主存通信的次数,同样在单核处理器中,这样有于“备份”造成同样的问题)。这样的优化带来的问题之一就是变量可见性---如果线程t1与线程t2分别被安排了在不同的处理器上面,那么t1与t2对于变量A的修改是互相不可见得,如果T1给A赋值,然后T2又新赋值,那么T2的操作就将T1的覆盖掉了,这样会产生不可预料的后果,所以,即使有些操作的原子性的,但是如果不具有可见性,那么多个处理器中的备份就存在失去原子性的意义。
原子性:
和微观世界一样,在微观世界中原子是构成物质的基本单位(电子暂且不论),所以原子的意思代表的是不可分割。由于不可分性质,原子性是拒绝多线程操作的(只有分解为多多步操作,多线程才能对其进行操作,就像一个果盘里有多个苹果,多个小朋友从果盘里拿苹果,每个人都能拿到(苹果足够多),如果果盘里只有一个苹果,一个小朋友拿了其他小朋友就拿不到了,这就是原子性苹果具有原子性,小朋友相当于原子)原子性不论是多核还是单核,具有原子性的量,同一时刻只能有一个线程来对他进行操作。