传统定时器和线程互斥技术

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/qq_35640274/article/details/81175878

写在前面:最近在学习多线程的部分,尝试写一写博客,整理自己的思路,有理解不到位的欢迎指正,共同进步。

线程的创建

线程的创建有两种方式:
1、直接new Thread类
2、实现Runnable接口

	Thread thread = new Thread() {
		@Override
		public void run() {
			// 保证线程一直在运行
			while (true) {
				try {
					// 睡1秒
					Thread.sleep(1000);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				System.out.println("thread:" + Thread.currentThread().getName());
			}
		}
	};
	thread.start();

	Thread thread2 = new Thread(new Runnable() {
		@Override
		public void run() {
			while (true) {
				try {
					Thread.sleep(1000);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				System.out.println("Runnable:" + Thread.currentThread().getName());
			}
		}
	});
	thread2.start();

代码会不停地打印两个线程的名称

thread:Thread-0
Runnable:Thread-1
Runnable:Thread-1
thread:Thread-0

思考题
如果new Thread的同时又实现了Runnable接口,分别重写不同run方法,会执行哪一个

	Runnable runnable = new Runnable() {
		@Override
		public void run() {
			System.out.println("runnable3");
		}
	};
	Thread thread3 = new Thread(runnable) {
		@Override
		public void run() { 
			System.out.println("thread3");
		}
	};
	thread3.start();

答案
Runnable相当于是Thread的父类,父类和子类都实现了run方法,子类会覆盖父类,所以输出是

thread3

传统的定时器

Java本身的定时器Timer通过新建一个定时器,实现一个定时任务来完成。

	new Timer().schedule(new TimerTask() {

		@Override
		public void run() {
			System.out.println("bombing");
		}
	}, 3000, 5000); //3s后爆炸,每隔5s爆炸
	//通过循环打印当前时间来观察爆炸时间
	while(true) {
		System.out.print(new Date().getSeconds() + " ");
		try {
			Thread.sleep(1000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}

输入如下:

9 10 11 bombing
12 13 14 15 16 bombing
17 18 19 20 21 bombing

思考题
如果想要实现定时器,间隔2s和4s交替爆炸,应该怎样做

答案
常规定时器执行间隔时间在创建时已经设置好了,不符合要求。考虑在一个定时器内嵌套一个另一个定时器,这时怎样控制间隔时间?利用静态属性来控制,无论新建多少对象,静态属性始终只有一个。
代码如下:

	public class MyTimerTask extends TimerTask{
		private static int num = 1; // 静态属性,一个类只有一个值
		@Override
		public void run() {
			System.out.println("num=" + num++ + " " + new Date().getSeconds() + " bombing ");
			int count = num % 2;
			try {
				Thread.sleep(count);
				// 嵌套一个定时任务
				new Timer().schedule(new MyTimerTask(), count * 2000 + 2000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		
	}
	// 主函数新建一个定时任务即可
	public static void main(String[] args) {
		new Timer().schedule(new MyTimerTask(), 2000);
	}

输出为:

num=1 14 bombing 
num=2 16 bombing 
num=3 20 bombing 
num=4 22 bombing 
num=5 26 bombing 
num=6 28 bombing 

线程互斥

下面代码实现了一个简单的字符串输出功能,一直不停的输出“zhangSan”和“liSi”

public class SynchronizedThread {

	public static void main(String[] args) {
		while (true) {
			new Thread() {
				@Override
				public void run() {
					output("zhangSan");
				}
			}.start();
			
			new Thread() {
				@Override
				public void run() {
					output("liSi");
				}
			}.start();
		}
	}
	
	public static void output(String str) {
		for (int i = 0; i < str.length(); i++) {
			System.out.print(str.charAt(i));
		}
		System.out.println();
	}
}

从输出结果中发现了异常的结果

zhangSaliSi
zhanglliSi
zhangSan

输出的顺序发生了混乱,什么导致了这种情况呢?
一个线程正在输出的过程中,资源被另一个线程占用导致当前线程等待,于是一个字符串的打印一部分又开始打印另一个字符串,便出现了乱码。如果涉及到银行账户余额的变化时,一个线程对余额的增加还没处理完成,另一个线程对余额进行扣除,就会出现严重的资金问题。所以需要线程互斥。在这里我们用加锁的方式来保证线程的原子性。
可以对类进行加锁,也可以对代码块加锁,关键字synchronized。

	public static synchronized void output2(String str) {
		for (int i = 0; i < str.length(); i++) {
			System.out.print(str.charAt(i));
		}
		System.out.println();
	}
	
	public static void output3(String str) {
		synchronized (SynchronizedThread.class) {
			for (int i = 0; i < str.length(); i++) {
				System.out.print(str.charAt(i));
			}
			System.out.println();
		}
	}

当使用线程来同时运行多个任务时,可以通过使用锁(互斥)来同步两个任务的行为,从而使得一个任务不会干涉另一个任务的资源。两个任务交替使用某种共享资源(一般是内存),可以使用互斥来使得任何时刻只有一个任务可以访问这项资源。

展开阅读全文

没有更多推荐了,返回首页