CHAPTER-14 线程

线程


在这里插入图片描述

应用程序的执行实例 --> 进程 <-- 有独立的内存空间和系统资源

CPU调度和分派的基本单位 --> 线程 <-- 进程中执行运算的最小单位,可完成一个独立的顺序控制流程

多线程

什么是多线程:

1.如果在一个进程中同时运行了多个线程,用来完成不同的工作,则称之为"多线程"

2.多个线程交替占用CPU资源, 而非真正的并行执行

多线程的好处:

充分利用CPU的资源

简化编程模型

带来良好的用户体验

主线程

Thread类

Java提供了java.lang.Thread类支持多线程编程

主线程

main()方法即为主线程入口

产生其他子线程的线程

必须最后完成执行,因为它执行各种关闭动作

-显示主线程名

public static void main(String[] args){
    Thread t = Thread.currentThread();
    System.out.println("当前线程是:"+t.getName());
    t.setName("MyJavaThread");
    System.out.println("当前线程是:"+t.getName());
}

线程的创建和启动

在Java中创建线程的两种方式

继承java.lang.Thread类

实现java.lang.Runnable接口

使用线程的步骤

STEP 1:定义线程

STEP 2:创建线程对象

STEP 3:启动线程

STEP 4:终止线程

继承Thread类创建线程

1.定义MyThread类继承Thread类

2.重写run()方法,编写线程执行体

public class MyThread extends Thread{
    //重写run()方法
    	public void run(){
            for(int i = 0;i<100;i++){
                system.out.println(Thread.currentThread().getName()+":"+i);
            }
        }
}

3.创建线程对象,调用start()方法启动线程

public static void main(String{} args){
    MyThread thread = new MyThread();
    thread.start();//启动线程
}

4.多个线程交替执行,不是真正的"并行"

5.线程每次执行时长由分配的CPU时间片长度决定

MyThread t1 = new MyThread();
MyThread t2 = new MyThread();
t1.start();
t2.start();

启动线程是否可以直接调用run()方法?

主线程调用run():只有主线程一条执行路径

主线程调用start():多条执行路径,主线程和子线程并行交替执行

在这里插入图片描述

实现Runnable接口创建线程

在这里插入图片描述

比较两种创建线程的方式

继承Thread类

1.编写简单,可直接操作线程

2.适用于单继承

实现Runnable接口

1.避免单继承局限性

2.便于共享资源

线程的状态

在这里插入图片描述

线程调度

线程调度指按照特定机制为多个线程分配CPU的使用权

方法说明
void setPriority(int newPriority)更改线程的优先级
static void sleep(long mills)在指定的毫秒数内让当前正在执行的线程休眠
void join()等待该线程终止
static void yield()暂停当前正在执行的线程对象,并执行其他线程
void interrupt()中断线程
boolean isAlive()测试线程是否处于活动状态

线程休眠

1.让线程暂时睡眠指定时长,线程进入阻塞状态

2.睡眠时间过后线程会再进入可运行状态

public static void sleep(long millis)

3.millis为休眠时长,以毫秒为单位

4.调用sleep()方法需处理InterruptedException异常

线程设置优先级

setPriority(Thread.MAX_PRIORITY);

setPriority(Thread.MIN_PRIORITY);

线程休眠

Thread.sleep(毫秒)

线程的强制运行

1.使当前线程暂停执行,等待其他线程结束后再继续执行本线程

public final void join()
public final void join(long mills)
public final void join(long mills,int nanos)

millis:以毫秒为单位的等待时长

nanos:要等待的附加纳秒时长

需处理InterrputedException异常

public static void main(String[] args){
    Thread temp = new Thread(new MyThread());
    temp.start();
    for(int i = 0;i<20;i++){
        if(i == 5){
            try{
                temp.join();//阻塞主线程,子线程强制执行
            }catch(InterruptedException e){
                e.printStackTrace();
            }
        }
  System.out.println(Thread.currentThread().getName()+"运行:"+i);
    }
    //省略代码...
}

线程的礼让

1.暂停当前线程,允许其他具有相同优先级的线程获得运行机会

2.该线程处于就绪状态,不转为阻塞状态

public static void yield()

只是提供一种可能,但是不能保证一定会实现礼让

public class MyThread implements Runnable{
    public void run(){
        for(int i = 0;i<5;i++){
            System.out.println(Thread.currentThread().getName()+"正在运行:"+i);
            if(i == 3){
                System.out.println("线程礼让:");
                Thread.yield();//当=3时,线程礼让
            }
        }
    }
}

yield() VS join()

yield()

1.暂停当前正在执行的线程对象,并执行其他线程,线程礼让

2.只是提供一种可能,但是不能保证一定会实现礼让

区别

yield():礼让,放手。当前线程处于就绪状态

join():阻塞当前线程,直到其他线程执行完毕,当前线程才进入就绪状态

同步代码块

使用synchronized关键字修饰的代码块
synchronized(syncObject){
    //需要同步的代码块
}

syncObject为需同步的对象,通常为this

效果与同步方法相同

public void run(){
    while(true){
        synchronized(this){//同步代码块
            //省略修改数据的代码
            //省略显示信息的代码
        }
    }
}

synchronized就是为当前的线程声明一把锁

多个并发线程访问同一资源的同步代码块时

1.同一时刻只能有一个线程进入synchronized(this)同步代码块

2.当一个线程访问一个synchronized(this)同步代码块时,其他线程synchronized(this)同步代码块同样被锁定

3.当一个线程访问一个synchronized(this)同步代码块时,其他线程可以访问该资源的非synchronized(this)同步代码

多线程共享数据

可能带来的问题:数据不安全

原因:多线程共同操作数据时,引发的冲突(如延迟时,操作未全部完成等)

线程同步:即各线程之间要有个先来后到,不能一窝蜂

线程同步其实是"排队":上锁,排队,一个一个来,不能同时操作

线程安全的类型

查看ArrayList类的add()方法定义

public boolean add(E e){
    //集合扩容,确保能新增数据
    ensureCapacityInternal(size+1);
    //在新增位置存放数据
    elementData[size++]=e;
    return true;
}

ArrayList类的add()方法为非同步方法

当多个线程向同一个ArrayList对象添加数据时,可能出现数据不一致问题

方法是否同步效率比较适合场景
线程安全多线程并发共享资源
非线程安全单线程

为达到安全性和效率的平衡,可以根据实际场景来选择合适的类型

常见类型对比

Hashtable && HashMap
Hashtable

1.继承关系:实现了Map接口,Hashtable继承Dictonary类

2.线程安全,效率较低

3.键和值都不允许为null

HashMap

1.继承关系:实现了Map接口,继承AbstractMap类

2.非线程安全,效率较高

3.键和值都允许为null

StringBuffer && StringBuilder

1.前者线程安全,后者非线程安全



案例

案例一

在这里插入图片描述

-测试类

package hw1;

public class Test {
	public static void main(String[] args) {
		MyThread th = new MyThread();
		MyThread2 th2 = new MyThread2();
		th.start();
		th2.start();
	}
}

-线程类1

package hw1;

public class MyThread extends Thread{

	@Override
	public void run() {
		for(int i = 1;i<21;i++) {
			System.out.println(i+".你好,来自线程a     "+Thread.currentThread().getName());
		}
	}
}

-线程类2

package hw1;

public class MyThread2 extends Thread{

	@Override
	public void run() {
		for(int i = 1;i<21;i++) {
			System.out.println(i+".你好,来自线程b     "+Thread.currentThread().getName());
		}
	}
}

案例二

实现Runnable接口的方式创建线程

需求说明:修改案例一,要求线程类实现Runnable接口的方式创建,并修改测试类

创建Thraed类对象,使用Runnable接口实现类的对象作为构造方法的参数

-测试类

package hw2;

public class Test {
	public static void main(String[] args) {
		MyThread myThread = new MyThread();
		Thread th1 = new Thread(myThread);
		Thread th2 = new Thread(myThread);
		th1.start();
		th2.start();
	}
}

-线程类

package hw2;

public class MyThread implements Runnable{

	@Override
	public void run() {
		for(int i = 1;i<21;i++) {
			System.out.println(i+".你好,来自线程    "+Thread.currentThread().getName());
		}
	}
}

案例三

在这里插入图片描述

-测试类

package hw3;

public class Test {
	public static void main(String[] args) {
		MyThread th1 = new MyThread();
		MyThread2 th2 = new MyThread2();
		th1.setName("年轻人");
		th2.setName("老年人");
		
		System.out.println("********开始爬山********");
		th1.start();
		th2.start();
	}
}

-线程类1

package hw3;

public class MyThread extends Thread{

	@Override
	public void run() {
		for(int i=0;i<10;i++) {
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			System.out.println(Thread.currentThread().getName()+"爬完100米!");

		}System.out.println(Thread.currentThread().getName()+"到达终点!");
	}

}

-线程类2

package hw3;

public class MyThread2 extends Thread{

	@Override
	public void run() {
		for(int i=0;i<10;i++) {
			try {
				Thread.sleep(2000);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			System.out.println(Thread.currentThread().getName()+"爬完100米!");
		}System.out.println(Thread.currentThread().getName()+"到达终点!");
	}
}

案例四

在这里插入图片描述

-测试类

package hw4;

public class Test {
	public static void main(String[] args) {
		//MyThread myThread = new MyThread();
		Thread th1 =Thread.currentThread();//主线程
//		Thread th2 = Thread.currentThread();
		
		
		System.out.println("******显示默认优先级******");
		
		Thread th2 = new MyThread();
		
		th1.setPriority(5);
//		th2.setPriority(5);
		
//		th1.start();
//		th2.start();
		
		System.out.println("主线程名:  "+th1.getName()+",优先级:  "+th1.getPriority());
		System.out.println("子主线程名:  "+th2.getName()+",优先级:  "+th2.getPriority());
		
		System.out.println("******修改默认优先级后******");
		
		th1.setPriority(10);
		th2.setPriority(1);
		System.out.println("主线程名:  "+th1.getName()+",优先级:  "+th1.getPriority());
		System.out.println("子主线程名:  "+th2.getName()+",优先级:  "+th2.getPriority());
	}
}

-线程类

package hw4;

public class MyThread extends Thread{

	@Override
	public void run() {
	}
}

案例五

在这里插入图片描述

-测试类

package hw5;

public class Test {
	public static void main(String[] args) {
		/**
		 * 普通号
		 */
		Thread th1 = Thread.currentThread();
		th1.setPriority(th1.currentThread().MIN_PRIORITY);
		th1.setName("普通号");
		//th1.start();
		
		/**
		 * 特需号
		 */
		Thread th2 = new MyThread();
		th2.setPriority(th2.currentThread().MAX_PRIORITY);
		th2.setName("特需号");
		th2.start();
		
		for(int i = 1; i<51;i++) {
			System.out.println(th1.getName()+": "+i+"号病人正在看病!");
			System.out.println(th2.getName()+": "+i+"号病人正在看病!");
			if(i==10) {
				try {
					th1.join();
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}
	}
}

-线程类

package hw5;

public class MyThread extends Thread{

	@Override
	public void run() {
		
	}
}

案例六

在这里插入图片描述

-测试类

package hw6;

public class Test {
	public static void main(String[] args) {
		Runnable runnable = new RunThread();
		Thread run1 = new Thread(runnable,"1");
		Thread run2 = new Thread(runnable,"2");
		Thread run3 = new Thread(runnable,"3");
		Thread run4 = new Thread(runnable,"4");
		Thread run5 = new Thread(runnable,"5");
		
		run1.start();
		run2.start();
		run3.start();
		run4.start();
		run5.start();		
	}
}

-线程类

package hw6;

public class RunThread implements Runnable {
	private int metres = 1000;
	@Override
	public void run() {
		// TODO Auto-generated method stub
		while(true) {
			synchronized (this) {
				if(metres <100) {
					break;
				}
				System.out.println(Thread.currentThread().getName()+"号选手拿到了接力棒!");
				for(int i = 0 ;i<100;i++) {
					try {
						Thread.sleep(100);
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
					System.out.println(Thread.currentThread().getName()+"号选手跑了"+i+"米");
				}
				metres -= 100;
			}
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}
}

案例七

在这里插入图片描述

-测试类

package hw7;

public class Test {
	public static void main(String[] args) {
		Runnable runnable = new MyThread();
		Thread t1 = new Thread(runnable,"桃跑跑");
		Thread t2 = new Thread(runnable,"张票票");
		Thread t3 = new Thread(runnable,"黄牛党");
		System.out.println("-----------------------------------");
		t1.start();
		t2.start();
		t3.start();
	}
}

-线程类

package hw7;

public class MyThread implements Runnable{
	private int ticket = 10;
	private int num = 0;
	private int hnd=0;
	
	public void run() {
		while(true) {
			synchronized (this) {
				if(ticket <= 0) {
					break;
				}
				if(Thread.currentThread().getName().equals("黄牛党")) {
					if(hnd == 1) {
						return;
					}
					hnd++;
				}
				ticket --;
				num ++;
				try {
					Thread.sleep(200);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				System.out.println(Thread.currentThread().getName()+"抢到了第"+num+"张票,剩余"+ticket+"张票!");
				
			}
		try {
			Thread.sleep(200);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		}
	}
}

package hw7;

public class MyThread implements Runnable{
private int ticket = 10;
private int num = 0;
private int hnd=0;

public void run() {
	while(true) {
		synchronized (this) {
			if(ticket <= 0) {
				break;
			}
			if(Thread.currentThread().getName().equals("黄牛党")) {
				if(hnd == 1) {
					return;
				}
				hnd++;
			}
			ticket --;
			num ++;
			try {
				Thread.sleep(200);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			System.out.println(Thread.currentThread().getName()+"抢到了第"+num+"张票,剩余"+ticket+"张票!");
			
		}
	try {
		Thread.sleep(200);
	} catch (InterruptedException e) {
		// TODO Auto-generated catch block
		e.printStackTrace();
	}
	}
}

}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值