线程同步 synchronized和volatile

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方法仅能允许同一时间内一个其中一个被线程调用

同一静态方法在同一时间内仅能允许一个线程运行

猜你喜欢

转载自blog.csdn.net/y2052/article/details/82314880