多线程总结上篇(基础知识)

多线程总结上篇

1、进程和线程的区别:

一个进程是多个线程组成的,进程是操作系统管理内存最小单位,线程使用内存,从进程申请。

2、线程创建的两种方式

方式一:用Thread类创建线程
声明为一个 Thread 类的子类,并覆盖 run() 方法
class MyThread  extends Thread {
 public void run( ) { 
   /* 覆盖该方法*/ 
   }
}
当使用继承创建线程,这样启动线程:
new MyThread().start();</span>



方式二:使用Runnable接口创建多线程
声明为一个实现 Runnable 接口的类,并实现 run() 方法
<span style="font-size:14px;">class MyThread  implements Runnable{
public void run() {
    /* 实现该方法*/ 
  }
}</span>
当使用实现接口创建线程,这样启动线程:

new Thread(new MyThread()).start();

3、两种实现方式的比较:
     使用Runnable接口可以将代码和数据分开,形成清晰的模型,还可以从其他类继承,保持程序风格的一致性;
      直接继承Thread类,不能再从其他类继承 ,编写简单,可以直接操纵线程 。

      在实际编程中,我们优先使用Runnable接口创建多线程,因为JAVA是单继承的,继承了Thread,就不能继承其他的类了
      备注:可以用Thread.currentThread().getName()获取线程的名字。  


4、线程的4种状态

       创建(new Thread().start())
       执行状态 (线程run方法正在运行)
       阻塞状态(run方法在执行一段时间后暂停执行)
       线程死亡(run方法执行结束、发生异常)

       那么这四种状态,是如何进行切换的呢?如右边图:

       * 新建 --- 运行 (获得cpu的使用权)
       * 运行 --- 阻塞 (Thread.sleep join、使用同步锁 wait IO读写、网络传输)
       * 阻塞 --- 运行  结束阻塞,重新获得cpu使用权 
      
       这里简要概述,下一篇详细说明。。。。

       关于图中部分方法的解释:
       sleep 使当前线程睡眠一段时间,睡眠过程中,不释放锁资源
       join 等待目标线程执行结束后,当前线程才能继续执行
       wait 当你获得一个同步锁后,选择在锁上面监视器进行等待,等待必须由别人进行 
       唤醒 notify  notifyAll

5、线程锁

加锁目的:保证一段程序同一时间只能由一个线程进行执行,阻止两个线程同时执行一 段代码 ,java中每个对象都可以作为锁 ,锁的本质,就是锁定一块内存地址。

经典案例:卖火车票
情景描述:票源只有一个,可以有多个窗口进行卖票,保证无错票产生
思路:因为保证票源只有一个所以可以考虑用单例设计模式,可以循环产生多个线程模拟多个窗口,并且保证线程同步。
 代码如下:
   
class Ticket{
	private static Ticket ticket=null;
	private int totalTicket=100;//总票数
	private int hasSaledTicket=0;//已卖的票数
			
	//构造函数私有化
	private Ticket(){
				
	}
public static Ticket getTicketIntance(){
                if(ticket==null){
                   synchronized (Ticket.class) {
                 	if(ticket==null){
	                    ticket=new Ticket();
                            }
	                }
                     }
                  return ticket;
           }
public synchronized void  sale(){
		if(totalTicket<=0){
		   throw  new  RuntimeException("票已经卖完了");
		}
		++hasSaledTicket;//卖了一张票
		System.out.println("打印一张票"+hasSaledTicket);

		try {
		  Thread.sleep(2);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		--totalTicket;//总票数减一
		System.out.println("剩余票数"+totalTicket);
		}

     }
 
 
 // 准备窗口
 class SaleTicketWindow implements Runnable {
		private Ticket ticket;
                //将票源传入
		SaleTicketWindow(Ticket ticket) {
		this.ticket = ticket;
		}

		@Override
		public void run() {
		while (true) {
		try {
		ticket.sale();// 卖票
		} catch (RuntimeException e) {
			break;// 捕获到异常,说明票卖完,跳出循环
		  }
		}
	}
    }
 
 
 
 
public class SaleTicketSystem {
	 //单例设计模式获取对象
	 private static Ticket mTicket=Ticket.getTicketIntance();
		public static void main(String[] args) {
		   for (int i = 0; i < 5; i++) {	
			SaleTicketWindow window=new SaleTicketWindow(mTicket);
			Thread t=new Thread(window);
			t.start();//启动线程
		}
	}
}
在多窗口卖票系统中:
      这个程序运行时正常的,但是如果不加synchronized关键字修饰Ticket的sale方法,运行程序会发现打印出诸如-1,-2等错票。 这就是多线程的运行出现了安全 问题。
 
问题的原因: 
      当多条语句在操作同一个线程共享数据时,一个线程对多条语句只执行了一部分(CPU就切换到另外的线程去),还没有执行完,另一个线程参与进来执行。导致共享数据的错误。

     解决办法: 
     对多条操作共享数据的语句,让一个线程都执行完。在执行过程中,其他线程不可以参与执行。 
     Java对于多线程的安全问题提供了专业的解决方式,有两种解决方式,同步代码块和同步函数,这两种方式可以保证同步中只能有一个线程在运行,下面分别对 两种实现方式进行介绍 。

A、同步代码块 ,用synchronized关键字来进行定义。 


      同步代码块格式 :
      synchronized(唯一对象){ 
           需要被同步的代码; 
       } 

     对象如同锁,持有锁的线程可以在同步中执行。没有持有锁的线程即使获取CPU的执行权,也进不去,因为没有获取锁

     同步的前提: 
     a、必须要有两个或者两个以上的线程。
     b、必须是多个线程使用同一个锁。 


     从程序上看就解决了线程的同步问题,但是问题就来了,同步的锁是什么呢? 
     锁可以是任意对象,我们创建了一个对象,结果解决了问题。
 
     直观分析: 
     由于同步代码块使用的锁是任意对象,很直观就可以想到函数是需要被对象调用才执行的。 那么函数都有一个所属对象引用。就是this。
     但这个对象是怎么传递进来的呢?
     原来是调用函数的时候,函数内持有一个函数对象的引用。
     那我们可不可以把同步定义在函数上? 
     答案是可以的,结果就有了同步函数。 

B、同步函数: 

所谓的同步函数就是在函数的返回值前面加一个synchronized关键字就是同步函数了。
使用同步函数注意事项: 
一定要明确哪个代码是需要进行同步,如果同步函数中的代码都是需要同步的,就可以使用同步函数。 如果同步函数被静态修饰后,使用的锁是什么呢? 因为静态方法中也不可以定义this。所以静态同步函数的锁不可能是this但是这个对象又是谁呢? 静态进内存时,内存中没有本类对象,但是一定有该类对应的字节码文件对象。 类名.class  该对象的类型是Class , 经过验证,静态同步函数的锁是 类名.class 。


6 、多线程是否会加快程序的执行效率呢?

 答:不会,机器的速度是一定的,CPU在多个线程之间来回切换需开销。故速度上可能还会减慢的。

        
今天博客写到这,接下来下一篇的博客,会写关于线程剩余的知识总结,come on!
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值