Java 多线程理解(一) 多线程技能

本文介绍了Java多线程的基本概念、实现方式、线程安全问题、sleep方法的使用,以及如何优雅地停止线程。重点讨论了线程安全的实现,线程的sleep方法与wait方法的区别,以及停止线程的各种策略。最后,文章提到了守护线程的概念及其在JVM中的角色。
摘要由CSDN通过智能技术生成

1.概念

关于多线程的基础概念不做过多概述!

线程:简单来讲,就是程序运行的最小单位,比如我们运行的main方法也是一个线程。一个进程可能有几个线程在运行计算数据。

多线程的优点:多任务操作系统的有点相信都体会过了,可以最大程度上利用CPU 的空闲时间,CPU在多个线程来回切换,大大减少单任务处理由于进程等待带来的空闲时间的CPU空闲,线程之间是相互独立的。

2.使用多线程

 想要学习一个技术就得接近它 控制它

1) 实现方式

a.继承Thread

b.实现Runnable接口

先来看看Thread的结构

public class Thread implements Runnable {

从源代码可以发现Thread实现了Runable接口,他们之间有多态关系

其实,使用继承Thread方式最大的局限就是多继承,所以为了支持多继承,完全可以实现Runnable接口,一边继承一边实现。两种方式没有本质区别,一个是重载一个重写。

public class Manager extends Thread{
	
	private List<String> queue = new LinkedList<String>();
	@Override
	public void run(){
		System.out.println();
	}
	
	public static void main(String[] args) {
		Manager m = new Manager();
		
		m.start();
	}
}

线程执行特性:

1.随机性

2.不确定性

多线程执行的先后顺序是不确定性的,调用start()方法只是告诉"线程规划器" 我已经做好执行的准备了  具体执行顺序让系统安排一个时间去执行run() 方法。start()的先后顺序不决定具体的线程执行顺序。具体代码这里就不贴了。

2) 线程安全

自定义线程类中的实例变量有共享变量和飞共享变量之分。

a.不共享的情况

各自线程有各自的实例变量相互不影响,多线程操作也能保证安全

b.共享情况

public class Manager extends Thread{
	int count = 10;
	@Override
	public void run(){
		super.run();
		count--;
		System.out.println("由"+Thread.currentThread()+"线程计算count:"+count);
	}
	public static void main(String[] args) {
		Manager m = new Manager();
		
		Thread p = new Thread(m,"Manager1");
		Thread c = new Thread(m,"Manager2");
		Thread v = new Thread(m,"Manager3");
		p.start();
		c.start();v.start();
	}
}

可以发现执行结果:
由Thread[Manager2,5,main]线程计算count:8
由Thread[Manager3,5,main]线程计算count:7
由Thread[Manager1,5,main]线程计算count:8

线程1、3对count同时进行了操作,产生非线程安全。想要的结果是依次递减打印。
i- -可以分解3步:

1.去的原有i的值;

2.计算i - -;

3.给i进行赋值;

如果多线程对着个参数进行操作,很有可能在第一步两个线程同时取到同样的数据,导致结果重复;其实很典型的列子就是消费者模式,多个消费者做在同一张桌子上吃饭,生产者端吃的。消费者必须等生产者端上来才能开始吃,也就是安排队的方式。具体怎么处理后续说明

3) sleep方法

简单介绍一下sleep()方法于我的理解,当然还有currentThread、isAlive、getId等方法,就不一一介绍了。

a)currentThread() 方法是获取当前线程对象

b)sleep() 方法是让当前线程在指定的毫秒数暂停。是指的this.currentThread()返回的线程

这里就要说到sleep和wait()的区别了:

1.sleep() 是Thread类的一个静态方法 让调用的当前线程暂停一段时间  wait 是Object 的方法

2.sleep 不会释放对象锁、放弃CPU ,wait会释放对象锁 且让出CPU

3.wait 必须依赖于synchroid ,否者会抛出java.lang.IllegalMonitorStateException 异常,先要获取对象锁 可以通过notify 、notifyall 唤醒

4) 停止线程

停止线程意味着在线程处理完任务之前停掉正在做的任务,看似简单,但必须做好防范,以便达到预期的效果。

停止线程可以用Thread.stop()  ,强制性停止当前对象线程,而且会释放锁,导致和预期不一致的结果

1) interrrupt()

并不能真正的停止线程:

public class ThreadTest extends Thread{
	@Override
	public void run(){
		for(int i = 0; i< 50000;i++){
			System.out.println("i:"+i);
		}
	}
	
	public static void main(String[] args) {
		ThreadTest test = new ThreadTest();
		test.start();
		
		test.interrupt();
		try {
			Thread.sleep(100);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		System.out.println("interrupt");
		test.stop();
	}
}

运行结果:
i:49994
i:49995
i:49996
i:49997
i:49998
i:49999
可以看出,调用interrupt() 方法后线程并没有立即停止,而是设置线程停止标志.
2)判断线程是否停止

a)Thread. interrupted()  测试当前线程是否停止 静态方法

b)this.isInterrupted() 判断线程是否停止 实例方法

那么这两个方法有什么区别呢?先来看看interrupted的解释:测试当前线程是否中断

public static void main(String[] args) {
		ThreadTest test = new ThreadTest();
		test.start();
		
		Thread.currentThread().interrupt();
		System.out.println(Thread.interrupted());
		System.out.println(Thread.interrupted());
		System.out.println("interrupt");
	}
运行结果:
true
false
interrupt
i:0
从上述结果来看 ,interrupted()的确判断出了线程是否中断状态,但为什么第二个位false呢,从官方帮助文档查阅发现:

测试当前线程是否中断,线程的中断状态由改方法清除。换句话说调用这个方法后线程的中断状态会被清除,这就是为什么第二次调用会是false。

再来看isInterrupted()方法:终端正在运行的线程,不是static方法;

public class ThreadTest extends Thread{
	@Override
	public void run(){
		for(int i = 0; i< 50000;i++){
			System.out.println("i:"+i);
		}
	}
	
	public static void main(String[] args) {
		ThreadTest test = new ThreadTest();
		test.start();
		try {
			Thread.sleep(10);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		Thread.currentThread().interrupt();
		test.interrupt();
		System.out.println(test.isInterrupted());
		System.out.println(test.isInterrupted());
		System.out.println("interrupt");
		
		test.stop();
		
	}
}
运行结果:
i:306
true
true
interrupt
可以看到并没有清除状态。

最后再来看下这两个方法的解释吧:
a) interrupted() 测试当前线程中断状态,并具有清除标志置为false的功能,静态方法

b) isinterrupted() 测试线程的终端状态,但不清状态标志  实例方法

3) 能停止的线程

据以上的内容,可以用在线程中判断线程状态的方式来停止线程:

public class ThreadTest extends Thread{
	@Override
	public void run(){
		for(int i = 0; i< 50000;i++){
			if(this.isInterrupted()){
				break;
			}
			System.out.println("i:"+i);
		}
		System.out.println("thread is end");
	}
	
	public static void main(String[] args) {
		ThreadTest test = new ThreadTest();
		test.start();
		try {
			Thread.sleep(10);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		Thread.currentThread().interrupt();
		test.interrupt();
		System.out.println(test.isInterrupted());
		System.out.println(test.isInterrupted());
		
	}
}
运行结果:
i:278
i:279
true
true
thread is end

从运行结果上看:线程跳出了循环,但是线程并没有真正的停止。如何解决呢,看下更新后的代码:

public class ThreadTest extends Thread{
	@Override
	public void run(){
		try {
			for(int i = 0; i< 50000;i++){
				if(this.isInterrupted()){
					throw new InterruptedException();
				}
				System.out.println("i:"+i);
			}
			System.out.println("thread is end");
		} catch (Exception e) {
			System.out.println("线程停止,进入catch块");
			// TODO: handle exception
		}
	}
	
	public static void main(String[] args) {
		ThreadTest test = new ThreadTest();
		test.start();
		try {
			Thread.sleep(10);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		Thread.currentThread().interrupt();
		test.interrupt();
		System.out.println(test.isInterrupted());
		System.out.println(test.isInterrupted());
		
	}
}
运行结果:
i:292
true
true
线程停止,进入catch块
由此可见线程进入异常停止!
4) 沉睡中停止

如果线程在sleep中停止呢?会发生什么情况:

public class ThreadTest extends Thread{
	@Override
	public void run(){
		try {
			Thread.sleep(200000);
			System.out.println("thread is end");
		} catch (Exception e) {
			System.out.println("线程停止,进入catch块");
			// TODO: handle exception
			e.printStackTrace();
		}
	}
	
	public static void main(String[] args) {
		ThreadTest test = new ThreadTest();
		test.start();
		try {
			Thread.sleep(1000);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		test.interrupt();
		System.out.println(test.isInterrupted());
		System.out.println(test.isInterrupted());
		
	}
}
运行结果:

false
false
线程停止,进入catch块
java.lang.InterruptedException: sleep interrupted
	at java.lang.Thread.sleep(Native Method)
	at com.wyj.design.consumer.ThreadTest.run(ThreadTest.java:7)
从以上结果来看,在沉睡中停止线程,会进入到catch(),且清除了线程运行状态标志。sleep不会释放线程锁及CPU,导致中断抛出异常

相反如果打断后在沉睡呢:

i:199998
i:199999
线程停止,进入catch块
java.lang.InterruptedException: sleep interrupted
	at java.lang.Thread.sleep(Native Method)
	at com.wyj.design.consumer.ThreadTest.run(ThreadTest.java:11)
从以上结果来看,先中断线程,在沉睡会进入到catch()。
5) 能停止的线程

暴力的停止线程stop().

调用stop() 会抛出ThreadDeth 异常,通常情况下不需要现行的捕捉。

stop 方法已被JDK标记为过期的方法作废

1.stop方法会释放锁 可能会导致线程安全

2.异常停止线程导致结束流程没有执行

5) 守护线程

在java线程中有两种线程:用户线程、守护线程

守护线程是一种特殊的线程,随着用户线程,在JVM中只要有一个非守护线程,守护线程就在工作,最常见的是垃圾回收线程。JVM所有线程结束了,垃圾回收线程也就结束了。

public class ThreadTest extends Thread{
	@Override
	public void run(){
		try {
			int i = 0;
			while(true){
				
				System.out.println("i:"+i++);
				Thread.sleep(1000);
			}
		} catch (Exception e) {
			System.out.println("线程停止,进入catch块");
			// TODO: handle exception
			e.printStackTrace();
		}
	}
	
	public static void main(String[] args) {
		ThreadTest test = new ThreadTest();
		test.setDaemon(true);
		test.start();
		try {
			Thread.sleep(1000);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}
运行结果:

i:0
i:1

用户线程结束了,守护线程也就结束了。

3 小结

本文介绍了Thread的一些基础知识。在使用线程的时候可能会发生很多意想不到的情况,这也是多线程一些不可预知的的一个体现,需要学习这个基础知识和一些常用的情况来规避这些问题。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值