线程/进程,多线程作用 线程的状态

版权声明:转载请注明出处 https://blog.csdn.net/h2604396739/article/details/86487291

理解线程、进程和锁

为什么需要线程?

1充分使用cpu来提高效率,包括两个方面:

  • 单个cpu的充分利用,在一个线程处理中,cpu并不是一直处于忙碌状态,典型场景是需要进行磁盘IO,而磁盘的IOcpu处理相比,速度相差很大,此时cpu就会长久处于空闲状态,所以如果还有别的线程,cpu就可以在空闲时间处理别的线程。注意:单个时间只会有一个线程被处理
  • 针对服务器有多个cpu的场景,单线程只会使用一个cpu,别的cpu就被浪费了,此时使用多线程,多个cpu会充分利用起来

2简化建模的复杂度

当程序中包含多种类型的任务时,一个线程执行一个任务相对于一个线程执行所有任务,可以避免执行任务的调度和切换,简化建模和逻辑处理。 

3异步事件的简化

如应用程序读取套接字的内容,如果数据没有到来,此时将阻塞别的所有请求,为了避免这个问题,单线程应用程序必须使用复杂的非阻塞IO,使用多线程则不会存在该问题。

区别线程与进程:

  • 进程是指运行中的应用程序,每个进程都有自己独立的地址(内存)空间,线程没有独立的地址空间
  • 线程是由进程创建的,一个进程可以拥有多个线程
  • 一个进程中的多个线程之间线程之间可以共享资源,如java中的堆和方法区。多个进程之间则不能共享这些资源

线程的锁,状态转化与方法

理解线程中的锁,为什么需要锁?什么作用?

      上面已经提到,与进程区别之一就是线程之间部分数据是共享的,那么怎么保证多线程共享数据的一致性呢,这就是锁的功能,可以对共享的数据上锁(需要人为使用synchronized或别的方式),线程如果没有获得锁,线程就不能使用共享数据,也就不能被执行;如果获得了锁,说明别的要使用该共享数据的线程此时一定不会被执行,注意不是别的所有线程,因为别的线程与获得锁的线程可以没有共享数据。

线程如果想要被运行必须有两个条件:1获得了被锁数据上的锁 2获得了cpu;线程必定是先获得锁,然后才有可能获得cpu

根据上面两个条件,线程被划分为如下几种状态:

  • Waiting  pool:等待池状态,此时不能参与争抢锁,还没有资格争抢cpu
  • Lock pool:锁池状态,此时有空闲锁时参与争抢锁,还没有资格争抢cpu
  • Runnable:可运行状态,已经获取了锁,但没有获取cpu,参与争抢cpu
  • Running:运行中,获取了锁和cpu
  • Blocking:阻塞状态,放弃了cpu,sleep一直持有锁,join锁自动回来,不参与争抢cpu,一定时间后会到runnable

线程的生命周期及状态转化如下图:

线程状态转化的方法:

join 强行让当前的线程等待,执行join进来的线程,直到join进来的线程结束,则该线程继续执行,虽然在等待期间锁会丢失,但是当join进来的线程结束后,则会立即得到原本的锁
yield:使当前线程从执行状态(运行状态)变为可执行态(就绪状态)。cpu会从众多的可执行态里选择,也就是说,当前也就是刚刚的那个线程还是有可能会被再次执行到的,并不是说一定会执行其他线程而该线程在下一次中不会执行到了。
wait :进入等待池,并且失去锁,只有别的线程执行notify或notifyAll进入锁池争抢锁
interrupt :中断阻塞
  如果线程在调用wait,join,sleep而导致的阻塞,则阻塞立即被中断,并收到InterruptedException;
  当线程并不处于阻塞状态下被调用interrupt方法时仅仅设置了该线程的中断状态为true,但是其并不会中断一个正在运行中的线程,只是发出了中断请求,然后由线程在一个合适的时刻中断自己

 wait和notify和sleep
     1、this.wait():当前正在访问此对象的线程wait,线程所获得的对象的锁丢失,由其它线程去争夺,直到有notify或者notifyAll方法唤醒它。
     2、sleep:thread类的方法,抱着锁睡觉,要直到sleep所在的方法全部被执行完毕以后才交出锁。
     3、this.notify():当前的notify不可以叫醒自身,可以叫醒别的,如果有很多线程挂起的话,就随机地决定哪一个,注意不会立刻释放获得的锁。
         如:a的notify()叫醒  b.wait()或c.wait()
     4、wait()和notify()的使用:必须包括在synchronized代码块中,等待中的线程必须由notify()方法显式地唤醒,否则它会永远地等待下去。很多人初级接触多线程时,会习惯把wait()和notify()放在run()方法里,一定要谨记,这两个方法属于某个对象,应在对象所在的类方法中定义它,然后run中去调用它。
wait和notify用法解析

  •    wait notify必须用在同步方法块或同步方法中,这是因为:
  •   调用wait和notify均需要释放锁,不同的是前者立刻释放锁并进入waitPool,后者并不会立刻释放锁,此时锁还在自己手中叫醒了与自己使用相同锁的在waiPool中的线程

notify和notifyAll
   本质上notify和notifyAll的优化,即notify仅从所有等待的线程中叫醒一个,所以此时需要所有等待线程等待的条件完全一致,并且保证被唤醒的是恰当的对象,否则会出现信号丢失。所以一般推荐使用notifyAll,唤醒所有的任务,然后任务会确认通知是否与自己有关。

volatile的分析:
主线程会有一个主内存,变量最终的值会被放到主内存中,但是每个子线程都会有单独自己的内存,对变量的修改一般都是各自维护各自内存中的值,在将变量声明为volatile之后,所有涉及到对该变量的修改和读取都是直接从主内存获取,这样就解决了同一变量在不同线程内存值不一致的问题。能保证读取到的数据是最新的,并且修改后的数据直接保存到主内存中
volatile 变量不会像锁那样造成线程阻塞,不保证操作的原子性。如volatile int i=0;两个线程中均执行i++,执行后可能为1

 

线程synchronized、wait和notify使用实例

public class MythreadExtendsThread extends Thread{
	private sequenceB sequence;
	String lock;
	public MythreadExtendsThread(sequenceB sequence,String lock){
		this.sequence=sequence;
		this.lock=lock;
	}
	public void run(){
		//此处说明是同步块
		synchronized(lock){
		for(int i=0;i<6;i++){
			System.out.println(Thread.currentThread().getName()+" i="+i+"sequence.getNumber()"+sequence.getNumber());
			try {
				Thread.sleep(i*1000);
				if(i==3){
				//有了同步块才可以使用notify和wait,而且调用wait和notify的是lock
					lock.notifyAll();
					lock.wait();
					//错误的调用如下,抛出java.lang.IllegalMonitorStateException
					//Thread.currentThread().wait();
				}
				if(i==5){
					lock.notifyAll();
				}
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		}
	}
}

public class ThreadTest {
	public static void main(String[] args) {
		boolean  flag=true;
		
		RunnerImplementsRunnable runner=new RunnerImplementsRunnable(flag);
		Thread thread1=new Thread(runner);
	
		String lock="lock";
		String lock2="lock2";
		sequenceB sequnce=new sequenceB();  
		//注意观察如下两个thread,他们引用了同一个lock,所以才可以相互叫醒
		MythreadExtendsThread thread2=new MythreadExtendsThread(sequnce,lock);
		MythreadExtendsThread thread3=new MythreadExtendsThread(sequnce,lock);
		//错误写法如下,此时,两者上的不是同一把锁,所以不可以相互叫醒
		//MythreadExtendsThread thread3=new MythreadExtendsThread(sequnce,lock2);
		thread2.start();
		
		for(int i=0;i<8;i++){
			System.out.println(i);
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			if(i==1){
				thread3.start();
			}
		}

	}
}

猜你喜欢

转载自blog.csdn.net/h2604396739/article/details/86487291