水滴石穿--多线程安全同步与锁

什么是线程安全?

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

java内存模型

 在说明多线程安全问题前,要明白java内存模型。

为什么有线程安全问题?

当多个线程同时共享,同一个全局变量或静态变量,做写的操作时,可能会发生数据冲突问题,也就是线程安全问题。但是做读操作是不会发生数据冲突问题。

经典售票问题我们可以一起写一下!!

/**
 * @classDesc: 演示多线程的安全问题
 * @author: hj
 * @date:2018年12月12日 上午10:49:59
 */

public class ThreadTrain implements Runnable {
	private int trainCount = 100;

	@Override
	public void run() {
		synchronized (this) {

			while (trainCount > 0) {
				try {
					Thread.sleep(10);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				sale();
			}
		}

	}

	// 模仿卖票
	public void sale() {
		if (trainCount > 0) {
			System.out.println(Thread.currentThread().getName() + ",出售第" + (100 - trainCount + 1) + "张票");
			trainCount--;
		}
	}

	public static void main(String[] args) {
		ThreadTrain threadTrain = new ThreadTrain();
		Thread t0 = new Thread(threadTrain, "t0");
		Thread t1 = new Thread(threadTrain, "t1");
		Thread t2 = new Thread(threadTrain, "t2");
		t0.start();
		t1.start();
		t2.start();
	}

}

 如果在run方法中不加入同步代码块就会出现线程不安全的问题。

线程安全解决办法:

问:如何解决多线程之间线程安全问题

答:使用多线程之间同步synchronized或使用锁(lock)。

问:为什么使用线程同步或使用锁能解决线程安全问题呢?

答:将可能会发生数据冲突问题(线程不安全问题),只能让当前一个线程进行执行。代码执行完成后释放锁,让后才能让其他线程进行执行。这样的话就可以解决线程不安全问题。

问:什么是多线程之间同步

答:当多个线程共享同一个资源,不会受到其他线程的干扰。

问:什么是多线程同步

答:当多个线程共享同一个资源,不会受到其他线程的干扰。

下面先看看一个通过synchronized实现的线程安全例子

/**
 * 线程安全:当多个线程访问某一个类(对象或方法)时,这个对象始终都能表现出正确的行为, 那么这个类(对象或方法)就是线程安全的。
 * synchronized:可以在任意对象及方法上加锁,而加锁的这段代码称为"互斥区"或"临界区"
 * 
 * @author 4 Todo:演示线程安全与不安全,通过synchronized关键字控制
 */
public class ThreadSafe extends Thread {
	private int count = 0;

	/* synchronized */
	@Override
	public synchronized void run() {
		count++;
		System.out.println("thread: " + Thread.currentThread().getName() + " do: " + count);
	}

	public static void main(String[] args) {
		/**
		 * 分析:当多个线程访问myThread的run方法时,以排队的方式进行处理(这里排对是按照CPU分配的先后顺序而定的),
		 * 一个线程想要执行synchronized修饰的方法里的代码: 1 尝试获得锁 2
		 * 如果拿到锁,执行synchronized代码体内容;拿不到锁,这个线程就会不断的尝试获得这把锁,直到拿到为止,
		 * 而且是多个线程同时去竞争这把锁。(也就是会有锁竞争的问题)
		 */
		ThreadSafe threadSafe = new ThreadSafe();
		Thread t1 = new Thread(threadSafe, "t1");
		Thread t2 = new Thread(threadSafe, "t2");
		Thread t3 = new Thread(threadSafe, "t3");
		Thread t4 = new Thread(threadSafe, "t4");
		t1.start();
		t2.start();
		t3.start();
		t4.start();
	}
}

 也可以试着把synchronized去掉看看结果。

接下来我们看看java锁的感念!

内置的锁

Java提供了一种内置的锁机制来支持原子性

每一个Java对象都可以用作一个实现同步的锁,称为内置锁,线程进入同步代码块之前自动获取到锁,代码块执行完成正常退出或代码块中抛出异常退出时会释放掉锁

内置锁为互斥锁,即线程A获取到锁后,线程B阻塞直到线程A释放锁,线程B才能获取到同一个锁

内置锁使用synchronized关键字实现,synchronized关键字有两种用法:

1.修饰需要进行同步的方法(所有访问状态变量的方法都必须进行同步),此时充当锁的对象为调用同步方法的对象

2.同步代码块和直接使用synchronized修饰需要同步的方法是一样的,但是锁的粒度可以更细,并且充当锁的对象不一定是this,也可以是其它对象,所以使用起来更加灵活

现在我们着重对synchronized做说明!

同步代码块synchronized

就是将可能会发生线程安全问题的代码,给包括起来。

synchronized(同一个数据){

 可能会发生线程冲突问题

}

就是同步代码块 

synchronized(对象)//这个对象可以为任意对象 

    需要被同步的代码 

 

 

synchronized (this) {

			while (trainCount > 0) {
				try {
					Thread.sleep(10);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				sale();
			}
		}

对象如同锁,持有锁的线程可以在同步中执行 

没持有锁的线程即使获取CPU的执行权,也进不去 

同步的前提: 

1,必须要有两个或者两个以上的线程 

2,必须是多个线程使用同一个锁 

必须保证同步中只能有一个线程在运行 

好处:解决了多线程的安全问题 

弊端:多个线程需要判断锁,较为消耗资源、抢锁的资源。 

同步方法:

什么是同步方法?

答:在方法上修饰synchronized 称为同步方法

public synchronized void run() {
		count++;
		System.out.println("thread: " + Thread.currentThread().getName() + " do: " + count);
	}

同步方法使用的是什么锁?

答:同步函数使用this锁。

补充几点琐碎的知识点

1.原子操作(业务整体需要使用完整的synchronized,保持业务的原子性。)

/**
 * 业务整体需要使用完整的synchronized,保持业务的原子性。
 */
public class DirtyRead {
	private String name = "";
	private int age = 0;

	/* synchronized */
	public synchronized void getvalue() {
		System.out.println("name is:" + name + " age is:" + age);
	}

	public synchronized void setvalue(String name, int age) {
		this.name = name;
		try {
			Thread.sleep(2000);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		this.age = age;
		System.out.println("修改完成 " + "name is:" + name + " age is:" + age);
	}

	public static void main(String[] args) {
		DirtyRead dirtyRead = new DirtyRead();
		new Thread(new Runnable() {

			@Override
			public void run() {
				dirtyRead.setvalue("hj", 100);

			}
		}).start();
		try {
			Thread.sleep(1000);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		dirtyRead.getvalue();
	}

}

2.锁的占有问题(关键字synchronized取得的锁都是对象锁,而不是把一段代码(方法)当做锁,所以代码中哪个线程先执行synchronized关键字的方法,哪个线程就持有该方法所属对象的锁(Lock),在静态方法上加synchronized关键字,表示锁定.class类,类一级别的锁(独占.class类)。)

/**
 * 关键字synchronized取得的锁都是对象锁,而不是把一段代码(方法)当做锁,
 * 所以代码中哪个线程先执行synchronized关键字的方法,哪个线程就持有该方法所属对象的锁(Lock),
 * 在静态方法上加synchronized关键字,表示锁定.class类,类一级别的锁(独占.class类)。
 * 
 * @author 4
 *
 */

public class MultiThread {
	private static int num = 0;

	/* static */
	private synchronized void print(String tag) {
		try {
			if (tag.equals("a")) {
				num = 100;
				System.out.println("tag set a,num is: " + num);
				Thread.sleep(2000);
			} else if (tag.equals("b")) {
				num = 200;
				System.out.println("tag set b,num is: " + num);
				Thread.sleep(2000);
			}
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}

	public static void main(String[] args) {
		MultiThread m1 = new MultiThread();
		new Thread(new Runnable() {

			@Override
			public void run() {
				m1.print("a");
			}
		}, "t1").start();
		new Thread(new Runnable() {

			@Override
			public void run() {
				m1.print("b");
			}
		}, "t2").start();

	}

}

3.对象锁的同步和异步问题(只有synchronized方法或者需要同步的代码才会同步执行,否则都是异步的)

/* *
 * 对象锁的同步和异步问题
 * */
public class Myobject {
	public synchronized void method1() {
		try {
			Thread.sleep(2000);
			System.out.println("method1被执行了。。。。");
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	/** synchronized */
	public void method2() {
		System.out.println("method2被执行了。。。。");
	}

	public static void main(String[] args) {
		Myobject objectlock = new Myobject();
		new Thread(new Runnable() {

			@Override
			public void run() {
				objectlock.method1();
			}
		}).start();
		new Thread(new Runnable() {

			@Override
			public void run() {
				objectlock.method2();
			}
		}).start();

	}

}

4.synchronized的重入(1.synchronized的重入  2.synchronized通过继承可以传递子类)

/**
 * 1.synchronized的重入 <br>
 * 2.synchronized通过继承可以传递子类(省略不写了)
 */
public class SyncDubbo1 {
	public synchronized void method1() {
		System.out.println("method1..");
		try {
			Thread.sleep(1000);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		method2();
	}

	public synchronized void method2() {
		System.out.println("method2..");
		try {
			Thread.sleep(1000);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		method3();
	}

	public synchronized void method3() {
		System.out.println("method3..");
	}

	public static void main(String[] args) {
		SyncDubbo1 syncDubbo1=new SyncDubbo1();
		new Thread(new Runnable() {

			@Override
			public void run() {
				syncDubbo1.method1();
			}
		}).start();
		new Thread(new Runnable() {

			@Override
			public void run() {
				syncDubbo1.method2();

			}
		}).start();

	}

}

5.锁的粒度大小

这一块本来是在lock这一块才说明现在先提一句。锁的粒度大小会影响运行的效率,粒度越大效率越差

LOCK使用

首先看看所得类型(对象锁,类锁,任意对象锁)

/***
 * 对象锁,类锁,任意对象锁都可以枷锁
 * @author 4
 */
public class ObjectLock {
	public void method1() {
		synchronized (this) {// 对象锁
			try {
				System.out.println("do method1");
				Thread.sleep(1000);
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}

	public void method2() {
		synchronized (this.getClass()) {// 类锁
			try {
				System.out.println("do method2");
				Thread.sleep(1000);
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}

	Object objectlock = new Object();

	public void method3() {
		synchronized (objectlock) {// 任意对象锁
			try {
				System.out.println("do method3");
				Thread.sleep(1000);
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}

	public static void main(String[] args) {
		final ObjectLock objLock = new ObjectLock();
		Thread t1 = new Thread(new Runnable() {
			@Override
			public void run() {
				objLock.method3();
			}
		});
		Thread t2 = new Thread(new Runnable() {
			@Override
			public void run() {
				objLock.method3();
			}
		});
		Thread t3 = new Thread(new Runnable() {
			@Override
			public void run() {
				objLock.method2();
			}
		});

		t1.start();
		t2.start();
		t3.start();
	}

}

 再说说所得粒度大小问题:

1.同步方法<同步代码块:因为一个是进入方法内加锁的,一个是在排队方法外加锁,这样即使获得了锁,进入方法中还得分配资源需要一定的时间。

2.减小锁的大小,byte数组对象锁,推荐的写法,效率好。

private byte []lock=new byte [1]

3.减小同步代码块的大小


/**
 * 使用synchronized代码块减小锁的粒度,提高性能, synchronized包含的业务要小
 */
public class Optimize {

	public void doLongTimeTask() {
		try {

			System.out.println("当前线程开始:" + Thread.currentThread().getName() + ", 正在执行一个较长时间的业务操作,其内容不需要同步");
			Thread.sleep(2000);
			synchronized (this) {
				System.out.println("当前线程:" + Thread.currentThread().getName() + ", 执行同步代码块,对其同步变量进行操作");
				Thread.sleep(1000);
			}
			System.out.println("当前线程结束:" + Thread.currentThread().getName() + ", 执行完毕");

		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}

	public static void main(String[] args) {
		final Optimize otz = new Optimize();
		Thread t1 = new Thread(new Runnable() {
			@Override
			public void run() {
				otz.doLongTimeTask();
			}
		}, "t1");
		Thread t2 = new Thread(new Runnable() {
			@Override
			public void run() {
				otz.doLongTimeTask();
			}
		}, "t2");
		t1.start();
		t2.start();

	}

}

 锁对象改变锁失效

/*
 * 锁对象的改变,锁失效6
 * */
public class ChangeLock {
	 private String lock = new String("lock");

	private void method() {
		synchronized (lock) {
			try {
				System.out.println("当前线程 : " + Thread.currentThread().getName() + "start.....");
				// lock = new String("change lock");
				Thread.sleep(2000);
				System.out.println("当前线程 : " + Thread.currentThread().getName() + "end.....");
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}

	public static void main(String[] args) {
		ChangeLock changeLock = new ChangeLock();
		new Thread(new Runnable() {

			@Override
			public void run() {
				changeLock.method();
			}
		}, "t1").start();
		new Thread(new Runnable() {

			@Override
			public void run() {
				changeLock.method();
			}
		}, "t2").start();
		new Thread(new Runnable() {

			@Override
			public void run() {
				changeLock.method();
			}
		}, "t3").start();

	}

}

 读者可以放开注释,体验锁失效的效果

ps:不要在锁的代码块中修改锁的内容。

显示锁reentrantlock

reentrantlock主要有4种方法lock()ulock()newCondition() getHoldCount()

1.reentrantlock入门lock()ulock()方法

/**
 * @classDesc:ReentrantLock lock unlock演示
 * @author: hj
 * @date:2018年12月12日 下午1:07:27
 */

public class UseReentrantLock {

	public Lock lock = new ReentrantLock();

	public void method1() {
		try {
			lock.lock();
			System.out.println("当前线程:" + Thread.currentThread().getName() + "进入method1..");
			Thread.sleep(1000);
			System.out.println("当前线程:" + Thread.currentThread().getName() + "退出method1..");
			Thread.sleep(1000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		} finally {

			lock.unlock();
		}
	}

	public void method2() {
		try {
			lock.lock();
			System.out.println("当前线程:" + Thread.currentThread().getName() + "进入method2..");
			Thread.sleep(2000);
			System.out.println("当前线程:" + Thread.currentThread().getName() + "退出method2..");
			Thread.sleep(1000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		} finally {

			lock.unlock();
		}
	}

	public static void main(String[] args) {

		final UseReentrantLock ur = new UseReentrantLock();
		Thread t1 = new Thread(new Runnable() {
			@Override
			public void run() {
				ur.method1();
				ur.method2();
			}
		}, "t1");

		t1.start();
		try {
			Thread.sleep(10);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}

}

2.reentrantlock的newCondition()方法

/**
 * @classDesc:lock.newCondition()方法演示
 * @author: hj
 * @date:2018年12月12日 下午1:16:02
 */

public class UseCondition {
	private Lock lock = new ReentrantLock();
	private Condition condition = lock.newCondition();

	public void method1() {
		try {
			lock.lock();
			System.out.println("当前线程:" + Thread.currentThread().getName() + "进入等待状态..");
			Thread.sleep(3000);
			System.out.println("当前线程:" + Thread.currentThread().getName() + "释放锁..");
			condition.await(); // Object wait
			System.out.println("当前线程:" + Thread.currentThread().getName() + "继续执行...");
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			lock.unlock();
		}
	}

	public void method2() {
		try {
			lock.lock();
			System.out.println("当前线程:" + Thread.currentThread().getName() + "进入..");
			Thread.sleep(3000);
			System.out.println("当前线程:" + Thread.currentThread().getName() + "发出唤醒..");
			condition.signal(); // Object notify
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			lock.unlock();
		}
	}

	public static void main(String[] args) {
		final UseCondition uc = new UseCondition();
		Thread t1 = new Thread(new Runnable() {

			@Override
			public void run() {
				uc.method2();

			}
		}, "t1");
		Thread t2 = new Thread(new Runnable() {

			@Override
			public void run() {
				uc.method2();
			}
		}, "t2");
		t1.start();
		t2.start();
	}
}

3. reentrantlock的多个newCondition()方法

/**
 * @classDesc: lock.newCondition()多condition的用法
 * @author: hj
 * @date:2018年12月12日 下午1:17:48
 */

public class UseManyCondition {

	private ReentrantLock lock = new ReentrantLock();
	private Condition c1 = lock.newCondition();
	private Condition c2 = lock.newCondition();

	public void m1() {
		try {
			lock.lock();
			System.out.println("当前线程:" + Thread.currentThread().getName() + "进入方法m1等待..");
			c1.await();
			System.out.println("当前线程:" + Thread.currentThread().getName() + "方法m1继续..");
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			lock.unlock();
		}
	}

	public void m2() {
		try {
			lock.lock();
			System.out.println("当前线程:" + Thread.currentThread().getName() + "进入方法m2等待..");
			c1.await();
			System.out.println("当前线程:" + Thread.currentThread().getName() + "方法m2继续..");
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			lock.unlock();
		}
	}

	public void m3() {
		try {
			lock.lock();
			System.out.println("当前线程:" + Thread.currentThread().getName() + "进入方法m3等待..");
			c2.await();
			System.out.println("当前线程:" + Thread.currentThread().getName() + "方法m3继续..");
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			lock.unlock();
		}
	}

	public void m4() {
		try {
			lock.lock();
			System.out.println("当前线程:" + Thread.currentThread().getName() + "唤醒..");
			c1.signalAll();
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			lock.unlock();
		}
	}

	public void m5() {
		try {
			lock.lock();
			System.out.println("当前线程:" + Thread.currentThread().getName() + "唤醒..");
			c2.signal();
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			lock.unlock();
		}
	}

	public static void main(String[] args) {

		final UseManyCondition umc = new UseManyCondition();
		Thread t1 = new Thread(new Runnable() {
			@Override
			public void run() {
				umc.m1();
			}
		}, "t1");
		Thread t2 = new Thread(new Runnable() {
			@Override
			public void run() {
				umc.m2();
			}
		}, "t2");
		Thread t3 = new Thread(new Runnable() {
			@Override
			public void run() {
				umc.m3();
			}
		}, "t3");
		Thread t4 = new Thread(new Runnable() {
			@Override
			public void run() {
				umc.m4();
			}
		}, "t4");
		Thread t5 = new Thread(new Runnable() {
			@Override
			public void run() {
				umc.m5();
			}
		}, "t5");

		t1.start(); // c1
		t2.start(); // c1
		t3.start(); // c2

		try {
			Thread.sleep(2000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}

		t4.start(); // c1
		try {
			Thread.sleep(2000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		t5.start(); // c2

	}

}

4.holdCount锁的持有次数

/**
 * lock.getHoldCount()方法:只能在当前调用线程内部使用,不能再其他线程中使用
 * 那么我可以在m1方法里去调用m2方法,同时m1方法和m2方法都持有lock锁定即可 测试结果holdCount数递增
 */
public class TestHoldCount {
	// 重入锁
	private ReentrantLock lock = new ReentrantLock();

	public void m1() {
		try {
			lock.lock();
			System.out.println("进入m1方法,holdCount数为:" + lock.getHoldCount());

			// 调用m2方法
			m2();

		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			lock.unlock();
		}
	}

	public void m2() {
		try {
			lock.lock();
			System.out.println("进入m2方法,holdCount数为:" + lock.getHoldCount());
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			lock.unlock();
		}
	}

	public static void main(String[] args) {
		TestHoldCount thc = new TestHoldCount();
		thc.m1();
		thc.m2();
		System.out.println("holdCount数为:" + thc.lock.getHoldCount());
	}
}

显示锁readwritelock

主要记住:RR共享 WW、RW、WW排斥

/**
 * @classDesc: 读写锁的使用
 * @author: hj
 * @date:2018年12月12日 下午1:21:30
 */

public class UseReentrantReadWriteLock {

	private ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock();
	private ReadLock readLock = rwLock.readLock();
	private WriteLock writeLock = rwLock.writeLock();

	public void read() {
		try {
			readLock.lock();
			System.out.println("当前线程:" + Thread.currentThread().getName() + "进入...");
			Thread.sleep(3000);
			System.out.println("当前线程:" + Thread.currentThread().getName() + "退出...");
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			readLock.unlock();
		}
	}

	public void write() {
		try {
			writeLock.lock();
			System.out.println("当前线程:" + Thread.currentThread().getName() + "进入...");
			Thread.sleep(3000);
			System.out.println("当前线程:" + Thread.currentThread().getName() + "退出...");
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			writeLock.unlock();
		}
	}

	public static void main(String[] args) {

		final UseReentrantReadWriteLock urrw = new UseReentrantReadWriteLock();

		Thread t1 = new Thread(new Runnable() {
			@Override
			public void run() {
				urrw.read();
			}
		}, "t1");
		Thread t2 = new Thread(new Runnable() {
			@Override
			public void run() {
				urrw.read();
			}
		}, "t2");
		Thread t3 = new Thread(new Runnable() {
			@Override
			public void run() {
				urrw.write();
			}
		}, "t3");
		Thread t4 = new Thread(new Runnable() {
			@Override
			public void run() {
				urrw.write();
			}
		}, "t4");

		t1.start(); // RR共享
		t2.start();

		// t1.start(); // R 互斥
		// t3.start(); // W

		// t3.start();//互斥
		// t4.start();

	}
}

 

多线程死锁

什么是多线程死锁?

答:同步中嵌套同步,导致锁无法释放

/**
 * @classDesc: 死锁问题,在设计程序时就应该避免双方相互持有对方的锁的情况 演示死锁
 * @author: hj
 * @date:2018年12月12日 下午12:13:01
 */

public class DeadLock implements Runnable {

	private String tag;
	private static Object lock1 = new Object();
	private static Object lock2 = new Object();

	public void setTag(String tag) {
		this.tag = tag;
	}

	@Override
	public void run() {
		if (tag.equals("a")) {
			synchronized (lock1) {
				try {
					System.out.println("当前线程 : " + Thread.currentThread().getName() + " 进入lock1执行");
					Thread.sleep(2000);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				synchronized (lock2) {
					System.out.println("当前线程 : " + Thread.currentThread().getName() + " 进入lock2执行");
				}
			}
		}
		if (tag.equals("b")) {
			synchronized (lock2) {
				try {
					System.out.println("当前线程 : " + Thread.currentThread().getName() + " 进入lock2执行");
					Thread.sleep(2000);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				synchronized (lock1) {
					System.out.println("当前线程 : " + Thread.currentThread().getName() + " 进入lock1执行");
				}
			}
		}
	}

	public static void main(String[] args) {

		DeadLock d1 = new DeadLock();
		d1.setTag("a");
		DeadLock d2 = new DeadLock();
		d2.setTag("b");

		Thread t1 = new Thread(d1, "t1");
		Thread t2 = new Thread(d2, "t2");

		t1.start();
		try {
			Thread.sleep(500);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		t2.start();
	}
}

面试题

1.什么是线程安全

2.java内存模型

3.线程安全实现

4.同步代码的优化

5.锁的加锁、释放,以及读写锁的使用特点

6.死锁的概念

猜你喜欢

转载自blog.csdn.net/weixin_40657079/article/details/84966685
今日推荐