synchronized实现线程的互斥处理
线程的互斥机制被称为 监视
1 修饰方法 修饰方法时锁定的是调用该方法的对象。
它并不能使调用该方法的多个对象在执行顺序上互斥
1.1 修饰实例方法
public synchronized void synTest{ //... }
针对 一个实例对象 1.2 修饰静态方法 public synchronized static void synTest{ //... }
与public static void synTest{
synchronized(类名.class){ //...
}
}等效
针对 一个同一类 2 修饰代码块 synchronized (变量名/对象/类){ } 针对方法中的一部分 而非整个方法 2.1 synchronized (变量名){ } 一次只有一个线程进入该代码块 2.2 synchronized (对象){ } 对象锁是用于对象实例方法,或者一个对象实例上的
如果线程进入,则得到对象锁 , 其他线程在该对象上的任何操作都不能进行
针对 一个对象 2.3 synchronized (类){ } 类锁用于类的静态方法或者一个类的class对象上的
当一个线程获得对象锁的同时,另一个线程可以获得该类锁
对于synchronized修饰的方法,称为synchronized方法或同步方法
对于synchronized修饰的代码块 称为synchronized语句
对于同步方法 每次只能由一个线程运行
每个实例都拥有一个独立的锁 所以一个实例中synchronized的实例方法在运行 其他实例中的synchronized的实例方法也可以运行
1.1
public synchronized void test(char a) {
int count = 1;
for(int i = 0 ; i < 5; i++)
{
System.out.printf(count++ + " %c\n",a);
try {
Thread.sleep(1500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public void print(final char c) { //匿名内部类只能使用方法中的final类
new Thread(new Runnable() {
public void run() {
test(c);
}
}).start();
}
SynMethod a = new SynMethod();
SynMethod b = new SynMethod();
a.print('A');
b.print('B');
输出
1 B
1 A
2 A
2 B
3 B
3 A
4 B
4 A
5 B
5 A
1.2
将1.1中 test()方法 添加修饰符 static
1.2输出
1 A
2 A
3 A
4 A
5 A
1 B
2 B
3 B
4 B
5 B
class TT {
Integer a = -100000;
Integer b = 100 ;
}
public class Test implements Runnable {
TT a ;
Test(){
a = new TT();
}
//两个匿名类实现的线程任务
public void printn() {
new Thread(new Runnable() {
@Override
public void run() {
int count = 0;
System.out.println("a.a线程准备");
synchronized (a) { //传入对象a
System.out.println("a.a线程开始 " );
for (int i = 0; i < 5; i++) {
a.a+=10; //仅使用该对象的变量a
System.out.println("a.a" + a.a+" 第%d次 "+ ++count);
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}).start();
}
public void printsn() {
new Thread(new Runnable() {
@Override
public void run() {
int count = 0;
System.out.println("a.b线程准备");
synchronized (a) { //传入对象a
System.out.println("a.b线程开始 ");
for (int i = 0; i < 5; i++) {
a.b+=10; //仅使用该对象的变量b
System.out.println("a.b" + a.b+" 第%d次 "+ ++count);
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}).start();
}
public static void main(String [] args) {
Test a = new Test();
a.printn();
a.printsn();
// System.out.println("Hello Synchronized about Object \n");
}
}
输出
a.a线程准备
a.b线程准备 //未进入synchronized 时两线程都在运行
a.a线程开始
a.a-99990 第%d次 1
a.a-99980 第%d次 2
a.a-99970 第%d次 3
a.a-99960 第%d次 4
a.a-99950 第%d次 5
a.b线程开始
a.b110 第%d次 1
a.b120 第%d次 2
a.b130 第%d次 3
a.b140 第%d次 4
a.b150 第%d次 5
//synchronized(传入对象时) 将获得对象锁
System.out.println("a.a" + a.a+" 第%d次 "+ ++count);
应改为
System.out.printf("a.a " + a.a+" 第%d次\n", ++count);
volatile
一种轻量级的同步机制(与synchronized相比)
可见性,是指当一条线程修改了共享变量的值,新值对于其他线程来说是可以立即得知的。
volatile具备两种特性,第一就是保证共享变量对所有线程的可见性。将一个共享变量声明为volatile后,会有以下效应:
1.当写一个volatile变量时,JMM会把该线程对应的本地内存中的变量强制刷新到主内存中去;
2.这个写会操作会导致其他线程中的缓存无效
3.定义long或double变量时,如果使用volatile关键字,就会获得(简单的赋值与返回操作)原子性
禁止指令重排序优化。volatile对于单个的共享变量的读/写具有原子性
1.重排序操作不会对存在数据依赖关系的操作进行重排序。
. 2.重排序是为了优化性能,但是不管怎么重排序,单线程下程序的执行结果不能被改变,但对于多线程中若线程A的某个变量作为多线程中的标志位,则重排序可能要破坏程序的逻辑性
synchronized | volatitle |
可能会造成线程的阻塞 | 不会造成线程的阻塞 |
可对 变量 方法 代码块 | 仅能对变量使用 |
可以保证变量的修改可见性和原子性 | 仅使变量在多个线程间可见 |
synchronized则可实现线程的安全性 | 仅仅使用volatile并不能保证线程安全性 |
同一实例的synchronized方法仅能允许同一时间内一个其中一个被线程调用
同一静态方法在同一时间内仅能允许一个线程运行