- 线程安全概念:当多个线程访问某一个类(对象或方法)时,这个类始终都能表现出正确的行为,那么这个类(对象或方法)就是线程安全的。
- 实现一个线程:extends Thread(重写run方法) 或 implements Runnable
- synchronized:可以在任意对象及方法上加锁,而加锁的这段代码称为互斥区,或临界区。
- 注释:一个线程想要执行syschronized修饰的方法里的代码,首先是尝试获得锁,如果拿到锁,执行syschronzied代码体内容,拿不到锁,这个线程就会不断的尝试获得这把锁,直到拿到为止,而且是多个线程同时去竞争这把锁。(也就是会有锁竞争的问题)。
1 public class MyThread extends Thread{
2
3 private int count=5;
4
5 public synchronized void run() {
6 count--;
7 System.out.println(this.currentThread().getName()+":"+count);
8 }
9
10 public static void main(String args[]) {
11
12 MyThread thread=new MyThread();//当前线程
13 Thread t1=new Thread(thread,"1号线程");
14 Thread t2=new Thread(thread,"2号线程");
15 Thread t3=new Thread(thread,"3号线程");
16 Thread t4=new Thread(thread,"4号线程");
17 Thread t5=new Thread(thread,"5号线程");
18 t1.start();
19 t2.start();
20 t3.start();
21 t4.start();
22 t5.start();
23 //按照线程启动,结果应该为:43210,但是run方法不加synchronized达不到预期结果
//使用synchronized可以锁住但是存在锁竞争问题
24 }
25 }
- 多个线程多个锁:
- 注释:关键字synchronized取得的锁都是对象锁,而不是把一段代码(方法)当作锁,多个对象通过多个线程去访问synchronized修饰的方法时,线程拿到的是自己指定对象的锁(不是相同的锁),这种情况下会导致结果达不到预期,要想多线程不同对象拿到相同的锁,可以将synchronzied修饰的方法改为静态的(static),表示锁定.class类,类一级别的锁(独占.class类)
public class MultiThread {
private static int num=0;
//static
public static synchronized void printNum(String tag) {
try {
if(tag.equals("a")) {
num=100;
System.out.println("a");
Thread.sleep(2000);
}else {
num=200;
System.out.println("b");
}
System.out.println(tag+":"+num);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public static void main(String args[]) {
MultiThread multiThread1=new MultiThread();
MultiThread multiThread2=new MultiThread();
//线程1
Thread t1=new Thread(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
multiThread1.printNum("a");
}
});
//线程2
Thread t2=new Thread(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
multiThread2.printNum("b");
}
});
t1.start();
t2.start();
//预期结果应该是:a,a:100,b,b:200;但是即使方法加上synchronzied修饰,结果也达不到预期
//2个线程分别得到的是multiThread1与multiThread2的对象锁,达不到同步的效果
//要想达到效果可以将方法改成静态的,此时属于类(MultiThread)一级别的锁
//2个线程要做到不同时调用printNum方法,锁.class类
}
}
- 同步:synchronzied,同步的概念就是共享,不是共享资源没必要同步,同步的目的就是为了线程安全,线程安全条件:原子性(同步),可见性。
- 异步:asynchronized,异步的概念就是独立,互相之间不受任何制约(类似Ajax)。
1.当数据库的一个事务A正在使用一个数据但还没有提交,另外一个事务B也访问到了这个数据,还使用了这个数据,这就会导致事务B使用了事务A没有提交之前的数据。(如果当前事务A回滚,那么事务B拿到的就是脏数据)
2.线程A去查某一数据,在还没有拿到该数据的时候,线程B修改了该数据,线程A拿到的是B没修改之前的数据,这是由于数据库一致性读的特性造成的。(在数据库删改的过程中,会将数据存入快照中,如果监听到数据有改动会去快照中找旧数据,所以拿到的是之前的数据)
- synchronized锁重入:使用synchronized时,当一个线程得到了一个对象的锁后,再次请求此对象时可以再次得到该对象的锁。
/**
* synchronized锁重入
*/
public class SyncDubbo1 {
public synchronized void method1() {
System.out.println("1");
method2();
}
public synchronized void method2() {
System.out.println("2");
method3();
}
public synchronized void method3() {
System.out.println("3");
}
public static void main(String args[]) {
SyncDubbo1 dubbo=new SyncDubbo1();
//线程1
Thread t1=new Thread(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
dubbo.method1();
}
});
t1.start();
}
}
- 父子继承关系中要想实现同步,都得使用synchronized,否则存在线程安全问题。
/**
* 存在继承关系时,synchronzied修饰的父类子类方法,可以实现线程安全
*/
public class SyncDubbo2 {
static class A{
public int i=10;
public synchronized void add(){
try {
i--;
System.out.println("A:"+i);
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
static class B extends A{
public synchronized void add(){
try {
while(i>0){
i--;
System.out.println("B:"+i);
Thread.sleep(1000);
super.add();
}
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
public static void main(String args[]){
//线程1
Thread t1=new Thread(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
B b=new B();
b.add();
}
});
t1.start();
}
}
- synchronized使用String的常量加锁,会出现死循环问题。
- volatile关键字:volatile关键字主要作用是使变量在多个线程之间可见。
- volatile的作用就是强制线程到主内存(共享内存)里去读取变量,而不去线程工作内存区里去读取,从而实现多个线程间的变量可见,也就是满足线程安全的可见性。
- volatile不具备同步性(原子性),可以算是一个轻量级的synchronized,性能要比synchronzied强大很多,不会造成阻塞(netty底层大量使用volatile)。
- volatile用于多个线程之间变量可见,不能代替synchronzied的同步功能。(AtomicInteger类可以实现原子性,但是它只保证本身方法的原子性,不能保证多个方法的原子性)。
- while轮询的方式(通过条件判断是否达到实现通讯,用volatile)。
- 同步synchronized:线程是操作系统中独立的个体,但这些个体如果不经过特殊的处理就不能成为一个整体,线程间的通讯就成为整体的必要方法之一。
- 使用wait/notify:wait和notify必须配合synchronized关键字使用。wait方法释放锁,notify方法不释放锁。