线程同步和死锁和单例设计模式理解

线程与同步

什么是同步
同步就是加锁,不让其它人访问
synchronized指的就是同步的意思

什么情况下需要同步
当多线程并发, 我们希望某一段代码执行的过程中CPU不要切换到其他线程工作. 这时就需要同步,否则会有线程安全问题.

同步代码块
1 使用synchronized关键字加上一个锁对象来定义一段代码, 这就叫同步代码块
2 多个同步代码块如果使用相同的锁对象, 那么他们就是同步的
3 使用同步锁时,应该尽是让锁的范围小点,才能提高性能

同步方法
1 使用synchronized关键字修饰一个方法, 该方法中所有的代码都是同步的
2 非静态同步方法的锁是:this
3 静态同步方法的锁是:字节码对象(xx.class)

例子:火车站售票

public class{

	public static void main(String[] args) {
		// 生成任务
		Task task = new Task();
		Thread a = new Thread(task);
		a.setName("a售票口");
		Thread b = new Thread(task);
		b.setName("b售票口");
		Thread c = new Thread(task);
		c.setName("c售票口");
		Thread d = new Thread(task);
		d.setName("d售票口");
		// 开始线程 多线程是抢占式的 
		a.start();
		b.start();
		c.start();
		d.start();

	}

}

//买票任务
class Task implements Runnable {
	int ticket = 100;

//	    使用synchronized关键字修饰一个方法, 该方法中所有的代码都是同步的
//	非静态同步方法的锁是:this
//	静态同步方法的锁是:字节码对象(xx.class)
	@Override
	public synchronized void run() {

		// 同步代码块,锁最好同一个对象,如果不是同一对象,还是会有线程安全问题
		// * 锁:this,代表当前对象
		// * 锁:如果 new 对象,就不是同一把锁
		// * 锁:字节码对象例如:String.class,内存中,只有一个字节码对象
		// synchronized (this) {
		while (true) {
			if (ticket <= 0) {
				System.out.println(Thread.currentThread() + "票卖完了");
				break;
			} else {
				System.out.println(Thread.currentThread() + "抢到票了 票号:" + ticket);
				ticket--;
			}
		}
	}
	// }

}

执行效果
在这里插入图片描述
锁的总结
同步代码块,锁最好同一个对象,如果不是同一对象,还是会有线程安全问题

  • 锁:this,代表当前对象
  • 锁:如果 new 对象,就不是同一把锁
  • 锁:字节码对象 例如:String.class,内存中,只有一个字节码对象
  • 如果锁对象是唯一,线程执行时就会独占该锁,一直到锁包含的代码块执行完才将锁释放,此后被阻塞的线程方能获得该锁,从而重新进入可执行状态。

死锁
多线程以及多进程改善了系统资源的利用率并提高了系统 的处理能力。然而,并发执行也带来了新的问题——死锁。所谓死锁是指多个线程因竞争资源而造成的一种僵局(互相等待),若无外力作用,这些进程都将无法向前推进。
简单来说:死锁就是大家都抱着锁,不释放锁

一个简单的锁案例

package 线程与同步;

public class 死锁 {

	public static void main(String[] args) {

		String s1 = "筷子a";
		String s2 = "筷子b";

		new Thread() {
			public void run() {

				while (true) {
					//s1对象锁
					synchronized (s1) {
						System.out.println("线程a拿到了" + s1 + " 需要" + s2);
						//s2对象锁
						synchronized (s2) {
							System.out.println("线程a拿到了" + s2 + " 开始吃饭");
						}
					}

				}
			};
		}.start();
		new Thread() {
			public void run() {

				while (true) {
					//s2对象锁
					synchronized (s2) {
						System.out.println("线程b拿到了" + s2 + " 需要" + s1);
						//s1对象锁
						synchronized (s1) {
							System.out.println("线程b拿到了" + s1 + " 开始吃饭");
						}
					}

				}
			};
		}.start();

	}

}

效果图
在这里插入图片描述原因:
当a线程占用着s1对象锁 需要s2对象锁执行后面代码时,b线程占用着s2对象锁 需要s1对象锁执行后面代码。两个线程各自占有对方想要的资源 不肯释放就造成了死锁。

回顾线程安全的类
Vector,StringBuffer,Hashtable
Vector是线程安全的,ArrayList是线程不安全的 (具体可以看源码)
StringBuffer是线程安全的,StringBuilder是线程不安全的 (具体可以看源码)
Hashtable是线程安全的,HashMap是线程不安全的(具体可以看源码)

单例设计模式

什么是单例
保证类在内存中只有一个对象。
对象是new出来的,因些也就是说在程序中只能new一次对象

单例实现的基本步骤
1 声明一个类,类中有一个静态属性,类型与类名相同
2 把空参构造方法声明为私有
3 在类中提供一个公共静态访问方法来返回该对象实例

单例的多种写法

//一饿汉式
//class Singleton {
//
//	private static Singleton singleton = new Singleton();
//
//	private Singleton() {
//
//	}
//
//	public static Singleton getSingleton() {
//		return singleton;
//	}
//}

//二懒汉式
//class Singleton {
//
//	private static Singleton singleton;
//
//	private Singleton() {
//
//	}
//
//	public static Singleton getSingleton() {
//		if (singleton == null)
//			singleton = new Singleton();
//		return singleton;
//	}
//}

//三简单方式
class Singleton {

	public  final  static  Singleton singleton = new Singleton();

	private Singleton() {

	}

}

饿汉式和懒汉式的区别
饿汉式是空间换时间,懒汉式是时间换空间
在多线程访问时,饿汉式不会创建多个对象,而懒汉式有可能会创建多个对象
如果考虑线程安全问题,用饿汉式
如果不考虑线程安全问题,用懒汉式

Runtime类的使用
Runtime类是一个单例类
每个 Java 应用程序都有一个 Runtime 类实例,使应用程序能够与其运行的环境相连接。通过 getRuntime 方法获取当前运行时。
案例:自动关机
Runtime r = Runtime.getRuntime();
r.exec(“shutdown -s -t 300”);//300秒后关机
r.exec(“shutdown -a”); //取消关机

Timer定时器
Timer一种工具,用于在后台线程中执行的任务。可安排任务执行一次,或者定期重复执行。
方法
public void schedule(TimerTask task, long delay)
public void schedule(TimerTask task, long delay, long period)
public void schedule(TimerTask task, Date firstTime, long period)

public class 定时器 {

	public static void main(String[] args) {
		
       Timer timer=new  Timer();
       //内部会根据new TimerTask() 创建子线程
       timer.schedule(new  TimerTask() {
		
		@Override
		public void run() {
			System.out.println(Thread.currentThread()+"执行了任务A");
			
		}
	}, 3000);
	}

}

ps: Thread[Timer-0,5,main]执行了任务A
{ Timer:线程名
5:优先级
main:该线程所在线程组}

发布了23 篇原创文章 · 获赞 17 · 访问量 1279

猜你喜欢

转载自blog.csdn.net/qq_41587409/article/details/105472978