多线程的理解

本文详细阐述了多线程技术的核心概念,包括创建多线程的方式、线程运行状态、线程安全问题及解决方案、死锁现象及其避免方法、线程间的通信机制,以及如何利用ExecutorService产生线程池和定时器。通过实例解析,帮助读者全面掌握多线程编程技巧。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


多线程技术的理解

一.多线程的重点类容:

1.创建多线程的方式(两种)。

2.线程运行的状态(五种)。

3.线程的安全问题。同步代码块,同步函数,lock接口。

4.死锁。

5.线程通信。

6.用executors产生单线程,创建线程池。

 

进程和线程是什么:一个运动的程序,在内存上分配的空间就是进程(如电脑上任务管理器中的进程)线程就是程序的一个执行控制单元。一个进程中往往偶多个线程,至少有一个线程。

     怎么去解多线程?进程好比一个商场,线程好比一个人,多线程可以比做很多人去逛商场。线程可比排队逛商场,里面的人出来了才能在进去一个人。对商场而言,多线程的效率肯定要高很多,程序的多线程也是一样。

 

1创建线程的方式

(a)定义一个类继承Thread ,由子类复写run方法。

步骤:

1,定义类继承Thread类;

2,目的是复写run方法,将要让线程运行的代码都存储到run方法中;

3,通过创建Thread类的子类对象,创建线程对象;

4,调用线程的start方法,开启线程,并执行run方法。

代码:class Demo extends Thread{
	
	      public void run()
{
		 //多线程代码
	    }
}
//说明:如果Demo有参数,run前面要有有参的构造函数。
/* Demo d = new Demo();
这个时候创建子类对象同时也创建了多线程对象*/

(b) 创建线程的第二种方式:

定义一个类实现一个接口Runnable。

步骤:

1,定义类实现Runnable接口。

2,覆盖接口中的run方法(用于封装线程要运行的代码)。

3.创建实现Runable接口的子类对象

4.用Thread创建线程对象,并接受接口的子类对象

5.用Thread的start方法开启线程,运行接口的run方法。

代码:class Demo implements Runnable{
	
	 public void run(){
		 //多线程代码
	 }
}
public class duoXianCheng {
	
    public static void main(String[] args){
          Demo d = new Demo();
          Thread t1 new thread(d);
          Thread t2 new thread(d);
          t1.start();
          t2.start();
}
}<span style="font-family: Arial; ">说明:接口的子类对象要作为参数传递给线程对象</span>


为什么会有Runnable接口:Thread类只能被单继承,Runnable可以被多实现,弥补了Thread的缺陷。

2.线程有5中状态

 被创建:start()

运行:具备执行资格,同时具备执行权;

冻结:sleep(time),wait()—notify()唤醒;线程释放了执行权,同时释放执行资格;

临时阻塞状态:线程具备cpu的执行资格,没有cpu的执行权;

消亡:stop()

(注意:执行权和执行资格的变化)

3.线程安全:

     为什么会产生线程安全问题?

以商场卫生间为例,整个商场的人都去上厕所,大家挤着谁也上不了,这个时候商场定义一个规则,大家排队上厕所,问题就解决了。  厕所是共享资源。排队可以解决问题。

   线程安全问题发生的条件:多条语句在操作同一个共享资源,就能发生多线程安全问题,排队可以解决这个问题


(a)  synchronized(对象){
   (被同步的代码)(共享数据)
}
(b) public  synchronizedvoidmethod (){
 (被同步的代码)(共享数据)
}
(c) Lock lock= newReentrantLock();
public void method()  {
      lock.lock();
 (被同步的代码)共享数据
    lock.unlock();
  }
 }

注意:(a)中对象就是锁,任意对象都可以作为锁。

(a)  中同步函数中有没static锁不一样,

没有static锁是this

有static 锁是类名.class

(懒汉式单例模式考虑线程安全时候会用到)

(b)  中同步代码一般要try ,lock.unlock要放在finally中代码运行完后一定要释放锁。

单核cpu是在多线程快速切换运行的,也就是在某一个时刻,只有一个线程在运行。

    4死锁

     同步嵌套同步是最常见的死锁。

例如:最常见的原因是锁的嵌套循环,例如if  else判断语句里面,当线程满足if条件,获得A锁执行if里面的同步,A同步里面还嵌套

了一个同步,需要B锁,这时线程切换到else条件,获得了B锁执行里面的同步,这个同步里面也嵌套了一个同步,需要获得A锁才能执行。if和else里面的同步都不放锁,就形成了死锁。

解决办法:用同一个锁。

5线程通信
思路: 多个线程在操作同一个资源,但是操作的动作不同。
1.将资源封装成对象。
2.将线程执行的任务封装成对象。(run方法)
3.
完成线程通信需要用到等待唤醒机制


wait:将同步中的线程处于冻结状态。释放了执行权,释放了资格。同时将线程对象存储到线程池中。
notify:唤醒线程池中某一个等待线程。
notifyAll:唤醒的是线程池中的所有线程。


注意:  wait和sleep区别: 分析这两个方法:从执行权和锁上来分析:
wait:可以指定时间也可以不指定时间。不指定时间,只能由对应的notify或者notifyAll来唤醒。
sleep:必须指定时间,时间到自动从冻结状态转成运行状态(临时阻塞状态)。
wait:线程会释放执行权,而且线程会释放锁。
Sleep:线程会释放执行权,但不是不释放锁。

</pre><pre name="code" class="java">经典代码:生产者和消费者:
//多线程间通信问题               <生产烤鸭问题>
/*resoures  producer consumer  和主函数创建线程4个类。resoures定义生产和消费的方法,producer和consumer定义接受resoures的构造函数
 * ,并实现多线程。*/
public class ProuderComsumerDemo {
			public static void main(String[] args) {
				resoures r = new resoures();
				producer pro = new producer(r);
				consumer con =new consumer(r);
				Thread t1 = new Thread(pro);
				Thread t2 = new Thread(pro);
				Thread t3 = new Thread(con);
				Thread t4 = new Thread(con);
				t1.start();
				t2.start();
				t3.start();
			    t4.start();
}
}
//资源
  class resoures {
	  //定义变量:生产什么。生产多少了,生产与消费之间的切换变量
	  private String name;
	  private  int count=0;
	  private  boolean falg =false;
	  //生产方法
	
	  public synchronized void set(String name){
		  // 什么时候生产 
		
		  while(falg)
			try {this.wait();} catch (InterruptedException e) {e.printStackTrace();}//睡觉
		   //生产       
		     count++;
		  if(count==1000){
			 
		  }
		  this.name = name+count;
		  System.out.println(Thread.currentThread().getName()+"生 产 了"+this.name);
		  //生产完了要告诉消费者
		  falg=true;
		  notifyAll();
		   }
	 //消费方法
	  public synchronized void out(){
		      while(!falg)
				try {this.wait();} catch (InterruptedException e) {e.printStackTrace();}
		      
		    System.out.println(Thread.currentThread().getName()+"消 费 了"+this.name);
		    falg=false;
			  notifyAll();
	  }
  }
//生产者
class producer implements Runnable{
         private resoures r;
         public producer(resoures r) {
			super();
			this.r = r;
		}
	@Override
	public void run() {
		   while(true){
			   r.set("烤鸭");		   
		   }	
	}	
}
//消费者
class consumer implements Runnable{
    private resoures r;
    
public consumer(resoures r) {
		super();
		this.r = r;
	}
@Override
public void run() {
	   while(true){
		   r.out();
   }	
}
}

6.(a)用executors产生单线程,创建线程池。
ExecutorService pool = Executors.newSingleThreadExecutor(); 
pool.execute(new Runnable(){})
(b)/产生一个单线程,创建定时器 
 ScheduledExecutorService timer =  Executors.newScheduledThreadPool(1);  
        timer.scheduleAtFixedRate(  
                new Runnable(){})
(注意:executors必须记住。)


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值