学习多线程基础,这一篇就够啦!(一)

目录

第一部分:基本概念

第二部分:如何创建一个线程

创建线程方式一:继承Thread类。

创建线程方式二:实现Runnable接口。

例子:四个窗口一起共卖100张票(有待改进版)

第三部分:线程的安全问题

例子:

线程安全问题产生的原因:

线程安全问题解决思路:

单例模式涉及的多线程问题

 

第一部分:基本概念

要明白什么是线程,先要明白什么是进程。

  1. 进程:简单理解为正在进行中的程序
  2. 线程:就是进程中一个负责程序执行的控制单元(执行路径)
  3. 多线程:一个进程中有多个执行路径。(马路上有多车道)

 

开启多个线程是为了同时运行多部分代码(杀毒的同时清理垃圾)。

 

每一个线程都有自己运行的内容。这个内容可以称为线程要执行的任务。

 

多线程的好处:解决了多部分代码同时运行的问题。

多线程的弊端:线程太多 效率降低。(因为应用程序的执行都是cpu在做着快速的切换完成的。这个切换是随机的。)

 

JVM启动时就启动了多个线程,至少有两个线程可以分析的出来。

1,执行main函数的线程,该线程的任务代码都定义在main函数中。

2,finalize 负责垃圾回收的线程。

 

第二部分:如何创建一个线程

创建线程方式一:继承Thread类。

步骤:

1,定义一个类继承Thread类。

2,覆盖Thread类中的run方法。(run方法中定义就是线程要运行的任务代码。)

3,直接创建Thread的子类对象创建线程。

4,调用start方法(不是run方法!)开启线程并调用线程的任务run方法执行。

 

注释:

  1. 不是类中的所有代码都需要被线程执行。为了区分哪些代码能够被线程执行,java提供了Thread类的run()方法来包含那些被线程执行的代码。
  2. 调用run()和调用start()的区别:调用run()仅仅是普通的调用,是单线程的,而调用start()分为了两步,先启动了线程,再由jvm调用该线程的run()。
class Demo2 extends Thread    //创建线程的第一步:继承Thread类
{
	private String  name;
	Demo2(String name)
	{
		
		this.name = name;
	}
	
	
	public void run()       //创建线程的第二步: 覆盖Thread类中的run方法:写入要执行的代码
	{
		
		//需要此线程完成的功能代码
		System.out.println("名字是"+name);
	}
}

class ThreadDemo2 
{
	public static void main(String[] args) {
		

		Demo2 d1 = new Demo2("旺财");   //创建线程的第三步:创建Thread的子类对象创建线程。
		Demo2 d2 = new Demo2("xiaoqiang");
		d1.start();//创建线程的第四步:调用start方法开启线程并调用线程的任务run方法执行,虚拟机会调用该线程的run方法
		d2.start();
		 
		System.out.println("over...."+Thread.currentThread().getName()); //获取线程的名称,格式:Thread-编号(从0开始)
	}
	
}

 

 

(补充:currentThread():返回当前正在执行的线程对象)

 

三种运行结果:(多线程理解为不同车道,不同车道实际车速不同,故运行结果会有偏差)

 

 

 

 

 

创建线程方式二:实现Runnable接口。

1,定义类实现Runnable接口。

2,覆盖接口中的run方法,将线程的任务代码封装到run方法中。

3,通过Thread类创建线程对象,并将Runnable接口的子类对象作为Thread类的构造函数的参数进行传递。

       为什么?因为线程的任务都封装在Runnable接口子类对象的run方法中。

       所以要在线程对象创建时就必须明确要运行的任务。

4,调用线程对象的start方法开启线程。

class Demo3 implements Runnable //创建线程的第一步:定义类实现Runnable接口。
{
	public void run()//创建线程的第二步:覆盖接口中的run方法,将线程的任务代码封装到run方法中。
	{
		for(int x=0; x<20; x++)
		{
			System.out.println(Thread.currentThread().getName()+"....."+x);
		}
	}
}


class  ThreadDemo3
{
	public static void main(String[] args) 
	{	
		Demo3 d = new Demo3();
		Thread t1 = new Thread(d);//创建线程的第三步:通过Thread类创建线程对象,并将Runnable接口的子类对象作为Thread类的构造函数的参数进行传递。
		Thread t2 = new Thread(d);
		t1.start();//创建线程的第四步:调用线程对象的start方法开启线程。
		t2.start();
	}
}

 

 

实现Runnable接口的好处:

1,将线程的任务从线程的子类中分离出来,进行了单独的封装。

       按照面向对象的思想将线程任务的封装成对象。

2,避免了java单继承的局限性。

所以,创建线程的第二种方式较为常用。

 

 

例子:四个窗口一起共卖100张票(有待改进版)

class Ticket implements Runnable
{
	private  int num = 100; 
	public void run()
	{
		while(true)
		{
			if(num>0)
				{
					System.out.println(Thread.currentThread().getName()+" is saleing 第"+num--+" 张票");
				}
		}
	}
}

class  TicketDemo
{
	public static void main(String[] args) 
	{

		Ticket t = new Ticket();//创建一个线程任务对象。

		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();
//		t1.start();
//		t1.start();//一个线程不能开启两次 (线程结束后也不能再次开启),会抛出无效线程状态异常 illegalThreadStateException
	}
}

 

 

运行结果(截取部分,其实是按顺序卖的,只是卖掉了来不及打印,所以打印出来是乱序):

 

线程的状态图:

 

设置线程的优先级:

 

 

 

第三部分:线程的安全问题

例子:

在售票过程中,非理想情况下,当多个线程同时进入判断语句(假设此时num=1,线程认为符合条件,进入),如下图。线程需要排队等待处理。执行线程0后,num=0,再执行线程1后,num=0,(此时已不对,不能售出第0张票)再执行线程2与线程3,就会造成不对的执行后果。

 

不正确的后果:

 

 

线程安全问题产生的原因:

1,多个线程在操作共享的数据。

2,操作共享数据的线程代码有多条。

 

当一个线程在执行操作共享数据的多条代码过程中,其他线程参与了运算。

就会导致线程安全问题的产生。

 

 

线程安全问题解决思路:

就是将多条操作共享数据的线程代码封装起来,当有线程在执行这些代码的时候,

其他线程时不可以参与运算的。必须要当前线程把这些代码都执行完毕后,其他线程才可以参与运算。 (简单的说就是不让线程同时做同一件事,得排队)

 

 

在java中,用同步代码块(对多条语句的封装)就可以解决这个问题。

 

同步代码块的格式:

synchronized(对象)

{

       需要被同步的代码 ;

}

 

 

同步函数:可视作同步代码块的简写形式,将synchronized关键字放入函数即为同步函数

同步函数和同步代码块的区别:

同步函数的锁是固定的this,谁调用谁是锁

同步代码块的锁是任意的对象。

 

静态的同步函数使用的锁是  该函数所属字节码文件对象

可以用 getClass方法获取,也可以用当前  类名.class 表示。

 

 

售票例子改进版:

class Ticket implements Runnable
{
	private  int num = 100; 
	Object object =new Object();
	public void run()
	{
		while(true)
		{
			synchronized(object)
			{
			if(num>0)
				{
					try{Thread.sleep(10);}catch (InterruptedException e){}  
					System.out.println(Thread.currentThread().getName()+" is saleing 第"+num--+" 张票");
				}
			}
		}
	}
}

 

 

 

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

同步的弊端:相对降低了效率,因为同步外的线程的都会判断同步锁。可能会发生死锁

 

同步的前提:同步中必须有多个线程并使用同一个锁。

 

 

单例模式涉及的多线程问题

//懒汉式
class Single{
	private static Single single =null;
	
	private Single() {}
	
	public static Single getInstance() {
		if(single==null)  //加入双重判断是为了解决效率问题。   
			//具体流程为:线程们判断single是否为空,先判断完成为空的线程进同步代码块,则其他的线程无法进去,
			//先进去的执行完出来以后,后面的进程进去再进行判断,发现不为空,则不用再new
		{
			synchronized (Single.class) //加入同步为了解决多线程安全问题。
			{
				if(single==null)
					single=new Single();


			}
		}
		return single;
	}
}

学习多线程基础,这一篇就够啦!(二):https://blog.csdn.net/weixin_43827227/article/details/96982701

 

多线程总结:

1,进程和线程的概念。

       |--进程:

       |--线程:

2,jvm中的多线程体现。

       |--主线程,垃圾回收线程,自定义线程。以及他们运行的代码的位置。

3,什么时候使用多线程,多线程的好处是什么?创建线程的目的?

       |--当需要多部分代码同时执行的时候,可以使用。

4,创建线程的两种方式。★★★★★

       |--继承Thread

              |--步骤

       |--实现Runnable

              |--步骤

       |--两种方式的区别?

5,线程的5种状态。

       对于执行资格和执行权在状态中的具体特点。

       |--被创建:

       |--运行:

       |--冻结:

       |--临时阻塞:

       |--消亡:

6,线程的安全问题。★★★★★

       |--安全问题的原因:

       |--解决的思想:

       |--解决的体现:synchronized

       |--同步的前提:但是加上同步还出现安全问题,就需要用前提来思考。

       |--同步的两种表现方法和区别:

       |--同步的好处和弊端:

       |--单例的懒汉式。

       |--死锁。

      

7,线程间的通信。等待/唤醒机制。

       |--概念:多个线程,不同任务,处理同一资源。

       |--等待唤醒机制。使用了锁上的 wait notify notifyAll.  ★★★★★

       |--生产者/消费者的问题。并多生产和多消费的问题。  while判断标记。用notifyAll唤醒对方。 ★★★★★

       |--JDK1.5以后出现了更好的方案,★★★

              Lock接口替代了synchronized 

              Condition接口替代了Object中的监视方法,并将监视器方法封装成了Condition

              和以前不同的是,以前一个锁上只能有一组监视器方法。现在,一个Lock锁上可以多组监视器方法对象。

              可以实现一组负责生产者,一组负责消费者。

       |--wait和sleep的区别。★★★★★

      

8,停止线程的方式。

       |--原理:

       |--表现:--中断。

9,线程常见的一些方法。

       |--setDaemon()守护线程

       |--join();

       |--优先级

       |--yield();

       |--在开发时,可以使用匿名内部类来完成局部的路径开辟。

发布了38 篇原创文章 · 获赞 6 · 访问量 1929

猜你喜欢

转载自blog.csdn.net/weixin_43827227/article/details/96606212