线程安全

线程安全


一、线程安全

线程安全概念:
当多个线程访问某一个类(对象或方法)时,这个类(对象或方法)都能表现出正确的行为,那么这个类(对象或方法)就是线程安全的。

二、同一个对象的锁

1.对同一个对象的对象锁 synchronized
synchronized 锁是对象级别的

package com.study.current.thread.day01;

/**
 * 线程安全概念:当多个线程访问某一个类(对象或方法)时,这个类(对象或方法)都能表现出正确的行为,那么这个类(对象或方法)就是线程安全的。
 */
public class MyThread extends Thread {

	private int count = 5 ;
	
	/**
	 * synchronized 的作用: 加锁,防止并发方法
	 * 
	 * synchronized:可以在任意对象及方法上加锁,而加锁的这段代码称为“互斥区”或“临界区”。
	 */
	public synchronized void run(){
		/**
		 * 线程执行的顺序不是代码的顺序,而是CPU分配的顺序决定的
		        当过个线程访问myThread的run方法时,以排队的方式进行处理
		        排队是按照CPU分配的先后顺序而定的
		        一个线程想要执行synchronized修饰的方法里的代码,首先是尝试获得锁,如果拿到锁,执行
		   synchronized代码体内容
		        拿不到锁,这个线程就会不断地尝试获得这把锁,直到拿到为止
		        而且是多个线程同时去竞争这把锁
		 */
		this.count -- ;
		System.out.println(Thread.currentThread().getName()+" count:"+count);
	}
	
	public static void main(String[] args) {
		MyThread thread = new MyThread();
		
		Thread t1 = new Thread(thread,"t1");
		Thread t2 = new Thread(thread,"t2");
		Thread t3 = new Thread(thread,"t3");
		Thread t4 = new Thread(thread,"t4");
		Thread t5 = new Thread(thread,"t5");
		t1.start();
		t2.start();
		t3.start();
		t4.start();
		t5.start();
		
	}
	
}



2.例子分析

不加synchronized 运行结果:
t2 count:3
t3 count:2
t4 count:1
t1 count:3
t5 count:0

与预期的结果:count值按照顺序输出,相差很大
--------------------------------------------
添加synchronized 运行结果:
t1 count:4
t3 count:3
t2 count:2
t4 count:1
t5 count:0

count 值按照顺序输出,但t1 ~ t5 不是按照顺序,即线程执行的顺序不是代码的位置顺序
而是谁先抢到CPU的执行权,那么谁就先执行


二、多个对象的对象锁

1.对多个对象的对象锁 synchronized

多个线程多个锁:多个线程,每个线程都可以拿到自己指定的锁,分别获得锁之后,执行synchronized的方法体的内容

package com.study.current.thread.day01;

/**
 *  关键字synchronized 取得的锁都是对象锁,而不是把一段代码或方法当做锁,所以实例代码中的那个线程先执行
	synchronized关键字的方法,那个线程就持有该方法所属对象的锁Lock,两个对象,线程获得的就是两个不同的锁
	他们互不影响

	有一种情况则是相同的锁,即在静态方法上加 synchronized 关键字,表示锁定.class类,类一级别的锁,独占.class类
 *
 */
public class MultiThread extends Thread {

	public int num = 0 ; // 初始化
	
	public synchronized void printNum(String tag) throws InterruptedException{
		if("a".equals(tag)){
			num = 100 ;
			System.out.println("taga num = 100 ");
			Thread.sleep(1000);
		}else{
			num = 200 ;
			System.out.println("tagb num = 200 ");
		}
		
		System.out.println("tag : "+tag + " num : "+num);
	}
	
	public static void main(String[] args) {
		final MultiThread thread1 = new MultiThread();
		final MultiThread thread2 = new MultiThread();
		
		Thread t1 = new Thread(new Runnable() {
			
			public void run() {
				try {
					thread1.printNum("a");
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		});
		
		Thread t2 = new Thread(new Runnable() {
			
			public void run() {
					try {
						thread2.printNum("b");
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
			}
		});
		
		t1.start();
		t2.start();
	}
}




2.例子分析

运行结果:
taga num = 100
tagb num = 200
tag : b num : 200
tag : a num : 100

与预期 taga num = 100 后执行 tag : a num : 100 不同
是因为两个线程 t1 t2 分别获得了 两个不同的对象 thread1 thread2 的对象锁,互相不影响,所以运行结果与预期不同。

3.如果要保持进程间的锁
添加 class 级别的锁

package com.study.current.thread.day01;

/**
 *  关键字synchronized 取得的锁都是对象锁,而不是把一段代码或方法当做锁,所以实例代码中的那个线程先执行
	synchronized关键字的方法,那个线程就持有该方法所属对象的锁Lock,两个对象,线程获得的就是两个不同的锁
	他们互不影响

	有一种情况则是相同的锁,即在静态方法上加 synchronized 关键字,表示锁定.class类,类一级别的锁,独占.class类
 *
 */
public class MultiThread extends Thread {

	
	public static int num = 0 ; // 初始化
	
	/**
	 * 添加 static 关键字,是类级别的锁
	 * 避免不同的对象间并发执行
	 * 不同的线程获取不同的对象的锁,运行时需要相互等待
	 * 
	 * 不加 static 关键字,是对象级别的锁
	 * 不同的线程获取不同的对象的锁,互相执行不影响
	 * 
	 * @param tag
	 * @throws InterruptedException
	 */
	public static synchronized void printNum(String tag) throws InterruptedException{
		if("a".equals(tag)){
			num = 100 ;
			System.out.println("taga num = 100 ");
			Thread.sleep(1000);
		}else{
			num = 200 ;
			System.out.println("tagb num = 200 ");
		}
		
		System.out.println("tag : "+tag + " num : "+num);
	}
	
	public static void main(String[] args) {
		final MultiThread thread1 = new MultiThread();
		final MultiThread thread2 = new MultiThread();
		
		Thread t1 = new Thread(new Runnable() {
			
			public void run() {
				try {
					thread1.printNum("a");
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		});
		
		Thread t2 = new Thread(new Runnable() {
			
			public void run() {
					try {
						thread2.printNum("b");
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
			}
		});
		
		t1.start();
		t2.start();
	}
}



运行结果:

taga num = 100
tag : a num : 100
tagb num = 200
tag : b num : 200

四:同步及异步

1.概念
同步: synchronized
同步的概念就是共享,如果不是共享的资源,就没有必要共享

异步: asynchronized
异步的概念就是独立,相互之间不受到任何制约。

同步的目的就是为了线程安全

线程安全的特性
原子性(同步)
可见性

2.列子
package com.study.current.thread.day01;

public class MyObject extends Thread {

	public synchronized void method1(){
		System.out.println(this.currentThread().getName());
		try {
			Thread.sleep(4000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
	/**
	 * 	A线程先持有object 对象的Lock 锁
		B线程如果再这个时候调用对象中的同步 synchronized 方法则需等待。就是同步
		B线程可以以异步的方式调用对象中的非 synchronized 修饰的方法
			
		若method2 不加 synchronized ,则 t1 t2 同时输出,不需要获取 method2的锁
		反之,t1 输出后需等待 4 s 再输出 t2 
	 */
	public synchronized void method2(){
		System.out.println(this.currentThread().getName());
	}
	
	public static void main(String[] args) {
		
		final MyObject myO = new MyObject();
		
		Thread t1 = new Thread(new Runnable() {
			
			public void run() {
				myO.method1();
			}
		},"t1");
		
		Thread t2 = new Thread(new Runnable() {
			
			public void run() {
				myO.method2();
			}
		},"t2");
		
		t1.start();
		t2.start();
	}
}



3.分析

如果 method2 不加 synchronized 修饰
运行结果: t1  t2 同时输出
因为 method2 上无锁,不需要获取锁,即异步执行

添加收,t1 输出4s 后 t2 输出

猜你喜欢

转载自mingyundezuoan.iteye.com/blog/2389477
今日推荐