Thread

守护线程

  1. 定义:守护线程(即daemon thread),是个服务线程,准确地来说就是服务其他的线程.
    当其他非守护线程停止,他才停止。
public class zuoye2 {
	public static void main(String[] args) throws InterruptedException {
		//2. 创建一个后台守护线程,在启动线程之前设置是守护的!
		Thread t = new Thread(new Runnable() {
			@Override
			public void run() {
				while(true) {
					try {
						Date date = new Date();
						System.out.println(date);
						Thread.sleep(5000);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
			}
		});
		t.setDaemon(true);
		t.start();
		
		System.out.println(Thread.currentThread() + " 开始休眠60秒...");
		Thread.sleep(60000);
		System.out.println(Thread.currentThread() + " 休眠60秒结束。");
		System.out.println(Thread.currentThread() + " 前台线程结束。");
	}
Thread[main,5,main] 开始休眠60秒...
Wed Mar 25 18:15:41 CST 2020
Wed Mar 25 18:15:46 CST 2020
Wed Mar 25 18:15:51 CST 2020
Wed Mar 25 18:15:56 CST 2020
Wed Mar 25 18:16:01 CST 2020
Wed Mar 25 18:16:06 CST 2020
Wed Mar 25 18:16:11 CST 2020
Wed Mar 25 18:16:16 CST 2020
Wed Mar 25 18:16:21 CST 2020
Wed Mar 25 18:16:26 CST 2020
Wed Mar 25 18:16:31 CST 2020
Wed Mar 25 18:16:36 CST 2020
Thread[main,5,main] 休眠60秒结束。
Thread[main,5,main] 前台线程结束。

线程状态

在这里插入图片描述1. 线程状态:
New状态:使用new创建出一个线程
Runnable状态:
New状态时,调用t.start(),进入Runnable状态;
所有线程在Runnable状态中等待,被系统调度分配时间片,进入Running状态
Running状态:
所有线程只能从Runnable状态,才可进入到Running状态
只能有一个线程处于Running状态,进行数逻运算
当时间片用完时,正在执行的线程会被强制剥离出cpu,进入Runnable状态,等待下一次调度
线程就是在Running和Runnable状态之间,来回地切换,完成线程中的工作任务
当线程的工作任务都执行完时,从Running状态,切换到Dead状态(线程对象等待GC,结束)
Sleep Block状态:
当线程处于Running时,调用了Thread.sleep(),当前线程会进入休眠阻塞状态
当休眠时间结束了/超时,线程会由休眠阻塞状态,切换到Runnable状态
线程休眠时,被调用了interrupt()方法,打断了休眠,也切换到Runnable状态

Interrupt

使用interrupt()方法中断/打断 运行中的线程
1. 可用于实现 线程间的协作交互

2. 类似于这样的场景:
1)某个员工正在睡觉,被老板喊醒,继续工作!(ThreadDemo08)
2)某个员工正在工作,但现在已经下班了,被老板喊了下:下班了,不要再coding了!
当前这个员工,或收到这个中断的信息!

3. API方法
public void interrupt()
1)该方法可打断阻塞的线程,让其不再阻塞,设置中断标志为true,并在阻塞行抛出异常
2)对于正在运行中的线程,调用interrupt()方法,无法终止结束该线程!
只是在对应线程上,设置修改了中断标志为true

public boolean isInterrupted() {
返回是否被中断了,返回被中断了返回true,否则false
底层方法实现的代码是:
return isInterrupted(false);
//传递false,说明不清除中断标志

public static boolean interrupted() {
返回是否被中断了,返回被中断了返回true,否则false
同时reset清除当前线程的中断标志,设为false,没有被中断过
底层方法实现的代码是:
return currentThread().isInterrupted(true);
//传递true,说明要清除中断标志

public static void main(String[] args) throws InterruptedException {
		
		Thread t1 = new Thread(new Runnable() {
			@Override
			public void run() {
				System.out.println("t1线程开始工作...");
				while(true) {
					System.out.println("t1线程在工作...");
					
					//通过isInterrupted()方法,获得当前线程是否被别的线程中断过
					boolean flag = Thread.currentThread().isInterrupted();
					System.out.println("t1线程是否被中断过?" + flag);
					if(flag == true) {
						break;
					}
				}
				System.out.println("t1线程工作结束。");
			}
		});
		t1.start();
		System.out.println("主线程启动了 t1 线程");
		
		Thread.sleep(1000);
		System.out.println("主线程休眠1秒后,打断t1线程");
		t1.interrupt();
		System.out.println("主线程结束。");
	}
t1线程在工作...
t1线程是否被中断过?false
t1线程在工作...
t1线程是否被中断过?false
t1线程在工作...
t1线程是否被中断过?false
t1线程在工作...
t1线程是否被中断过?false
t1线程在工作...
t1线程是否被中断过?false
t1线程在工作...
t1线程是否被中断过?false
t1线程在工作...
t1线程是否被中断过?false
t1线程在工作...
主线程休眠1秒后,打断t1线程
t1线程是否被中断过?false
主线程结束。
t1线程在工作...
t1线程是否被中断过?true
t1线程工作结束。

IO Block

IO Block IO阻塞状态
1.当发生IO阻塞时,会从Running状态转换到IO Block状态
当数据读写完毕准备好之后,线程不再阻塞,进入Runnable状态
2.当程序中设计资源读写时,可提前缓冲准备好
BufferedInputStream、BufferedOutputStream
BufferedReader、PrintWriter
ByteArrayOutputStream
Queue(消息队列
3.IOBlock状态,当IO完成时,会切换到Runnable状态。
IO阻塞时,不会被Interrupt()方法打断!
sleep()、join()和wait(),会被Interrupt()方法打断。

public static void main(String[] args) throws InterruptedException {
	
	Thread t1 = new Thread(new Runnable() {
		
		@Override
		public void run() {
			Scanner sc = new Scanner(System.in);
			System.out.println("t1线程 请输入一个整数:");
			int num = sc.nextInt();	//IO BLOCK
			
			System.out.println("t1线程 输入的整数是:" + num);
			sc.close();
		}
	});
	t1.start();
	System.out.println("主线程 启动了t1线程");//debug
	
	Thread.sleep(3000);
	t1.interrupt();
}
主线程 启动了t1线程
t1线程 请输入一个整数:
90
t1线程 输入的整数是:90

Join

t.join()方法
1.当前线程中执行t.join()代码;
当前线程进入wait block状态,等待t线程终止;
t线程结束,当前线程才向下继续执行
2.项目中,需要其他几件事情完成后,才继续向下执行,可使用Join()方法
实战时,可使用CountDownLatch工具,才继续向下执行,可使用join()方法
movie.part-01.rar
movie.part-02.rar == 解压缩 ==》movie.avi
movie.part-03.rar

private static int num=0;   //t1线程读取数据
	private static double result=0.0;//t2线程计算得到的结果
	
	 public static void main(String[] args) throws InterruptedException {			
		 //模拟读取资源
  	   Thread t1=new Thread(new Runnable(){
  		   @Override
  		public void run() {
  			Scanner sc=new Scanner(System.in);
  			System.out.println("t1线程:请输入一个整数:");
  		  num=sc.nextInt();//IO Block
  			System.out.println("t1线程: 输入的整数是:"+num);
  			sc.close();
  		}		   
  	   });
  	   t1.start();
  	   //模拟计算线程
  	   Thread t2=new Thread(new Runnable(){
  		   @Override
  		public void run() {
  			try {
				t1.join();//t1执行的任务,要插入进来/加入进来
			} catch (InterruptedException e) {
			
				e.printStackTrace();
			}
  			System.out.println("t2线程 获得数据:" + num);
            System.out.println("t2线程 开始计算。。。");
  			System.out.println("t2线程 得到计算结果...");
  			result=Math.PI*num*num;
  		}
  	   });
  	   t2.start();
  	   //模拟输出结果
  	   Thread t3 = new Thread(new Runnable() {
		@Override
		public void run() {
			try {
				t2.join();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			System.out.println("t3线程 计算结果是:" + result);
		}	   
  	   });
  	   t3.start(); 
  	   //测试使用interrupt()方法,打断t2线程join()方法的wait block状态
	  // t2.interrupt();
		/*  at java.lang.Object.wait(Native Method)
		 	at java.lang.Thread.join(Unknown Source)
		 	at java.lang.Thread.join(Unknown Source)
		 	at day28.ThreadDemo11$2.run(ThreadDemo11.java:40)
		 	at java.lang.Thread.run(Unknown Source)
		 t2线程 获得数据:0
		 t2线程 开始计算。。。
		 t2线程 得到计算结果...
		 t3线程 计算结果是:0.0
		 t1线程:请输入一个整数:
		 9 
		 t1线程: 输入的整数是:9
		 主线程 整个程序执行结束!
*/
  	   //主线程t1 t2 t3 都执行完毕后
  	   t1.join();
  	   t2.join();
  	   t3.join();
  	   System.out.println("主线程 整个程序执行结束!");
	}
模拟输出结果
	t1线程:请输入一个整数:7
	t1线程: 输入的整数是:7
	t2线程 获得数据:7
	t2线程 开始计算。。。
	t2线程 得到计算结果...

Yield

Thread.yield()
1.该方法执行后,当前线程会主动让出CPU,由running状态切换到runnable状态
2.只用于教学,实战项目中,不要用!

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

			@Override
			public void run() {
				for(int i = 0; i < 100; i++) {
					System.out.println("你是谁?");
					Thread.yield();
				}
			}
		});
		Thread t2 = new Thread(new Runnable() {

			@Override
			public void run() {
				for(int i = 0; i < 100; i++) {
					System.out.println("我是查水表的");
					Thread.yield();
				}
			}
		});
		t1.start();
		t2.start();
	}
你是谁?
我是查水表的
你是谁?
我是查水表的
你是谁?
我是查水表的
你是谁?
我是查水表的
你是谁?
我是查水表的
你是谁?
我是查水表的
你是谁?
我是查水表的
你是谁?
我是查水表的
你是谁?
我是查水表的...

同步和异步操作

如何解决线程安全问题,需要将异步的操作 转变为 同步操作
1)异步操作:
多个线程并发运行操作代码,各线程各干各的,自己做自己的,没有交互讨论
也可解释为:多行代码执行的步调不一致,多行代码没有一起一次性都执行完毕
2)同步操作:
线程执行有先后顺序,我先都做完操作,我离开;你再来做所有操作,离开;别人再来
也可解释为:多行代码的步调保持一致,多行代码会一起同时一次性都执行完毕

Java中,可使用 synchronized关键字,实现同步操作
1.使用Synchronized,实现代码的同步操作,保证了代码原子性操作
即,同步代码块中的代码,要么都执行,要么都不执行
同步代码块在执行时,可以休眠sleep(),其他线程可以进入执行这个代码块吗?不行!原子性的!
2. 线程安全是指,多个线程并发访问一段代码,不会出现问题,即,认为是线程安全的
3. 在构造同步代码时,需要选择合适的对象作为参数传入
要求:每个线程使用的/看到的对象,都是同一个对象;否则的话,同步的功能会失效
常见的方案有:
(1)执行任务中,定义一个实例变量
private Object obj = new Object;
(2)直接使用当前对象this
(3)定义一个静态的对象
private static Object staticobj = new Object();
4. 锁机制
Java中每个对象身上,都有一把锁(同步监视锁)
我们将多个线程使用的/看到的那个对象,当作锁来使用!
当线程要执行这块代码块时,先获得对象上的锁,再执行同步代码快中的代码;
在执行同步代码块的过程中,如果其他线程想执行这个代码块,
必须等待,等正在执行的线程执行完毕,释放了锁,
才可能竞争得到这个锁,才可进入代码块运行!
提示:
(1)当线程执行时,进入同步代码块,会自动获得所
(2)在退出同步代码块时,会自动释放锁
5.同步方法
(1)如果方法中所有的代码,都被synchronized同步代码块括起来了!
可将synchronized关键字提升写在方法的签名中,去除方法中的同步代码块!
public synchronized void run() {
(2)同步的方法,是指方法中的代码要么都执行,要么都不执行(原子性)
(3)同步方法,使用的锁对象是:this对象(只有一个执行任务对象,被多个线程构造同时使用)
实战时,避免将整个方法都锁住!以免降低线程运行的效率!
如果所有方法都是同步的,在同一时刻,只会/只能有一个线程在执行该类中的某一个方法;
其他方法不让执行,因为其他的线程,在等待该对象锁this!
在某个时刻,只能有一个被执行调用,效率太低了!
这些同步的方法是互斥调用的!
不要使用StringBuffer, 推荐使用StringBuilder
Hashtable HashMap{get(key);size();}
Vector ArrayList
实战时,所在环境,绝大多数情况不会出现线程并发安全问题!
因此,就不需要总想着使用这些特殊的类(Vector…)
强烈推荐使用,性能更高的类!
6. 同步的范围
(1)在保证并发访问安全的情况下,尽可能缩小范围
以保证不需要被同步的代码行,可以被并发执行访问!以使得提高运行效率!
(2)一般在保证对临界资源原子性操作即可;
或 对不可分割的代码操作,保证原子性操作即可

public class ThreadDemo14 {
	public static void main(String[] args) throws InterruptedException {
		
		RunnableTask task = new RunnableTask();
		Thread t1 = new Thread(task, "t1");
		Thread t2 = new Thread(task, "t2");
		Thread t3 = new Thread(task, "t3");
		Thread t4 = new Thread(task, "t4");
		Thread t5 = new Thread(task, "t5");
		
		t1.start(); t2.start(); t3.start(); t4.start(); t5.start();
		//等待五个线程都执行完毕后,打印输出count
		t1.join(); t2.join(); t3.join(); t4.join(); t5.join();
		System.out.println("五个线程都执行后,count: " + task.getCount());
	}
}
class RunnableTask implements Runnable {
	//对象的属性
	private int count = 5;
	private Object obj=new Object(); //interrupt() blockerlock
	private static Object staticobj = new Object();
	
	/*
	 * Object myobj = new Object();
	 * synchronized(myobj)		//错误演示,每个线程都使用自己的一个新对象
	 */
	//synchronized(obj){		//多个线程使用同一个对象
	//synchronized(this) {		//多个线程使用了,同一个执行任务对象
	
	/*
	@Override
	public void run() {
	
		synchronized(staticobj) {	//多个线程使用了RunnableTask.staticobj对象
			count --;
			Thread.yield();  //用于测试,实战时不要用
			System.out.println(
					Thread.currentThread().getName() + " count:" + count);
		}
	}
	*/
	/*
	@Override
	public synchronized void run() {	//此时用到的锁是:this 多个线程使用同一个this对象
			count --;
			Thread.yield();  //用于测试,实战时不要用
			System.out.println(
					Thread.currentThread().getName() + " count:" + count);
	}
	*/
	
	@Override
	public void run() {
		String tname = Thread.currentThread().getName();
		System.out.println(tname + "开始执行任务代码。。。。");
		synchronized(obj) {
			count --;
			Thread.yield();  //用于测试,实战时不要用
			System.out.println(
					Thread.currentThread().getName() + " count:" + count);
		}
		System.out.println(tname + "执行代码任务结束。");
	}
	public int getCount() {
		return count;
	}
}
```c
t1 count:4
t4 count:3
t5 count:2
t3 count:1
t2 count:0
五个线程都执行后,count: 0

同步的静态方法

  1. 使用synchronized修饰静态方法,保证方法的同步
  2. 如果工具类中,所有的静态方法都是synchronized修饰的,
    这些方法再调用的时候是互斥的!调用了A方法,就不再允许调用B方法、C方法。。。等,更不可能允许调用A方法
  3. 此时用到的锁对象是:TestUtil.class对应的Class对象
    静态方法所在类的 类对象
    TestUtil.class 对应的class对象
public class ThreadDemo15 {
	public static void main(String[] args) {
		Thread t1 = new Thread(new Runnable() {

			@Override
			public void run() {
				for(int i = 0; i < 10; i++) {
					TestUtil.methodA();
				}
				
			}
			
		},"t1");
		Thread t2 = new Thread(new Runnable() {

			@Override
			public void run() {
				for(int i = 0; i < 10; i++) {
					TestUtil.methodB();
				}
				
			}
			
		},"t2");
		
		t1.start();
		t2.start();
		
	}

}
/**
 * 
 * 测试一个工具类,多个共聚方法访问临界资源
 *
 */
class TestUtil{
	public static int count = 0;
	
	public synchronized static void methodA() {
		count ++;
		Thread.yield();
		System.out.println(Thread.currentThread().getName()
				+ "执行methodA()方法 count:" + count);
	}
	
	public synchronized static void methodB() {
		count ++;
		Thread.yield();
		System.out.println(Thread.currentThread().getName()
				+ "执行methodA()方法 count:" + count);
	}
}

wait()和notify()方法

wait()方法

  1. 常用推荐的写法:
synchronized (obj) {
			while(!finished) {		//<--结束等待的条件,避免虚假唤醒(被中断)
				try {
					obj.wait();//进入wait block,并且释放锁!
				} catch (InterruptedException e) {
				}
			}
		}
  1. wait() 和 notify()方法,需要基于同一个对象进行 同步锁操作 及 等待唤醒操作
    处于wait()的线程,可能有多个;唤醒时,可能要唤醒多个线程;因此需要同步锁的支持
    wait()方法
  2. 必须写在一个带有结束条件判断的,无限循环中,避免被中断,虚假唤醒
  3. 当执行了wait()方法后,执行obj.wait()方法的那个线程,会进入wait block状态,
    直到被 另一个线程基于同一个对象 调用了notify()或notifyAll()方法 唤醒!
  4. 线程进行wait block状态的同时,会将线程所持有的锁释放!
    notify()方法
  5. 通知唤醒基于同一个对象上,调用wait()方法的线程
    对于有多个线程,基于同一个对象等待时,会唤醒其中任意一个线程
  6. wait等待的线程,被唤醒后,wait()方法后如果还有需要同步执行的代码,
    不会立即被执行到!!!需要竞争获得到锁,才可继续执行代码!
    绝大多数情况下,会从wait block队列(wait pool),切换到 obj同步锁的等待队列(lock pool)中
    notifyAll()方法
    通知唤醒基于同一个对象上,调用wait()方法的所有线程
public class ThreadDemo16 {
	// 定义一个对象,当作监视器锁来使用
	// 也作为 wait() 和 notfiy() 方法的结合点/集合点
	private static Object obj = new Object();
	private static boolean finished = false; //表示图片是否下载完毕
	public static void main(String[] args) throws InterruptedException {
		//下载线程,专门负责资源的下载:图片、网页、视频等
		//  当图片下载成功后,通知唤醒显示线程,来显示图片;
		//  下载线程继续下载其它东西
		Thread downloadThread = new Thread(new Runnable() {
			@Override
			public void run() {
				System.out.println("下载线程 开始下载图片");
				try {Thread.sleep(2000);} catch (InterruptedException e) {}
				System.out.println("下载线程 下载图片成功。");
				finished = true;	
				synchronized (obj) {
					System.out.println("下载线程 通知唤醒显示线程。");
					obj.notify();
				}		
			System.out.println("下载线程 继续下载其他资源...");
				
			}
		});	
		//显示线程,会等待下载线程将图片下载完毕后,立即显示图片
		Thread showThread = new Thread(new Runnable() {
			@Override
			public void run() {
				System.out.println("显示线程 准备开始显示图片...");
				//不成熟方案:join不合适;while(true){sleep(5000);}会消耗资源,效率不高。
				//推荐使用以下方案:

				//wait()和notify()方法,必须在同步代码块中!
				//wait()和notify()方法,都是基于所在同步块的锁对象上,进行操作的
				synchronized (obj) {
					while(!finished) {		//<--结束等待的条件,避免虚假唤醒(被中断)
						System.out.println("显示线程 等待下载线程通知图片下载成功...");
						try {
							obj.wait();//进入wait block,并且释放锁!
						} catch (InterruptedException e) {
							System.out.println("显示线程 在等待时,被中断了。");
						}
					}
					System.out.println("显示线程 被通知,图片已经下载成功,唤醒了,结束了阻塞等待。");
				}

				System.out.println("显示线程 显示图片。");
			}
		});
		downloadThread.start();
		showThread.start();
		
		//主线程中,休眠一秒后,打断显示线程
		Thread.sleep(1000);
		System.out.println("主线程 打断下显示线程");
		showThread.interrupt();
	}
}
下载线程 开始下载图片
显示线程 准备开始显示图片...
显示线程 等待下载线程通知图片下载成功...
主线程 打断下显示线程
显示线程 在等待时,被中断了。
显示线程 等待下载线程通知图片下载成功...
下载线程 下载图片成功。
下载线程 通知唤醒显示线程。
下载线程 继续下载其他资源...
显示线程 被通知,图片已经下载成功,唤醒了,结束了阻塞等待。
显示线程 显示图片。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值