Thread-线程的同步

Posted xiaokw

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Thread-线程的同步相关的知识,希望对你有一定的参考价值。

线程的同步实现方式

方式一:同步代码块

synchronized(锁对象){
    //需要上锁的内容(同步代码块)
}

方式二:同步方法

public synchronized 返回类型 方法名(参数){
    //需要上锁的内容(同步代码块)
}

普通的同步方法,锁对象是this

静态的同步方法,锁对象是:当前类.class

要求

  1. 有多线程
  2. 多个线程使用的锁对象必须是同一个
  3. wait、notifiy、notifiyAll和当前同步代码的锁对象必须是同一个,否则报异常

扩展:

synchronized("abc"){} //可以实现线程同步,因为abc在字符串常量池中,但即使没有共享的对象也会需要排队等待

Object obj = new Object(); //可以定一个全局属性,实现有共享对象obj

synchronized(obj){}

案例:解决单例模式中懒汉式多线程问题

public class Singletion_Demo3 {
	public static void main(String[] args) {
		A a = new A();
		A b = new A();
		a.start();
		b.start();
	}
}
class A extends Thread{
	public void run() {
		for(int i=0;i<10;i++) {
			System.out.println(Single.getInstance());
		}
	}
}
class Single{
	private Single() {}
	private static Single s;
	public static Single getInstance() {
		if(s==null) //提高效率
			synchronized(Single.class) {
				if(s == null) { //线程安全
					try {
						Thread.sleep(100); //放大问题
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
					s = new Single();
				}
			}
		return s;
	}
}

并发容器

CopyOnWriteArrayList

底层已经实现了线程同步

以下是不安全的线程

import java.util.ArrayList;
import java.util.List;

public class Thread_Demo1 {
	public static void main(String[] args) throws InterruptedException{
		List<Integer> listNum = new ArrayList<>();
		for(int i=0;i<100;i++) {
			new Thread(()->{
				for(int j=0;j<100;j++)
//					synchronized(listNum) {
						listNum.add(j);
//					}
			}).start();
		}
		Thread.sleep(5000);
		System.out.println(listNum.size());
	}
}

可以使用并发容器解决以上线程不安全问题

import java.util.concurrent.CopyOnWriteArrayList;

public class Thread_Demo2 {
	public static void main(String[] args) throws InterruptedException{
		CopyOnWriteArrayList<Integer> listNum = new CopyOnWriteArrayList<>();
		for(int i=0;i<100;i++) {
			new Thread(()->{
				for(int j=0;j<100;j++)
					listNum.add(j);
			}).start();
		}
		Thread.sleep(5000);
		System.out.println(listNum.size());
	}
}

线程死锁

产生

public class DeathLock {
	public static void main(String[] args) {
		new DeathMethod(true,"小红").start();
		new DeathMethod(false,"小明").start();
	}
}
class EatApple{
	public void eat() {
		System.out.println(Thread.currentThread().getName()+"吃苹果");
	}
}
class EatBanana{
	public void eat() {
		System.out.println(Thread.currentThread().getName()+"吃香蕉");
	}
}
class DeathMethod extends Thread{
	private static EatApple apple = new EatApple();
	private static EatBanana banana = new EatBanana(); ;
	boolean flag;
	public DeathMethod(boolean flag,String name) {
		super(name);
		this.flag = flag;
	}
	public void run() {
		if(flag) {
			synchronized(apple) {
				apple.eat();
				synchronized(banana) {
					banana.eat();
				}
			}
		}else {
			synchronized(banana) {
				banana.eat();
				synchronized(apple) {
					apple.eat();
				}
			}
		}
	}
}

就会出现以下情况,线程无法执行下去,一直在等待

小红吃苹果
小明吃香蕉

解决

更改run方法中的锁机制

不要在同一个代码块中,同时持有多个对象的锁

public void run() {
		if(flag) {
			synchronized(apple) {
				apple.eat();
			}
			synchronized(banana) { 
				banana.eat();
			}
		}else {
			synchronized(banana) {
				banana.eat();
			}
			synchronized(apple) {
				apple.eat();
			}
		}
	}

以上是关于Thread-线程的同步的主要内容,如果未能解决你的问题,请参考以下文章

在控制台应用程序中,为啥在等待的异步任务中使用同步阻塞代码 (Thread.Sleep(..)) 的行为类似于多线程? [复制]

多线程中锁的释放问题

两个线程同时执行同步块

几组线程同步代码测试

多线程Thread线程创建

同步函数死锁现象