java 多线程(通俗易懂)

线程和进程

进程

含义:一个运行中的应用程序。每个进程都会分配独立的内存空间。

线程

含义:一个进程的执行分支

它们之间的联系

  1. 一个进程中至少有两个线程在运行。一个执行main的线程,一个垃圾回收线程。
  2. 线程是进程中的最小执行单位。
  3. 同一个进程的线程共享方法区和堆内存;栈内存不共享。

特点:

  1. 使用了多线程之后,main结束之后,其他线程还有可能在运行。
  2. 单核和8核的cup实现多线程的方法:8核同一时间至少可以有8个线程同时进行;单核的实际上同一时间也只能有一个线程在执行,但是cpu运行非常快,多个线程不断切换,给人宏观上多个线程同时执行的的感觉。

生命周期:

  • 要到达运行状态只能通过就绪状态;
  • 启动线程后是进入就绪状态

 创建线程的两种方法:

继承Thread类

public class _01BuyTicket {	
	public static void main(String[] args) {
		
		Thread one = new BuyThreader();
		Thread two = new BuyThreader();
		
		one.start();
		two.start();
	}

}

class BuyThreader extends Thread{
	
	private int ticket = 10;
	
	//指定任务
	@Override
	public void run() {

		while(ticket>0){
			System.out.println("当前购买的票是:"+ticket);
			ticket = ticket - 1;
			
			try {
				Thread.sleep(100);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}
}

实现Runnable接口

	public static void main(String[] args) {
		
		Thread one = new BuyThreader();
		Thread two = new BuyThreader();
		
		one.start();
		two.start();
	}

}

class BuyThreader extends Thread{
	
	private int ticket = 10;
	
	//指定任务
	@Override
	public void run() {

		while(ticket>0){
			System.out.println("当前购买的票是:"+ticket);
			ticket = ticket - 1;
			
			try {
				Thread.sleep(100);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}
}

两个创建方法的区别:

  • 实现接口的方法可以多个线程共享资源且还可以继承其他类
  • 实现接口方法会有线程安全的问题

注意:run和start方法的区别

启动线程是使用start,使用run只是简单的方法调用而已。

线程休眠 

方式区别1区别2注意点
sleep(long millis)是Thread中的静态方法不会释放锁资源

wait(long millis)/

wait()

是Object中的方法会释放锁资源wait()代表无限等待,通过调用notify()\notifyAll()唤醒
public class BuyTicketDemo {
	
	public static void main(String[] args) {
		
		Runnable runnable = new Buyer();
		 
		Thread one = new Thread(runnable,"one");
		Thread two = new Thread(runnable,"two");
		
		one.start();
		two.start();
	}
}

class Buyer implements Runnable{
	private int ticket=10;
	@Override
	public void run() {
		buy();
	}

	private void buy() {
		//思路
		    //线程买了一些要休息一下,并且让其他线程可以进入
		
		synchronized (this) {      
			while(ticket >0){
				
				//notify(); //唤醒其中一个(wait)休眠的线程
				notifyAll();//唤醒所有(wait)休眠的线程
				
				System.out.println(Thread.currentThread().getName()+"购买的是"+ticket);
				ticket--;
				 
				try {
					//Thread.sleep(1000);   //不会释放资源(锁)   
					//wait(100);            //会释放资源(锁)
					
					if(ticket!=0){
						wait();  //无限休眠   
					}
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		}
	}
}

 线程安全(重点)

 含义:多个线程操作一份数据不会发生错乱

我们前面说到,线程共享方法区和堆,所以所以可能存在线程安全问题。也就是多线程操作静态变量或者实例变量会出现线程安全。操作局部变量不会(不共享)。

处理方法:

  1. 同步锁

同步:

同步方法:

		public class _01BuyTicket {
	
	public static void main(String[] args) {
		
		Runnable buyer = new Buyer();
		
		Thread one = new Thread(buyer);
		 
		
		Thread two = new Thread(buyer);
		
		one.start();
		two.start();
	}
}

class Buyer implements Runnable{

	private  int ticket=10;
	
	//指定线程的任务
	@Override
	public void run() {     
		buy();
	}
	
	private synchronized void buy(){ //监视对象this,static同步方法的监视对象是 类.class
		
		while(ticket>0){   
			System.out.println("购买的ticket是:"+ticket); 
			
			try {
				Thread.sleep(100);   //让线程休眠
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			
			ticket--;  
		}
	}	
}

注意:

  1. 给方法加上synchronized就是给这个方法加上一个监视对象(相当于锁),监视对象用来监视该方法一次只能有线程执行。(但是其他的监视对象的线程可以进来)。
  2. 普通方法上的监视对象是this(自身对象);static的监视对象是类.class

同步整个方法让其监视对象只能是this,且方法中有些代码是不需要同步的,导致效率较低;所以我们一般同步代码块。

同步代码块

对上面相同的案例进行改造

public class _03BuyTicket {
	   同步代码块
	   //synchronized(监视对象){
	   //}
	  
	public static void main(String[] args) {
		
		Runnable buyer = new Killer();
		
		Thread one = new Thread(buyer);
		 
		
		Thread two = new Thread(buyer);
		
		one.start();
		two.start();
	}
}

class Killer implements Runnable{

	private int ticket=10;
 //对象
    Object o= new Object(); // 实例变量。实例变量obj也是共享的。
	
	//指定线程的任务
	@Override
	public void run() {     
		buy();
	}
	
 
	private void buy(){ 
		System.out.println("hello"); 
	  
		synchronized (this){  //this  
			while(ticket>0){   
				System.out.println("购买的ticket是:"+ticket); 
				try {
					Thread.sleep(10);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				ticket--;  
			}
		}
	}
}

注意:监视对象的选择

同步代码块可以自由选择监视对象,可以this,也可以是类中实例对象。

重中之重:这个监视对象一定要选好了。这个监视对象一定是你需要排队执行的这些线程对象所共享的。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值