上一章大概讲了线程的使用,这一篇只说说多线程里面的两个关键字:
synchronized和volatile
我们上次说到了线程安全的概念,当多个线程多同一个对象进行操作的时候,线程会出现脏数据的现象,此时,线程便签。简单来说就是两个线程同时对数据进行了操作,所以,我们需要这两个关键字对线程做一个同步:
首先:方法内的变量是线程安全的(因为方法内变量属于局部变量,本身就是私有,不存在安全)
其次:实例变量非线程安全
所以,要实现安全,就必须都能被操作,且存在次序,不可同时。
1.synchronized
synchronized:加锁
就这么简单的解释,很好理解。
用法:
用法 | 解释 |
---|---|
synchronized public void run(){} | 对一个方法进行说明synchronized |
synchronized(this) {} | 将要上锁的语句包含进去,同步语句块 |
synchronized(非this对象) {} | 把其中的this换成其他的对象 |
synchronized(class) {} | 给对象上锁 |
还有其他很多其妙的用法就需要详细去挖掘了,这里暂时只想起来这几种。
下面提一提使用synchronized的一些要点以及注意:
1.1多个对象多个锁
//main类
Mythread mythread =new Mythread();
mythread.setName("001");
Mythread mythread2 =new Mythread();
mythread2.setName("002");
Thread mythread01 =new Thread(mythread);
try {
Thread.sleep(200);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
Thread mythread02 =new Thread(mythread2);
mythread01.start();
mythread02.start();
//Thead类
@Override
public void run(){
ServiceClass serviceClass = new ServiceClass();
serviceClass.test(this.currentThread().getName());
}
//class类
public class ServiceClass {
int i =1;
public void test(String name){
while (i<10) {
i++;
System.out.println("当前线程"+name+"数字"+i);
}
}
}
例子很简单,但是有一定意义:
尽管我是对一个class类进行操作,但是,我是两个对象,这是互不影响的,和方法中的局部变量一样,并没有数据的混乱。
- 所以,对一个实例多个对象,会分别加锁,并不是加了一个,其他的也会加锁。
- 对于不需要的,没有数据共享的对象,不需要加锁。
1.2synchronized方法锁
synchronized public void run(){}
这一小节要说明的主要是:
synchronized关键字对方法加锁的时候,并不是代码块或者单单这个方法,他是对这个方法所属对象加锁,此时该对象被LOCK,其他线程需要等这个对象被解锁后,才可以访问这个线程
synchronized public void test(String name){
while (i<100000) {
i++;
System.out.println("当前线程"+name+"数字"+i);
}
}
public void test02(String name){
System.out.println(name);
}
int i =1;
synchronized public void test(String name){
while (i<100000) {
i++;
System.out.println("当前线程"+name+"数字"+i);
}
}
synchronized public void test02(String name){
System.out.println(name);
}
这里的System.out.println(name);已经在之前执行了
加上synchronized:
可以看出来,线程再最后才执行。
所以我们不难得出结论:
A线程持有C对象的锁之后,B线程想要访问synchronized 声明的方法需要同步,等A线程执行完才行。
但是,B线程访问非synchronized 声明的方法可以异步访问。
1.3synchronized重入
synchronized重入的意思就是,当A线程持有B对象的锁之后,可以访问B对象的synchronized声明的所有方法。自己可以获得自己的内部锁,只要还没有释放锁
这个就不需要解释了,应该能懂,不然,肯定会死锁。
1.4异常释放锁
这个的含义就是:当出现异常的时候,锁会被自动释放
1.5同步不可以继承
synchronized关键字不可以被继承到子类,子类仍然需要加上synchronized关键字。
这个也可以不用解释了。
最明显的就是不写上synchronized关键字子类不会报错~
1.6synchronized同步代码块
synchronized同步代码块的用法是
public void test(String name){
synchronized (name) {
while (i<100000) {
i++;
System.out.println("当前线程"+name+"数字"+i);
}
}
}
其中的name一般被称为对象监视器:
同步代码块的特点是:
当一个代码块被synchronized(aaa)声明,那么线程访问这个代码块是同步的,但是不同线程访问非synchronized(aaa)的代码块是异步。
而其中还得注意,只有当对象监视器,即上文的aaa相同的同步代码块,才能实现一个对象内同步。
例如:当线程A有了synchronized(aaa)代码块的锁,线程B就无法访问其他的synchronized(aaa)上锁的代码块,但是线程B可以访问synchronized(bbb)上锁的代码块
另外,一般不推荐使用String类型的数据作为对象监视器,存在一个常量池的问题
synchronized(this)锁定的是当前对象
来做个小结:
- synchronized同步方法和synchronized(this):
1.对其他synchronized同步方法或者synchronized(this)代码块同步阻塞
2.同一时间,只有一个线程可以执行synchronized同步方法中的代码 - synchronizedsynchronized(非this):
1,.多个线程持有对象监视器为同一对象时,只有一个线程可执行
1.6synchronized(class)同步代码块
对class对象持有锁,即就算该class有多个实现对象,是同一锁。
2.volatile
volatile是针对变量,即让一个变量对多个线程可见,实现的同步
volatile是强制的从公共内存中读取值。不从私有堆栈中取值。
3.总结
volatile远远不止这些,包括原子性,私有空间很多东西,这个先空着,以后补。
4.参考
《Java多线程编程核心技术》———–高洪岩
很简明的一本书,有兴趣可以看看。