Java学习——多线程

本次的好句好段:出自《蛊真人》

每日好句好段:

  • 人就像是一座座的浮冰孤岛,在命运的海洋中漂浮流荡。
  • 人和人的相遇,就像是浮冰孤岛之间的相互碰撞,只要是碰撞,就必有影响。
  • 有时候,浮冰孤岛相互粘在了一起,以“利益”、”亲情”、"友情”、"爱情”、“仇恨”之名。 但是最终,它们都将分开。孤独地走向毁灭。
  • 这就是人生的真相。 可惜人们总是害怕孤独,总要贪恋热闹的人群,总不愿无所事事。 因为当他们面对孤独,往往就会面对痛苦。
  • 但是一旦能直面这种痛苦,人就往往有了才华和勇气。所以。有句俗语一一杰出者必孤独。


前言

一个程序运行后至少有一个进程,一个进程中可以包含多个线程


提示:以下是本篇文章正文内容,下面案例可供参考

一、多线程概述

学习多线程之前,需要了解什么是进程和线程。

1.什么是进程?

进程:进程指正在运行的Java程序;

确切的来说,当一个程序进入内存运行,即变成一个进程,进程是处于运行过程中的程序,并且具有一定独立功能。
2.什么是线程?

线程:线程是进程中的一个执行单元,负责当前进程中程序的执行,一个进程中至少有一个线程。

一个进程中是可以有多个线程的,这个应用程序也可以称之为多线程程序。
3.什么是多线程呢?

即就是一个程序中有多个线程在同时执行。

简而言之:一个程序运行后至少有一个进程,一个进程中可以包含多个线程

二、实现线程

1.继承Thread类

  • 创建线程的步骤:

      1.定义一个类继承Thread;
      2.重写run方法;
      3.创建子类对象,就是创建线程对象;
      4.调用start方法,开启线程并让线程执行,同时还会告诉jvm去调用run方法;
    

代码如下(示例):

//定义一个类继承Thread
public class AaImpl extends Thread {
//重写run方法
    @Override
    public void run() {
        for (int i=0;i<100;i++){
            System.out.println(getName()+":"+i);
        }

    }
}
public class Thread1 {
    public static void main(String[] args) {
    //创建子类对象,就是创建线程对象。
        AaImpl aa = new AaImpl();
        //调用start方法,开启线程并让线程执行
        aa.start();
        //抛出异常
        try {
        //sleep()使当前正在执行的线程以指定的毫秒数暂停(暂时停止执行)。
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        //这明显又是一个线程(单线程)
        for (int i=0;i<100;i++){
            System.out.println("main:"+i);
        }
        //主方法中代码的执行时多线程吗?
        //主方法是单线程。
    }
}
注意:
在 "继承Thread类" 这个实现线程的方法中,你创建子类对象,就是创建一个线程对象,并调用它,这属于单线程。
但是你如果是创建多个线程对象,并调用它,这就属于多线程。
所以说,这种实现线程的方法用着会比较麻烦,一般我们都会用另一种实现线程的方法 "Runnable接口 " 。
	//这是单线程
	AaImpl aa = new AaImpl();
    aa.start();
    
	//这是多线程
	AaImpl ae = new AaImpl();
	AaImpl ab = new AaImpl();
	AaImpl ac = new AaImpl();
    ae.start();
    ab.start();
    ac.start();

2.实现Runnable接口

  • 创建线程的步骤:

      1.定义类实现Runnable接口;
      2.重写接口中的run方法;
      3.创建Thread类的对象
      4.将Runnable接口的子类对象作为参数传递给Thread类的构造函数。
      5.调用Thread类的start方法开启线程。
    

代码如下(示例):

public class MyThread2 implements Runnable {

	@Override
	public void run() {
		for (int i = 0; i < 100; i++) {
			//Thread t = Thread.currentThread();
			//System.out.println(t.getName() + ":" + i);
			
			//链式编程
			System.out.println(Thread.currentThread().getName() + ":" + i + num);
		}
	}
 
}

public class ThreadDemo {
	public static void main(String[] args) {
		// 创建线程实例
		MyThread2 mt = new MyThread2();
		Thread t = new Thread(mt);
		t.setName("李四");
		// 啟動線程
		t.start();

		// 创建线程实例
		MyThread2 mt2 = new MyThread2();
		Thread t2 = new Thread(mt2);
		t.setName("老王");
		// 啟動線程
		t.start();
	}
}
注意:
这一种实现线程的方法虽然与"继承Thread类" 这个实现线程的方法的用法相差不大,但是我们更普遍使用这一种方法实现线程,因为它好用啊。

三、常用功能

  • 构造方法:
    • public Thread() :分配一个新的线程对象。
    • public Thread(String name) :分配一个指定名字的新的线程对象。
    • public Thread(Runnable target) :分配一个带有指定目标新的线程对象。
    • public Thread(Runnable target,String name) :分配一个带有指定目标新的线程对象并指定名字。
  • 常用方法:
    • public String getName() :获取当前线程名称。
    • public void start() :导致此线程开始执行; Java虚拟机调用此线程的run方法。
    • public void run() :此线程要执行的任务在此处定义代码。
    • public static void sleep(long millis) :使当前正在执行的线程以指定的毫秒数暂停(暂时停止执行)。
    • public static Thread currentThread() :返回对当前正在执行的线程对象的引用。

四、Thread和Runnable的区别

如果一个类继承Thread,则不适合资源共享。但是如果实现了Runable接口的话,则很容易的实现资源共享。

  • 实现Runnable接口比继承Thread类所具有的优势:
    1. 适合多个相同的程序代码的线程去共享同一个资源。
    2. 可以避免java中的单继承的局限性。
    3. 增加程序的健壮性,实现解耦操作,代码可以被多个线程共享,代码和线程独立。
    4. 线程池只能放入实现Runable或Callable类线程,不能直接放入继承Thread的类。

案例

需求:用三个线程模拟三个售票窗口,共同卖100张火车票,每个线程打印出卖第几张票

public class TicketThread implements Runnable {
	int tickets = 100;// 火车票数量

	@Override
	public void run() {
	// 出售火车票
		while (true) {
	// 当火车票小于0张,则停止售票
			if (tickets > 0) {
/*
 * t1,t2,t3 假设只剩一张票 t1过来了,他一看有票,他就进来了,但是他突然肚子不舒服,然后他就去上卫生间了
 * t2也过来了,他一看也有票,他也进来了,但是他的肚子也不舒服,他也去上卫生间了
 * t1上完了卫生间回来了,开始售票 tickets = 0; t2也上完卫生间回来了,他也进行售票 tickets = -1;
 * */
				try {
					Thread.sleep(100);
				} catch (InterruptedException e) {
				// TODO Auto-generated catch block
					e.printStackTrace();
				}
				System.out.println(Thread.currentThread().getName() + ":" + tickets--);
			}
		}
	}
}

public class TicktetTest {
	public static void main(String[] args) {
		//创建线程对象
		TicketThread tt = new TicketThread();
		
		Thread t = new Thread(tt);
		t.setName("窗口1");
		Thread t2 = new Thread(tt);
		t2.setName("窗口2");
		Thread t3 = new Thread(tt);
		t3.setName("窗口3");
		
		//启动线程对象
		t.start();
		t2.start();
		t3.start();
	}
}

五、线程同步

当多个线程操作同一个对象时,就会出现线程安全问题,被多个线程同时操作的对象数据可能会发生错误。线程同步(synchronized)可以保证在同一个时刻该对象只被一个线程访问。

  • synchronized关键字
    • 那么怎么去使用呢?有三种方式完成同步操作:
      • 同步代码块
        synchronized 关键字可以用于方法中的某个区块中,表示只对这个区块的资源实行互斥访问。

         语法格式:
         	synchronized(同步锁){ 
         		//需要同步操作的代码 
         	}
         	同步锁: 对象的同步锁只是一个概念,可以想象为在对象上标记了一个锁.。
         		1. 锁对象 可以是任意类型。 
         		2. 多个线程对象 要使用同一把锁。 
         		3. 注意:在任何时候,最多允许一个线程拥有同步锁,谁拿到锁就进入代码块,其他的线程只能在外等着 (BLOCKED)
        
      • 同步方法
        使用synchronized修饰的方法,就叫做同步方法,保证A线程执行该方法的时候,其他线程只能在方法外 等着。

          语法格式:
          public synchronized void method(){ 
          	//可能会产生线程安全问题的代码 
          }
        
      • 锁机制 ( Lock锁 )

        • Lock锁也称同步锁,加锁与释放锁方法化了,如下:
          • public void lock() :加同步锁。
          • public void unlock() :释放同步锁。

六、线程状态

线程的生命周期共有 6 种状态,分别是:新建 New、运行(可运行)Runnable、阻塞Blocked、计时等待Timed Waiting、等待Waiting 和终止Terminate。
在这里插入图片描述

线程状态导致状态发生条件
NEW (新建)线程刚被创建,但是并未启动。还没调用start方法。
Runnable (可运行)线程可以在java虚拟机中运行的状态,可能正在运行自己代码,也可能没有,这取决于操作系统处理器。
Running (运行状态)线程获取CPU权限进行执行。需要注意的是,线程只能从就绪状态进入到运行状态。
Blocked (锁阻塞)当一个线程试图获取一个对象锁,而该对象锁被其他的线程持有,则该线程进入Blocked状 态;当该线程持有锁时,该线程将变成Runnable状态。
Waiting (无限等待)一个线程在等待另一个线程执行一个(唤醒)动作时,该线程进入Waiting状态。进入这个 状态后是不能自动唤醒的,必须等待另一个线程调用notify或者notifyAll方法才能够唤醒。
Timed Waiting (计时等待)同waiting状态,有几个方法有超时参数,调用他们将进入Timed Waiting状态。这一状态 将一直保持到超时期满或者接收到唤醒通知。带有超时参数的常用方法有Thread.sleep 、 Object.wait。
Teminated (被终止)因为run方法正常退出而死亡,或者因为没有捕获的异常终止了run方法而死亡。

总结

  • 实现线程的创建方法:
    都是先定义一个类“继承Thread类”或者是“实现Runnable接口”,接着就是重写“run()”方法,然后就是创建线程对象,最后调用start()方法启动线程。
  • Thread和Runnable的区别
    继承Thread,则不适合资源共享。
    实现Runable接口,则很容易的实现资源共享。
  • 线程同步
    如果不想操作线程时数据出现错误就加上 synchronized ,当然你要是想让它出错,就当我没说过就这话。
  • 线程状态
    只需了解线程的生命周期是怎么走的就行了

路在脚下,继续前行即可。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值