多线程相关

1 多线程内存图解

在这里插入图片描述

2 Thread类

  1. 构造方法:
    publicThread():分配一个新的线程对象。
    publicThread(Stringname):分配一个指定名字的新的线程对象。
    publicThread(Runnabletarget):分配一个带有指定目标新的线程对象。
    publicThread(Runnabletarget,Stringname):分配一个带有指定目标新的线程对象并指定名字。
  2. 常用方法:
    publicString getName():获取当前线程名称。
    publicvoid start():导致此线程开始执行;Java虚拟机调用此线程的run方法。
    publicvoid run():此线程要执行的任务在此处定义代码。
    public static void sleep(longmillis):使当前正在执行的线程以指定的毫秒数暂停(暂时停止执行)。
    public staticThread currentThread():返回对当前正在执行的线程对象的引用。

3 Thread和Runnable的区别

如果一个类继承Thread,则不适合资源共享。但是如果实现了Runable接口的话,则很容易的实现资源共享。

  • 总结:
    实现Runnable接口比继承Thread类所具有的优势:
    1.适合多个相同的程序代码的线程去共享同一个资源。
    2.可以避免java中的单继承的局限性。
    3.增加程序的健壮性,实现解耦操作,代码可以被多个线程共享,代码和线程独立。
    4.线程池只能放入实现Runable或Callable类线程,不能直接放入继承Thread的类。
  • 扩充:在java中,每次程序运行至少启动2个线程。一个是main线程,一个是垃圾收集线程。因为每当使用java命令执行一个类的时候,实际上都会启动一个JVM,每一个JVM其实在就是在操作系统中启动了一个进程。

4 线程的调度方式

  1. 分时调度:平均分配执行时间。
  2. 抢占式调度:根据优先级进行,优先级一样则随机选择一天线程,然后cpu告高速切换,java使用此方式。
  3. 协同式调度:我们使用线程自己控制,但是此方式有奉献,一条线程阻塞会影响我们其他的线程执行,导致整个程序崩溃。

5 线程安全问题

  1. 线程安全问产生的原因
    在这里插入图片描述
  2. 解决方式
    a 使用同步代码块
    synchronize(锁对象){
    //需要同步的代码片段
    }
    b 使用同步方法:它的锁对象是this也就会当前类对象
    public synchronized void test01(){
    //需要同步的代码片段
    }
    c 静态同步方法:锁对象是class文件对象
    public static synchronized void test01(){
    //需要同步的代码片段
    }
    d Lock锁
    Lock lock = new ReentrantLock();
    lock.lock();//获取锁
    //需要同步的代码
    lock.unlock();//释放锁

6线程同步的原理

在这里插入图片描述

7 线程的状态

  1. 新建(NEW):新创建了一个线程对象。
  2. 可运行(RUNNABLE):线程对象创建后,其他线程(比如main线程)调用了该对象的start()方法。该状态的线程位于可运行线程池中,等待被线程调度选中,获取cpu 的使用权 。
  3. 运行(RUNNING):可运行状态(runnable)的线程获得了cpu 时间片(timeslice) ,执行程序代码。
  4. 阻塞(BLOCKED):阻塞状态是指线程因为某种原因放弃了cpu 使用权,也即让出了cpu timeslice,暂时停止运行。直到线程进入可运行(runnable)状态,才有机会再次获得cpu timeslice 转到运行(running)状态。阻塞的情况分三种:
     a 等待阻塞:运行(running)的线程执行o.wait()方法,JVM会把该线程放入等待队列(waitting queue)中。
     b 同步阻塞:运行(running)的线程在获取对象的同步锁时,若该同步锁被别的线程占用,则JVM会把该线程放入锁池(lock pool)中。
     c 其他阻塞:运行(running)的线程执行Thread.sleep(long ms)或t.join()方法,或者发出了I/O请求时,JVM会把该线程置为阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入可运行(runnable)状态。
  5. 死亡(DEAD):线程run()、main() 方法执行结束,或者因异常退出了run()方法,则该线程结束生命周期。死亡的线程不可再次复生
    在这里插入图片描述
    几个常用方法的比较:
  6. Thread.sleep(long millis),一定是当前线程调用此方法,当前线程进入阻塞,但不释放对象锁,millis后线程自动苏醒进入可运行状态。作用:给其它线程执行机会的最佳方式。
  7. Thread.yield(),一定是当前线程调用此方法,当前线程放弃获取的cpu时间片,由运行状态变会可运行状态,让OS再次选择线程。作用:让相同优先级的线程轮流执行,但并不保证一定会轮流执行。实际中无法保证yield()达到让步目的,因为让步的线程还有可能被线程调度程序再次选中。Thread.yield()不会导致阻塞。
  8. t.join()/t.join(long millis),当前线程里调用其它线程1的join方法,当前线程阻塞,但不释放对象锁,直到线程1执行完毕或者millis时间到,当前线程进入可运行状态。
  9. obj.wait(),当前线程调用对象的wait()方法,当前线程释放对象锁,进入等待队列。依靠notify()/notifyAll()唤醒或者wait(long timeout)timeout时间到自动唤醒。
    5.obj.notify()唤醒在此对象监视器上等待的单个线程,选择是任意性的。notifyAll()唤醒在此对象监视器上等待的所有线程。

8 等待与唤醒机制

  1. 等待与唤醒机制:线程之间的通信手段,多个线程在处理同一个资源,并且任务不同时,需要线程通信来帮助解决线程之间对同一个变量的使用或操作。就是多个线程在操作同一份数据时,避免对同一共享变量的争夺。也就是我们需要通过一定的手段使各个线程能有效的利用资源。
  2. 作用:操作同一个资源的时候,提高有效利用率。
  3. wait/notify 就是线程间的一种协作机制。
  4. 调用wait和notify方法需要注意的细节
  • notify后会执行wait之后的代码。
  • wait方法与notify方法必须要由同一个锁对象调用。因为:对应的锁对象可以通过notify唤醒使用同一个锁对象调用的wait方法后的线程。
  • wait方法与notify方法是属于Object类的方法的。因为:锁对象可以是任意对象,而任意对象的所属类都是继承了Object类的。
  • wait方法与notify方法必须要在同步代码块或者是同步函数中使用。因为:必须要通过锁对象调用这2个方法。
//包子资源类
public class BaoZi {
	String pier ;
	String xianer ;
	booleanflag=false ;//包子资源是否存在包子资源状态
}

//吃货类消费者
public class ChiHuo extends Thread {
	private BaoZi bz;
	public ChiHuo(Stringname,BaoZibz){
		super(name);
		this.bz=bz;    
	}
	@Override
	public void run() {
		while(true){
			synchronized (bz){
				if(bz.flag==false){//没包子
					try {
						bz.wait();                    
					} catch (InterruptedExceptione) {
						e.printStackTrace();                                                               
					}                
				}
				System.out.println("吃货正在吃"+bz.pier+bz.xianer+"包子");
				bz.flag=false;
				bz.notify();           
			}     
		}  
	 }
 }

//包子铺类生产者
public class BaoZiPu extends Thread {
	private BaoZi bz;
	public BaoZiPu(Stringname,BaoZibz){
		super(name);
		this.bz=bz;    
	}
	@Override
	public void run() {
		int count=0;
		//造包子
		while(true){
		//同步
			synchronized (bz){
				if(bz.flag==true){//包子资源存在
					try {
						bz.wait();                    
					} catch (InterruptedExceptione) {
						e.printStackTrace();                   
					 }                
				}
				// 没有包子造包子
				System.out.println("包子铺开始做包子");
				if(count%2==0){// 冰皮五仁
					bz.pier="冰皮";
					bz.xianer="五仁";                
				}else{// 薄皮牛肉大葱
					bz.pier="薄皮";
					bz.xianer="牛肉大葱";                
				}
				count++;
				bz.flag=true;
				System.out.println("包子造好了:"+bz.pier+bz.xianer);
				System.out.println("吃货来吃吧");
				//唤醒等待线程(吃货)
				bz.notify();           
		 }        
		}  
	}
}

//测试类
public clas sDemo {
	public static void main(String[] args) {
	//等待唤醒案例
	BaoZibz=newBaoZi();
	ChiHuoch=newChiHuo("吃货",bz);
	BaoZiPubzp=newBaoZiPu("包子铺",bz);
	ch.start();
	bzp.start();    
	}
}

9 线程池

  1. 线程池
    在这里插入图片描述
  2. 线程池的原理
    在这里插入图片描述
  3. 创建线程池
    java.util.concurrent.Executors:1.5之后线程的工厂类。

https://www.cnblogs.com/pcheng/p/13540619.html

10 守护线程和非守护线程

Java中有两类线程:User Thread(用户线程)、Daemon Thread(守护线程)

用户线程即运行在前台的线程,而守护线程是运行在后台的线程。 守护线程作用是为其他前台线程的运行提供便利服务,而且仅在普通、非守护线程仍然运行时才需要,比如垃圾回收线程就是一个守护线程。当VM检测仅剩一个守护线程,而用户线程都已经退出运行时,VM就会退出,因为没有如果没有了被守护这,也就没有继续运行程序的必要了。如果有非守护线程仍然存活,VM就不会退出。

守护线程并非只有虚拟机内部提供,用户在编写程序时也可以自己设置守护线程。用户可以用Thread的setDaemon(true)方法设置当前线程为守护线程。

虽然守护线程可能非常有用,但必须小心确保其他所有非守护线程消亡时,不会由于它的终止而产生任何危害。因为你不可能知道在所有的用户线程退出运行前,守护线程是否已经完成了预期的服务任务。一旦所有的用户线程退出了,虚拟机也就退出运行了。 因此,不要在守护线程中执行业务逻辑操作(比如对数据的读写等)。、

另外有几点需要注意:

  1. setDaemon(true)必须在调用线程的start()方法之前设置,否则会抛出IllegalThreadStateException异常。
  2. 在守护线程中产生的新线程也是守护线程。
  3. 不要认为所有的应用都可以分配给守护线程来进行服务,比如读写操作或者计算逻辑。
  4. 守护线程中产生的线程也是守护线程。
  5. 典型的守护线程是(GC)垃圾回收线程。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值