Skip to content

线程冲突

2228字约7分钟

java多线程

2024-10-24

线程冲突

1.产生原因

1.多个线程同时操作同一个资源

2.多个线程操作频度和操作时间很相近

2.解决方法

1.线程隔离

不共享资源

1.1栈隔离(使用局部变量)

1.2给每个线程一个资源副本,ThreadLocal

2.通过同步机制

2.1 synchronized

1.同步代码块 synchronized修饰代码块称为同步代码块,参数为需要同步的线程需要用到的对象 synchronized (object) { i++; System.out.println("MyThread2:"+i); }

2.同步体包含所有的同步代码,保证同一范围只能有一个线程在该区域运行

3.同步体线程运行到该区域时会尝试加锁,若没有锁就直接加锁,若已加锁(加锁未成功)则此线程进入阻塞状态直到运行的线程离开同步体,阻塞才能结束

4.JDK1.6之前通过操作系统的核心态锁机制

5.synchronized 用来控制使用同步对象的线程

6.锁对象,阻塞使用对象的线程

7.是一个Java的关键字是同步锁

8.修饰一个代码块,表示该代码块是一个同步块,可以保证同一时间只能有一个使用该对象的线程

9.修饰一个方法,表示该方法是同步方法,其作用范围是整个方法,锁定的是this

10.修饰一个静态方法,锁定的是类名.class

11.不可修饰变量和类

作用

1.对某个对象(信号量)上锁

2.保证线程在互斥区互斥

3.解决了多个线程操作同一资源冲突的问题

线程安全

多个线程同时操作该类的方法,不会产生线程冲突

单例模式饿汉模式线程安全,懒汉模式不安全(可加synchronized )解决

public class SynchronizedTest {
	static int i = 1;
	static class MyThread1 extends Thread{
		// 相当于信号量,用来线程同步
		private Object object;
		public MyThread1() {

		}
		MyThread1(Object object){
			this.object = object;
		}
		@Override
//		public void run() {
//			// 同步代码块
//			// synchronized修饰代码块称为同步代码块,参数为需要同步的线程需要用到的对象(锁定的是用到的对象)
//			synchronized (object) {
//				i++;
//				System.out.println("MyThread1:"+i);
//			}
//		}
		// synchronized修饰方法该方法为同步方法,锁定的是this
		public synchronized void run() {
			// 同步代码块
			// synchronized修饰代码块称为同步代码块,参数为需要同步的线程需要用到的对象
			i++;
			System.out.println("MyThread1:"+i);
		}
	}
	static class MyThread2 extends Thread{
		private Object object;
		public MyThread2() {

		}
		MyThread2(Object object){
			this.object = object;
		}
		@Override
//		public void run() {
//		// 同步代码块
//		// synchronized修饰代码块称为同步代码块,参数为需要同步的线程需要用到的对象
//		synchronized (object) {
//			i++;
//			System.out.println("MyThread2:"+i);
//		}
//	}
	public synchronized void run() {
		// 同步代码块
		// synchronized修饰代码块称为同步代码块,参数为需要同步的线程需要用到的对象
		i++;
		System.out.println("MyThread2:"+i);
	}
	}
	public static void main(String[] args) {
//		testCountNotSyn();
//		testCountSyn();
		testCountSynWithMethd();
	}

	private static void testCountNotSyn() {
		// 线程不同步
		Thread thread1 = new MyThread1();
		Thread thread2 = new MyThread2();
		thread1.start();
		thread2.start();
	}
	
	private static void testCountSyn() {
		// 线程同步
		Object object = new Object();
		Thread thread1 = new MyThread1(object);
		Thread thread2 = new MyThread2(object);
		thread1.start();
		thread2.start();
	}
	private static void testCountSynWithMethd() {
		// 线程同步
		Object object = new Object();
		Thread thread1 = new MyThread1(object);
//		Thread thread2 = new MyThread2(object);
		Thread thread2 = new Thread(thread1);
		thread1.start();
		thread2.start();
	}
}
2.2锁
1.synchronized 和lock的区别
1.都是线程同步机制

2.synchronized 是关键字,lock是Java对象,实现机制不用,lock更灵活

3.可重入锁:已经加锁可以再次加锁

4.是否异常释放锁:synchronized 异常自动释放,lock需要手动释放

5.操作方式:synchronized 自动加锁,lock手动加锁

6.性能:lock性能高

1.来源: lock是一个接口,而synchronized是java的一个关键字,synchronized是内置的语言实现;

2.异常是否释放锁: synchronized在发生异常时候会自动释放占有的锁,因此不会出现死锁;而lock发生异常时候,不会主动释放占有的锁,必须手动unlock来释放锁,可能引起死锁的发生。(所以最好将同步代码块用try catch包起来,finally中写入unlock,避免死锁的发生。)

3.是否响应中断 lock等待锁过程中可以用interrupt来中断等待,而synchronized只能等待锁的释放,不能响应中断;

4.是否知道获取锁 Lock可以通过trylock来知道有没有获取锁,而synchronized不能;

5.Lock可以提高多个线程进行读操作的效率。(可以通过readwritelock实现读写分离)

6.在性能上来说,如果竞争资源不激烈,两者的性能是差不多的,而当竞争资源非常激烈时(即有大量线程同时竞争),此时Lock的性能要远远优于synchronized。所以说,在具体使用时要根据适当情况选择。

7.synchronized使用Object对象本身的wait 、notify、notifyAll调度机制,而Lock可以使用Condition进行线程之间的调度。

1.来源: lock是一个接口,而synchronized是java的一个关键字,synchronized是内置的语言实现;

2.异常是否释放锁: synchronized在发生异常时候会自动释放占有的锁,因此不会出现死锁;而lock发生异常时候,不会主动释放占有的锁,必须手动unlock来释放锁,可能引起死锁的发生。(所以最好将同步代码块用try catch包起来,finally中写入unlock,避免死锁的发生。)

3.是否响应中断 lock等待锁过程中可以用interrupt来中断等待,而synchronized只能等待锁的释放,不能响应中断;

4.是否知道获取锁 Lock可以通过trylock来知道有没有获取锁,而synchronized不能;

5.Lock可以提高多个线程进行读操作的效率。(可以通过readwritelock实现读写分离)

6.在性能上来说,如果竞争资源不激烈,两者的性能是差不多的,而当竞争资源非常激烈时(即有大量线程同时竞争),此时Lock的性能要远远优于synchronized。所以说,在具体使用时要根据适当情况选择。

7.synchronized使用Object对象本身的wait 、notify、notifyAll调度机制,而Lock可以使用Condition进行线程之间的调度。

image-20211013204231887

public class LockTest {
	static int i =0;
	static class Thread1 implements Runnable{

		@Override
		public void run() {
			Lock lock = new ReentrantLock();
//			if (!lock.tryLock()) {
//				System.out.println("Thread1等待");
//			}
			// 对当前线程加锁
			lock.lock();
//			i++;
			System.out.println("Thread1: "+i);
			
			i++;
			// 对当前线程解锁
			lock.unlock();
		}
		
	}
	static class Thread2 implements Runnable{

		@Override
		public void run() {
			Lock lock = new ReentrantLock();
//			if (!lock.tryLock()) {
//				System.out.println("Thread2等待");
//			}
			lock.lock();
			i++;
			System.out.println("Thread2: "+i);
			lock.unlock();
		}
	}
	
	public static void main(String[] args) {
		test();
	}

	private static void test() {
		Thread thread1 = new Thread(new Thread1());
		Thread thread2 = new Thread(new Thread2());
		thread1.start();
		thread2.start();
	}
}
死锁

1.产生原因

1.多个线程共同操作了的资源

2.线程可以单独锁定部分资源且不释放

2.解决死锁

1.线程隔离

2.不允许锁定部分资源

3.要有释放条件

public class DiedLock {
	static class Thread1 implements Runnable{
		// 定义资源
		private Object a;
		private Object b;
		
		public Thread1(Object a, Object b) {
			this.a = a;
			this.b = b;
		}

		@Override
		public void run() {
			synchronized (a) {
				System.out.println("Thread1锁a");
				synchronized (b) {
					System.out.println("Thread1锁b");
				}
			}
		}
	}
	static class Thread2 implements Runnable{
		// 定义资源
		private Object a;
		private Object b;
		
		public Thread2(Object a, Object b) {
			this.a = a;
			this.b = b;
		}

		@Override
		public void run() {
			synchronized (b) {
				System.out.println("Thread2锁b");
				synchronized (a) {
					System.out.println("Thread2锁a");
				}
			}
		}
	}
	public static void main(String[] args) {
		diedLock();
	}

	private static void diedLock() {
		Object a = new Object();
		Object b = new Object();
		Thread thread1 = new Thread(new Thread1(a, b));
		Thread thread2 = new Thread(new Thread2(a, b));
		thread1.start();
		thread2.start();
	}
}
活锁

1.原因

1.共享资源

2.多个线程单独锁定部分资源

3.周期性释放资源

2.解决

不允许周期性释放资源,必须随机释放

2.3.notify()与wait()

notify()唤醒信号(随机唤醒对象监视器(阻塞的线程)中的任意一个)

nitifyAll()唤醒所有阻塞的线程到就绪队列

wait()阻塞使用它的线程

public class WaitNotifyTest {
	static class Thread1 implements Runnable{
		// 信号量
		private Object mutux;
		
		public Thread1(Object mutux) {
			this.mutux = mutux;
		}

		@Override
		public void run() {
			synchronized (mutux) {
				String[] strings = "a,b,c".split(",");
				for (int i = 0; i < strings.length; i++) {
					// 每次运行之前唤醒信号
					mutux.notify();
					System.out.print(strings[i]+",");
					// 每次运行之后阻塞信号
					try {
						mutux.wait();
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
				// 执行完毕唤醒所有线程自然结束
				mutux.notify();
			}
		}
	}
	static class Thread2 implements Runnable{
		private Object mutux;
		
		public Thread2(Object mutux) {
			this.mutux = mutux;
		}

		@Override
		public void run() {
			synchronized (mutux) {
				String[] strings = "1,2,3".split(",");
				for (int i = 0; i < strings.length; i++) {
					mutux.notify();
					System.out.print(strings[i]+",");
					try {
						mutux.wait();
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
			}
		}
	}
	public static void main(String[] args) {
		print();
	}

	private static void print() {
		Object mutex = new Object();
		Thread thread1 = new Thread(new Thread1(mutex));
		Thread thread2 = new Thread(new Thread2(mutex));
		thread1.start();
		thread2.start();
	}
}
2.4slee()与wait()的区别

1.都可以让线程进入阻塞

2.来自不同的类,sleep->Thread,wait->Object

3.sleep没有释放锁,wait释放了锁

4.sleep是让自己挂起,而wait是让使用它的线程挂起