黑马程序员——Java语言基础:多线程

------ Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------

  

多线程


一、概述

1. 进程:一个正在执行中的程序。每一个进程执行都有一个执行顺序,该顺序是一个执行路径或称为一个控制单元。


2. 线程:就是进程中的一个独立的控制单元,线程在控制着进程的执行。只要进程中有一个线程在执行,进程就不会结束。

             一个进程中至少有一个线程。

3. 线程的状态:


(1)被创建:等待调用start启动。


(2)运行状态:具有执行资格和执行权。


(3)临时状态(阻塞):有执行资格,但是没有执行权。


(4)冻结状态:遇到  sleep()方法和wait()方法时,失去执行资格和执行权


sleep()方法时间到或者调用notify()方法时,获得执行资格,变为临时状态。


(5)消忙状态:stop()方法,或者run方法结束


4. 多线程:在java虚拟机启动的时候会有一个java.exe的执行程序,也就是一个进程。该进程中至少有一个线程负责java程序的执行。而且这个线程运行的代码存在于main方法中,该线程称之为主线程。JVM启动除了执行一个主线程,还有负责垃圾回收机制的线程。这种在一个进程中有多个线程执行的方式,就叫做多线程。


※ 当某些代码需要同时被执行时,就用单独的线程进行封装。


5. 多线程存在的意义:多线程的出现能让程序产生同时运行效果,可以提高程序执行效率


例如:在java.exe进程执行主线程时,如果程序代码特别多,在堆内存中产生了很多对象,而同时对象调用完后,就成了垃圾。如果垃圾过多就有可能是堆内存出现内存不足的现象,只是如果只有一个线程工作的话,程序的执行将会很低效。而如果有另一个线程帮助处理的话,如垃圾回收机制线程来帮助回收垃圾的话,程序的运行将变得更有效率。


二、创建线程的方式

1. 继承方式:通过继承Thread类,然后复写其run方法的方式来创建线程。


通过查找java的帮助文档API,我们发现java中已经提供了对线程这类事物的描述的类——Thread类。


※创建步骤:


a.定义类继承Thread。


b.复写Thread中的run方法。(目的:将自定义代码存储在run方法中,让线程运行)


c.创建定义类的实例对象。相当于创建一个线程。


d.用该对象调用线程的start方法。该方法的作用是:启动线程,调用run方法。


 如果对象直接调用run方法,等同于只有一个线程在执行,自定义的线程并没有启动。


例:   

/*  
小练习  
创建两线程,和主线程交替运行。  
*/    
    
//创建线程Test    
class Test extends Thread    
{    
    Test(String name)    
    {    
        super(name);    
    }    
    //复写run方法    
    public void run()    
    {    
        for(int x=0;x<1000;x++)    
        System.out.println(Thread.currentThread().getName()+"..run..."+x);    
    }    
}    
class  ThreadTest    
{    
    public static void main(String[] args)     
    {    
        new Test("one+++").start();//开启一个线程并执行该线程的run方法,如果.run(),线程创建了,但并未运行,仅调用了run方法  
        new Test("tow———").start();//开启第二线程    
        //主线程执行的代码    
        for(int x=0;x<2000;x++)    
        System.out.println("main run!");    
    }    
}    

  ※ 运行结果每一次都不同,因为多个线程都活去了cpu的执行权,cpu执行到谁,誰就运行

  ※ 明确一点,某一个时刻,只能有一个程序在执行(多核除外)

  ※ cpu在做着快速的切换,以达到看上去是同时运行的效果(可把多线程的运行行为看做抢夺cpu的执行权)

  ※ 多线程的一个特性:随机性

2. 实现方式:实现Runnable接口,并复习其中run方法的方式。

   使用继承方式有一个弊端,那就是如果该类本来就继承了其他父类,无法通过Thread类来创建线程,于是出现实现方式

   ※ 创建步骤:

   a. 定义类实现Runnable的接口。

   b. 覆盖Runnable接口中的run方法。目的也是为了将线程要运行的代码存放在该run方法中。

   c. 通过Thread类创建线程对象。

   d. 将Runnable接口的子类对象作为实参传递给Thread类的构造方法。

   ※ 为什么要将Runnable接口的子类对象传递给Thread的构造函数?

      自定义的run方法所属的对象是Runnable接口的子类对象。所以要让线程去指定对象的run方法,就必须明确该run方法所属对象。

   e. 调用Thread类中start方法启动线程。start方法会自动调用Runnable接口子类的run方法。

   ※ 实现方式好处:避免了单继承的局限性。在定义线程时,建议使用实现方式。 

   例:

class TicketDemo implements Runnable//extends Thread
{
	private static int tick  = 100;
	public void run() 
	{
		while (true)
		{
	<span style="white-space:pre">		</span>if(tick > 0)//剩余票数大于0,输出线程名及余票数
				System.out.println(Thread.currentThread().getName()+"...sale:" + tick--);
		}
	}
}
class Ticket
{
	public static void main(String[] args)
	{
		TicketDemo t = new TicketDemo();//Runnable接口子类的实例对象
		Thread t1 = new Thread(t);//多个窗口同时买票
		Thread t2 = new Thread(t);
		Thread t3 = new Thread(t);
		Thread t4 = new Thread(t);
		t1.start();//启动线程
		t2.start();
		t3.start();
		t4.start();	
	}

}

三、多线程的安全问题

1. 导致安全问题的出现的原因:当多条语句在操作同一线程共享数据时,一个线程对多条语句只执行了一部分,还没用执行完

另一个线程参与进来执行,导致共享数据的错误。

简单的说就两点:

(1)多个线程访问出现延迟

(2)线程的随机性

※ 线程安全问题在理想状态下,不容易出现,但一旦出现对软件的影响是非常大的。

2. 解决办法——同步(java中对于多线程的安全问题提供了专业的解决方式——synchronized

(1)同步的前提

a. 必须要有两个或者两个以上的线程。

b. 必须是多个线程使用同一个锁。

对多条操作共享数据的语句,只能让一个线程都执行完,在执行过程中,其他线程不可以参与执行。

(2)两种解决方式:同步代码块、同步函数(都是利用关键字synchronized来实现)

a.同步代码块

synchronized(对象)

{

需要被同步的代码;

}

※ 同步可以解决安全问题的根本原因就在那个对象上,其中对象如同锁,持有锁的线程可以在同步中执行。

    没有持有锁的线程即使获取cpu的执行权,也进不去,因为没有获取锁。

b.同步函数

格式:在函数上加上synchronized修饰符即可。

※ 同步函数用的是哪一个锁:函数需要被对象调用,那么函数都有一个所属对象引用。

   就是this。所以同步函数使用的锁是this。

例:

/*
使用两个线程来卖票,一个在同步代码块中,一个在同步函数中,都在执行卖票动作
*/
class TicketDemo1 implements Runnable
{
	private static int tick  = 100;
	Object obj = new Object();
	boolean flag  = true;
	public void run() 
	{
		if(flag)
		{
			while (true)
			{
				synchronized(this)
				{
					if(tick > 0)
					{
						try {Thread.sleep(10);} catch (InterruptedException e) {}
						System.out.println(Thread.currentThread().getName()+"..code:" + tick--);
					}
				}
			}
		}
		else
			while(true)
		<span style="white-space:pre">	</span>	show();
	}
	public synchronized void show()
	{
		if(tick > 0)
		{
			try {Thread.sleep(10);} catch (InterruptedException e) {}
			System.out.println(Thread.currentThread().getName()+"...show...:" + tick--);
		}
	}
		
}
class TicketTest
{

	public static void main(String[] args)
	{
		TicketDemo1 t = new TicketDemo1();
		Thread t1 = new Thread(t);
		Thread t2 = new Thread(t);
		t1.start();
		try{Thread.sleep(10);}catch(Exception e){}
		t.flag = false;
		t2.start();
	}

}

(3)同步的利弊:

好处:解决了多线程的安全问题。

弊端:多个线程需要判断锁,较为消耗资源。

(4)如何寻找多线程中的安全问题

a. 明确哪些代码是多线程运行代码。

b. 明确共享数据。

c. 明确多线程运行代码中哪些语句是操作共享数据的。

(5)静态函数的同步方式

如果同步函数被静态修饰后,使用的锁不是this,因为静态方法中也不可以定义this。

静态进内存时,内存中没有本类对象,但是一定有该类对应的字节码文件对象。例:类名.class(该对象的类型是Class)

这就是静态函数所使用的锁,而静态的同步方法,使用的锁是该方法所在类的字节码文件对象:类名.class。

例:

/* 
加同步的单例设计模式————懒汉式 
*/  
class Single  
{  
    private static Single s = null;  
    private Single(){}  
    public static void getInstance()  
    {  
        if(s==null)  
        {  
            synchronized(Single.class)  
            {  
                if(s==null)  
                    s = new Single();  
            }  
        }  
        return s;  
    }  
}  

(6)死锁: 当同步中嵌套同步时,就有可能出现死锁现象。

四、多线程间的通信

1. 概念:其实就是多个线程在操作同一个资源,但是操作的动作不同

2. wait()、notify()、notifyAll()语句都是用在同步中,因为要对持有监视器(锁)的线程操作

※ 为什么这些操作线程的方法要定义在Object类中?

因为这些方法在操作同步中线程时,都必须要标识他们所操作线程持有的锁,只有同一个锁上的被等待线程,可以被同一个锁上notify唤醒,不可以对不同锁中的线程进行唤醒,即等待和唤醒必须是同一个锁,而锁可以是任意对象,所以可以被任意对象调用的方法定义Object中。

3. 解决安全问题——通过锁

例:

/* 
生产者生产商品,供消费者使用 
有两个或者多个生产者,生产一次就等待消费一次 
有两个或者多个消费者,等待生产者生产一次就消费掉 
*/  
class Resource
{
	private String name;
	private int count = 1;
	private boolean flag = false;
	
	public synchronized void set(String name)
	{
		while(flag)
			try{	wait();	} catch(Exception e) { }
		this.name = name+"--"+ count++;
		
		System.out.println(Thread.currentThread().getName()+"....生产者"+this.name);
		flag = true;
		this.notifyAll();
	}
	public synchronized void out()
	{
		while(!flag)//多生产者多消费者时用while,但可能发生全部wait(),必须用notifyAll()
			try{	wait();	} catch(Exception e){ }
		System.out.println(Thread.currentThread().getName()+"....消费者............"+this.name);
		flag = false;
		this.notifyAll();
	}
}
//生产者线程
class Producer implements Runnable
{
	private Resource res;
	Producer(Resource res)
	{
		this.res = res;
	}
	public void run()
	{
		while(true)
		{
			res.set("+商品+");
		}
	}

}
//消费者线程
class Consumer implements Runnable
{
	private Resource res;
	Consumer(Resource res)
	{
		this.res = res;
	}
	public void run()
	{
		while(true)
		{
			res.out();
		}
	}

}

public class ProducerConsumerDemo 
{
	public static void main(String[] args) 
	{
		Resource r = new Resource();
		Producer pro = new Producer(r);
		Consumer con = new Consumer(r);
		
		Thread t1 = new Thread(pro);
		Thread t2 = new Thread(con);
		Thread t3 = new Thread(pro);
		Thread t4 = new Thread(con);
		
		t1.start();
		t2.start();
		t3.start();
		t4.start();
	}

}
※ JDK1.5提供了多线程升级解决方案:Lock操作,ObjectwaitnotifynotifyAll,替换成了Condition对象。

※ 该Condition对象可以通过Lock锁进行获取,并支持多个相关的Condition对象。

例:(同上例,生产者消费)

import java.util.concurrent.locks.*;    
class Resource   
{     
    private String name;  
    private int count=1;  
    private boolean flag = false;    
    //多态  
    private Lock lock=new ReentrantLock();  
    //创建两Condition对象,分别来控制等待或唤醒本方和对方线程  
    Condition condition_pro=lock.newCondition();  
    Condition condition_con=lock.newCondition();    
    //p1、p2共享此方法  
    public void setProducer(String name)throws InterruptedException  
    {  
        lock.lock();//锁  
        try  
        {  
            while(flag)//重复判断标识,确认是否生产  
                condition_pro.await();//本方等待  
            this.name=name+"......"+count++;//生产  
            System.out.println(Thread.currentThread().getName()+"...生产..."+this.name);//打印生产  
            flag=true;//控制生产\消费标识  
            condition_con.signal();//唤醒对方  
        }  
        finally  
        {  
            lock.unlock();//解锁,这个动作一定执行  
        }  
          
    }  
  
    //c1、c2共享此方法  
    public void getConsumer()throws InterruptedException  
    {  
        lock.lock();  
        try  
        {  
            while(!flag)//重复判断标识,确认是否可以消费  
                condition_con.await();    
            System.out.println(Thread.currentThread().getName()+".消费."+this.name);//打印消费  
            flag=false;//控制生产\消费标识  
            condition_pro.signal();  
        }  
        finally  
        {  
            lock.unlock();  
        }  
  
    }  
}  
  
//生产者线程  
class Producer implements Runnable   
{  
    private Resource res;  
    Producer(Resource res)  
    {  
        this.res=res;  
    }  
    //复写run方法  
    public void run()  
    {  
        while(true)  
        {  
            try  
            {  
                res.setProducer("商品");  
            }  
            catch (InterruptedException e)  
            {  
            }  
        }  
    }  
}  
  
//消费者线程  
class Consumer implements Runnable  
{  
    private Resource res;  
    Consumer(Resource res)  
    {  
        this.res=res;  
    }  
    //复写run  
    public void run()  
    {  
        while(true)  
        {  
            try  
            {  
                res.getConsumer();  
            }  
            catch (InterruptedException e)  
            {  
            }  
        }  
    }  
  
}  
  
class  ProducerConsumer  
{  
    public static void main(String[] args)   
    {  
        Resource res=new Resource();  
  
        new Thread(new Producer(res)).start();//第一个生产线程 p1  
        new Thread(new Consumer(res)).start();//第一个消费线程 c1  
  
        new Thread(new Producer(res)).start();//第二个生产线程 p2  
        new Thread(new Consumer(res)).start();//第二个消费线程 c2  
    }  
}  

五、停止线程

1. 线程停止方法:在JDK 1.5版本之前,有stop停止线程的方法,但升级后此方法已过时;

    现在只有一种办法,那就是让run方法结束。

※ 开启多线程运行,运行代码通常是循环结构。只要控制住循环,就可以让run方法结束,也就是线程结束。

  (设置flag标记)

2. 特殊情况:当线程处于冻结状态。就不会读取到标记。那么线程就不会结束。

   当没有指定的方式让冻结的线程恢复到运行状态时,这时需要对冻结进行清除,强制让线程恢复到运行状态中来。

   这样就可以操作标记让线程结束。Thread类提供该方法interrupt();

例:

class StopThread implements Runnable  
{  
    private boolean flag =true;  
    public  void run()  
    {  
        while(flag)  
        {  
            System.out.println(Thread.currentThread().getName()+"....run");  
        }  
    }  
    public void changeFlag()  
    {  
        flag = false;  
    }  
}  
  
class  StopThreadDemo  
{  
    public static void main(String[] args)   
    {  
        StopThread st = new StopThread();  
        Thread t1 = new Thread(st);  
        Thread t2 = new Thread(st);   
        t1.start();  
        t2.start();   
  
        int num = 0;  
        while(true)  
        {  
            if(num++ == 60)  
            {  
                t1.interrupt();//清除冻结状态  
                t2.interrupt();  
                st.changeFlag();//改变循环标记  
                break;  
            }  
            System.out.println(Thread.currentThread().getName()+"......."+num);  
        }  
        System.out.println("over");  
    }  
}  
※ join方法

   当A线程执行到了B线程的.join()方法时,A线程就会等待,等B线程都执行完,A线程才会执行。

   (此时B和其他线程交替运行)join可以用来临时加入线程执行。

※ setPriority()方法用来设置优先级:

    MAX_PRIORITY 最高优先级10

    MIN_PRIORITY   最低优先级1

    NORM_PRIORITY 分配给线程的默认优先级

※ yield()方法可以暂停当前线程,让其他线程执行。









1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合;、下 4载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合;、下载 4使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合;、下载 4使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。
学校大创竞赛管理系统,学生上报项目内容,学院、教务处、评审专家评审。SpringBoot、SpringCloud、SpringSecurity、redis、Mysql、swagger、fastdfs、maven、vue、webpack.zip项目工程资源经过严格测试可直接运行成功且功能正常的情况才上传,可轻松复刻,拿到资料包后可轻松复现出一样的项目,本人系统开发经验充足(全领域),有任何使用问题欢迎随时与我联系,我会及时为您解惑,提供帮助。 【资源内容】:包含完整源码+工程文件+说明(如有)等。答辩评审平均分达到96分,放心下载使用!可轻松复现,设计报告也可借鉴此项目,该资源内项目代码都经过测试运行成功,功能ok的情况下才上传的。 【提供帮助】:有任何使用问题欢迎随时与我联系,我会及时解答解惑,提供帮助 【附带帮助】:若还需要相关开发工具、学习资料等,我会提供帮助,提供资料,鼓励学习进步 【项目价值】:可用在相关项目设计中,皆可应用在项目、毕业设计、课程设计、期末/期中/大作业、工程实训、大创等学科竞赛比赛、初期项目立项、学习/练手等方面,可借鉴此优质项目实现复刻,设计报告也可借鉴此项目,也可基于此项目来扩展开发出更多功能 下载后请首先打开README文件(如有),项目工程可直接复现复刻,如果基础还行,也可在此程序基础上进行修改,以实现其它功能。供开源学习/技术交流/学习参考,勿用于商业用途。质量优质,放心下载使用。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值