学习Java第二十八天--多线程之线程的概念、组成和状态

14.多线程

14.1 什么是线程

14.1.1 什么是进程

  • 程序是静止的,只有真正运行时的程序,才被称为进程;
  • 单核CPU在任何时间点上,只能运行一个进程;
  • 宏观并行、微观串行;

14.1.2 线程的概念

  • 线程,又称轻量级进程(Light Weight Process);
  • 程序中的一个顺序控制流程,同时也是CPU的基本调度单位;
  • 进程由多个线程组成,彼此间完成不同的工作,交替执行,称为多线程;
  • 如:迅雷是一个进程,当中的多个下载任务即是多个线程;
  • Java虚拟机是一个进程,当中默认包含主线程(Main),可通过代码创建多个独立线程,与Main并发执行;

14.1.3 线程的组成

任何一个线程都具有基本的组成部分:

  • CPU时间片:操作系统(OS)会为每个线程分配执行时间;

  • 运行数据:
    堆空间:存储线程需使用的对象,多个线程可以共享堆中的对象;
    栈空间:存储线程需使用的局部变量,每个线程都拥有独立的栈;

  • 线程的逻辑代码;

14.1.4 创建线程

  • 创建线程的第一种方式:
    1.继承Thread类;
    2.覆盖run()方法;
    3.创建子类对象;
    4.调用start()方法;
public class TestExtendsThread {

	public static void main(String[] args) {
		
		MyThread t1 = new MyThread();//创建线程对象
		MyThread2 t2 = new MyThread2();
		
//		t1.run();//直接调用run方法,普通对象调用方法一样
		
		t1.start();//由JVM来调用run方法
		t2.start();

		for(int i = 0 ; i <= 10 ; i++) {
			System.out.println("main - "+i);
		}
	}

}
class MyThread extends Thread{//自定义线程
	public void run() {//线程的任务
		for(int i = 0 ; i <= 10 ; i++) {
			System.out.println("MyThread - "+i);
		}
	}
}

class MyThread2 extends Thread{//自定义线程
	public void run() {//线程的任务
		for(int i = 0 ; i <= 10 ; i++) {
			System.out.println("MyThread2 - "+i);
		}
	}
}
  • 创建线程的第二种方式:
    1.实现Runnable接口;
    2.覆盖run()方法;
    3.创建实现类对象;
    4.创建线程对象;
    5.调用start()方法;
public class TestImplementsRunnable {

	public static void main(String[] args) {
		
		MyRunnable mr1 = new MyRunnable();//1.创建线程类对象
		
		Thread t1 = new Thread(mr1);//2.Thread线程类的有参构造方法
		
		Thread t2 = new Thread(mr1);//不同的线程,执行的是相同的任务
		
		t1.start();//启动线程
		t2.start();
		
		for(int i = 0 ; i < 10 ; i++) {
			System.out.println("Main - "+i);
		}
	}

}
//实现接口,只是将当前类编程线程任务类,本身不是个线程
//任务可以是多个线程共享的
//更灵活:提供了能力,不影响继承
class MyRunnable implements Runnable{
	public void run() {
		for(int i = 0 ; i < 10 ; i++) {
			System.out.println(Thread.currentThread().getName()+" - "+i);
		}
	}
}

14.2 线程的状态

14.2.1 线程的状态(基本)

在这里插入图片描述

14.2.2 常见方法

  • 休眠:
    public static void sleep(long millis)
    当前线程主动休眠millis毫秒;
public class TestSleep {

	public static void main(String[] args) throws Exception{
		
		MyThread3 t1 = new MyThread3();
		t1.start();
		
		//通知完t1之后,main线程休眠2秒
//		Thread.sleep(2000);		
		
		MyRunnable2 task = new MyRunnable2();
		Thread t2 = new Thread(task);
		t2.start();
		
		for(int i = 1 ; i < 10 ; i++) {
			System.out.println(Thread.currentThread().getName()+" - "+i);
			if(i == 6) {//特定条件下休眠
				//通知完t1后,main线程休眠2秒
				Thread.sleep(2000);
			}
		}

	}

}
//线程类
class MyThread3 extends Thread{
	public void run() {
		for(int i = 0 ; i < 10 ; i++) {
			//获得当前线程的线程名称
			System.out.println(Thread.currentThread().getName()+" - "+i);
		}
	}
}
class MyRunnable2 implements Runnable{
	public void run() {
		for(int i = 0 ; i < 10 ; i++) {
			System.out.println(Thread.currentThread().getName() +" - "+i);
			try {
				Thread.sleep(1000);
			}catch(Exception e) {
				
			}
		}
	}
}
  • 放弃:
public static void yield()
当前线程主动放弃时间片,回到就绪状态,竞争下一次时间片;
public class TestYield {

	public static void main(String[] args) {
		
		Thread t1 = new Thread(new Task());
		t1.start();
		
		for(int i = 0 ; i < 10 ; i++) {
			System.out.println(Thread.currentThread().getName()+" - "+i);
			if(i % 2 == 0) {
				System.out.println("main主动放弃了");
				Thread.yield();//放弃:主动放弃当前持有的时间片,进入下一次竞争
			}
		}

	}

}
class Task implements Runnable{
	public void run() {
		for(int i = 0 ; i < 10 ; i++) {
			System.out.println(Thread.currentThread().getName() + " - " +i);
		}
	}
}
  • 结合:
    public final void join()
    允许其它线程加入到当前线程中;
public class TestJoin {
	public static void main(String[] args) throws InterruptedException{
		Thread t1 = new Thread(new Task2());
		Thread t2 = new Thread(new Task2());
		
		t1.start();
		t2.start();
		
		for(int i = 0 ; i < 10 ; i++) {
			System.out.println(Thread.currentThread().getName()+" - "+1);
			if(i == 6) {
				System.out.println("main执行到了20!执行t1");
				t1.join();//将t1加人到main线程执行流程中,等待t1线程执行流程结束后,main再
				//无限期等待:等待条件为调用join方法的线程执行完毕后,再进入就绪状态,竞争时间片
			}
		}
	}
}
class Task2 implements Runnable{
	public void run() {
		for(int i = 0 ; i < 10 ; i++) {
			System.out.println(Thread.currentThread().getName()+" - "+i);
		}
	}
}

14.2.3 线程的状态(等待)

在这里插入图片描述

14.3 线程安全

14.3.1 线程安全问题

在这里插入图片描述

  • 需求:A线程将“Hello”存入数组的第一个空位;B线程将“World”存入数组的第一个空位;
  • 线程不安全: 当多线程并发访问临界资源时,如果破坏原子操作,可能会造成数据不一致;
  • 临界资源:共享资源(同一对象),一次仅允许一个线程使用,才可保证其正确性;
  • 原子操作:不可分割的多步操作,被视作一个整体,其操作和步骤不可打乱或缺省;

14.3.2 同步方式(1)

  • 同步代码块:
    synchronized(临界资源对象){//对临界资源对象加锁
    //代码(原子操作)
    }
  • 注:
    每个对象都有一个互斥锁标记,用来分配给线程的;
    只有拥有对象互斥锁标记的线程,才能进入该对象加锁的同步代码块;
    线程退出同步代码块时,会释放相应的互斥锁标记;
public class TestSynchronized {

	public static void main(String[] args) {
		
		//临界资源对象
		//临界资源对象只有一把锁
		Account acc = new Account("6002" , "1234" , 2000);
		
		//两个线程对象,共享同一银行卡资源对象
		Thread husband = new Thread(new Husband(acc) , "丈夫");
		Thread wife = new Thread(new Wife(acc) , "妻子");
		
		husband.start();
		wife.start();
	}

}
class Husband implements Runnable{
	Account acc;
	public Husband(Account acc) {
		this.acc = acc;
	}
	//线程任务:取款
	public void run() {
//		synchronized(acc) {//对临界资源对象加锁
			this.acc.withdrawal("6002", "1234", 500);
//		}
	}
}
class Wife implements Runnable{
	Account acc;
	public Wife(Account acc) {
		this.acc = acc;
	}
	//线程任务:取款
	public void run() {
//		synchronized(acc) {
			this.acc.withdrawal("6002", "1234", 1200);//原子操作
		}
//	}
}

//银行账户
class Account{
	String cardNo;
	String password;
	double balance;
	
	public Account(String cardNo , String password , double balance) {
		super();
		this.cardNo = cardNo;
		this.password = password;
		this.balance = balance;
	}
	
	//取款(原子操作,从插卡验证,到取款成功的一系列步骤,不可缺少或打断)
	public void withdrawal(String no , String pwd , double money) {
		//
		synchronized(this) {//对当前共享实例加同步锁
			System.out.println(Thread.currentThread().getName()+"正在读卡。。。");
			if(no.equals(this.cardNo) && pwd.equals(this.password)) {
				System.out.println(Thread.currentThread().getName()+"验证成功。。。");
				if(money > this.balance) {
					System.out.println(Thread.currentThread().getName()+" 卡内余额不足");
				}else{
					
					try {
						Thread.sleep(1000);//模拟ATM数钱时间
					}catch(InterruptedException e){
						e.printStackTrace();
					}
					this.balance = this.balance - money;
					System.out.println(Thread.currentThread().getName()+"当前余额为:"+this.balance);
				}
				
			}else {
				System.out.println(Thread.currentThread().getName()+"卡号或密码错误");
			}
		}
	}
}

输出结果:

丈夫正在读卡。。。
丈夫验证成功。。。
丈夫当前余额为:1500.0
妻子正在读卡。。。
妻子验证成功。。。
妻子当前余额为:300.0

14.3.3 线程的状态(阻塞)

在这里插入图片描述

  • 注:JDK5之后,就绪、运行统称Runnable
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值