二,线程安全

一,三类线程安全问题

当多个线程同时访问一个对象,如果不用考虑线程之间的协作,不需要额外的同步控制,就可以得到正确的结果,那么这个对象是线程安全的。

常见的线程安全错误有三种:

1,运行结果错误

public static void main(String[] args) throws Exception {
  Runnable r = ()->{
    for(int i = 0; i < 1000; i++){
      a++;
    }
  };

  Thread t1 = new Thread(r);
  Thread t2 = new Thread(r);
  t1.start();
  t2.start();
  t1.join();
  t2.join();
  System.out.println(a);
}

  

由于a++非原子操作,导致a最终的结果不是2000

2,发布和初始化导致线程安全问题

如果在线程中发布或初始化变量,由于无法保证线程开始运行的时间,导致数据没有按照顺序执行。

3,活跃性问题

 分为死锁、活锁、饥饿

死锁,当两个线程相互拥有对方等待的资源,同时又等待对方的资源。这样就造成死锁。

	Object o1 = new Object(),o2 = new Object();
	
	public void thread1(){
		synchronized (o1) {
			try {
				Thread.sleep(1000);
				synchronized(o2){
					
				}
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
	public void thread2(){
		synchronized (o2) {
			try {
				Thread.sleep(1000);
				synchronized(o1){
					
				}
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
	
	public static void main(String[] args) throws Exception {
		P3 p = new P3();
		new Thread(()->{p.thread1();}).start();
		new Thread(()->{p.thread2();}).start();
	}
	

活锁,指发送在循环中的错误,一直执行,但是一直失败

饥饿,线程需要的资源始终无法得到,而导致线程一直无法运行。

 

二,需要注意线程安全的场景

1,访问共享变量和资源

比如多个线程同时访问static变量

2,依赖时序的操作

比如检查-执行操作。

if(x == 7)

  x = 2 * x;

先检查,后执行,这两步操作不是原子的,如果多个线程同时运行,就需要加锁保证原子性。

3,不同数据之间存在绑定关系

4,对方没有声明自己是线程安全的

 

三,为什么多线程会带来性能问题

1,线程调度的开销

上下文切换,缓存失效

2,线程协作的开销

 

posted @ 2020-04-20 17:19  apple2016  阅读(80)  评论(0编辑  收藏  举报