多线程

1.进程

进行中的应用程序,只有一个应用程序处于运行状态,才能被称之为进程。
应用程序的执行实例,有独立的内存空间和系统资源。

2.线程

CPU执行的最小单位,包含在进程中。
CPU调度和分派的基本单位,应用程序运算是最小单位。

3.进程和线程的关系

进程和线程的关系,就像车身和车轮的关系,不是越多越好,要根据实际的硬件环境,选择最合适的数量。

4.CPU和线程的执行

单核CPU下,多线程是轮流交替执行。每个任务最多执行20ms,每个任务准备好以后,会向CPU发出指令:准备好了
真正是否能够被执行,不确定,因为同时可能有很多线程都准备好了

5.并发和并行

并发是指同时发生,轮流交替来执行
并行是真正意义上的同时执行

6.线程的创建

6.1继承Thread类

线程的创建方式1:继承Thread类 重写run方法

package com.qfedu.test2;
/**
 * 	线程的创建方式1:继承Thread类  重写run方法
 * @author WHD
 *
 */
public class T1 extends Thread{
	@Override
	public void run() {
		System.out.println(Thread.currentThread().getName());
	}
	
	
	public static void main(String[] args) {
		T1 t1 = new T1();
		t1.setName("线程A");
		t1.start(); // 调用start方法才会开启新的线程 
//		t1.run(); 调用run方法不会开启新的线程 依然使用main线程调用run方法
	}

}

6.2实现Runnable接口

创建线程方式2:实现Rruuable接口,重写run方法

package com.qfedu.test2;
/**
 * 	创建线程方式2 :实现Runnable接口 重写run方法
 * @author WHD
 *
 */
public class T2 implements Runnable{

	@Override
	public void run() {
		System.out.println(Thread.currentThread().getName());
	}
	
	
	public static void main(String[] args) {
		T2 t2 = new T2();
		Thread thread = new Thread(t2, "线程A");
		thread.start();
	}
}

6.3两种方式对比

继承Thread类
编写简单,可直接操作线程
适用于单继承
实现Runnable接口
避免单继承局限性
便于共享资源
推荐使用实现runnable接口的方式创建线程

7.线程的状态

线程的状态
创建--就绪-- 阻塞--死亡

package com.qfedu.test3;
/**
 * 	线程的状态
 * 	创建-->就绪-->运行-->阻塞-->死亡
 * @author WHD
 *
 */
public class T1 extends Thread{
	@Override
	public void run() { // 运行
		try {
			Thread.sleep(2000); // 阻塞
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		System.out.println(Thread.currentThread().getName());
	}
	// 死亡
	
	
	public static void main(String[] args) {
		T1 t1 = new T1(); // 创建
		t1.setName("线程A");
		t1.start(); // 就绪
	}

}

8.线程的优先级

线程的优先级,默认为5,最低为1,最高位10
优先级高代表获取CPU的概率较大,并不能保证一定会优先获得CPU资源
MAX_PRIORITY最高10
MIN_PRIORITY最低1

package com.qfedu.test4;
/**
 * 	线程的优先级 默认为5 最低为1  最高为10
 * 	优先级高代表获取CPU的概率较大 并不能保证一定会优先获得CPU资源
 * @author WHD
 *
 */
public class T1 extends Thread{
	@Override
	public void run() {
		for (int i = 0; i < 10; i++) {
			System.out.println(Thread.currentThread().getName() + i);
		}
	}
	
	
	public static void main(String[] args) {
		T1 thread1 = new T1();
		thread1.setName("赵四");
		T1 thread2 = new T1();
		thread2.setName("广坤");
		
		System.out.println(thread1.getPriority()); 
		System.out.println(thread2.getPriority());
		
		
		thread1.setPriority(Thread.MAX_PRIORITY); // 设置优先级为最高  10
		thread2.setPriority(1);// 设置优先级为最低 1
		
		
		
		thread1.start();
		thread2.start();
		
		
	}

}

9.线程的休眠

线程休眠的方法sleep(long mills)

package com.qfedu.test5;
/**
 * 	线程休眠的方法 sleep(long mills)
 * 	
 * @author WHD
 *
 */
public class T1 extends Thread{
	@Override
	public void run() {
		try {
			Thread.sleep(3000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		System.out.println(Thread.currentThread().getName());
	}
	
	
	public static void main(String[] args) {
		T1 t1 = new T1();
		t1.setName("线程A");
		t1.start();
	}

}

10.线程的插队

线程的插队
join()等待插队线程执行完毕,再执行当前线程
join(long mills)等待插队线程固定时间,时间就执行当前线程

package com.qfedu.test5;
/**
 * 	线程的插队
 * 	join() 等待插队线程执行完毕 再执行当前线程
 * 	join(long mills) 等待插队线程固定时间 时间结束就执行当前线程
 * @author WHD
 *
 */
public class T2 extends Thread{
	@Override
	public void run() {
		for (int i = 0; i < 50; i++) {
			try {
				Thread.sleep(100);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			System.out.println(Thread.currentThread().getName() + i);
		}
	}
	
	
	public static void main(String[] args) throws InterruptedException {
		T2 t2 = new T2();
		t2.setName("--------线程A--------");
		t2.start();
		
		for (int i = 0; i < 20; i++) {
			if(i == 10) {
				t2.join(1000);
			}
			System.out.println(Thread.currentThread().getName() + i);
		}
		
		
		
	}
}

11.线程的礼让

yield()方法,线程礼让,当前线程礼让其他线程,让其他线程先执行
只是提供一种可能,不一定会礼让

package com.qfedu.test6;
/**
 * 	yield()方法  线程礼让  当前线程礼让其他线程  让其他线程先执行
 * 	只是提供一种可能 不一定会礼让
 * @author WHD
 *
 */
public class T1 extends Thread{
	@Override
	public void run() {
		for (int i = 0; i < 20; i++) {
			try {
				Thread.sleep(100);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			if(i == 5) {
				Thread.yield();
				System.out.println(Thread.currentThread().getName() + "礼让了------------------");
			}
			System.out.println(Thread.currentThread().getName() + i);
		}
	}
	
	public static void main(String[] args) {
		T1 thread1 = new T1();
		T1 thread2 = new T1();
		thread1.setName("线程A");
		thread2.setName("线程B");
		
		thread1.start();
		thread2.start();
		
		
	}
}

12.线程的中断

interrupt()中断线程的方法,但不是立即中断,如果当前线程有未执行完的任务,将执行完毕再中断
inerrupted()查看当前线程是否可以被中断,true为是,false为否
stop()为立即中断线程的方法

package com.qfedu.test6;
/**
 * 	interrupt() 中断线程方法  但不是立即中断 如果当前线程有未执行完的任务 将执行完毕再中断
 * 	interrupted() 查看当前线程是否可以被中断  true为是 false为否
 * 	stop() 为立即中断线程的方法
 * @author WHD
 *
 */
public class T2 extends Thread{
	@Override
	public void run() {
		for (int i = 0; i < 20; i++) {
			if(i == 10) {
				System.out.println("执行了线程中断方法interrupt()");
				Thread.currentThread().interrupt();
//				boolean flag = Thread.interrupted();
//				System.out.println(flag);
			}
			System.out.println(Thread.currentThread().getName() + i);
		}
	}
	
	
	public static void main(String[] args) throws InterruptedException {
		T2 t2 = new T2();
		t2.setName("线程A");
		t2.start();
		Thread.sleep(2000);
		System.out.println(t2.isAlive()); // 查看t2线程是否存活
		
		
	}

}

13.线程安全

多线程共享数据引发的问题

package com.qfedu.test8;
/**
 * 	因为T1类中count是实例类型的 所以每new一个对象 就存在一份拷贝 有三个count = 10
 * 	所以我们将count改为static修饰的
 * 	修改完以后:
 * 	1.同一张票重复卖出
 * 	2.在run方法中添加线程休眠 发现票的数量有误
 * @author WHD
 *
 */
public class T2 extends Thread{
	private  static int count = 10;
	@Override
	public void run() {
		while(count > 0) {
			try {
				Thread.sleep(500);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			count--;
			System.out.println(Thread.currentThread().getName() + "抢到了第"+ (10 - count)+" 张票,还剩余" + count + "张票");
		}
	}
	
	public static void main(String[] args) {
		T2 zhaosi = new T2();
		T2 guangkun = new T2();
		T2 dana = new T2();
		zhaosi.setName("赵四");
		guangkun.setName("广坤");
		dana.setName("大拿");
		
		
		zhaosi.start();
		guangkun.start();
		dana.start();
		
	}
}

解决方案:使用同步代码块

package com.qfedu.test8;
/**
 * 	
 * @author WHD
 *
 */
public class T4 implements Runnable{
	int count = 10;
	@Override
	public void run() {
		while(true ) {
			try {
				Thread.sleep(500);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			synchronized(new Object()) {
				if(count == 0) {
					break;
				}
				count--;
				System.out.println(Thread.currentThread().getName() + "抢到了第"+ (10 - count)+" 张票,还剩余" + count + "张票");
			}
		}
		// 后续代码
	}
	
	public static void main(String[] args) {
		T4 t3 = new T4();
		Thread zhaosi = new Thread(t3, "赵四");
		Thread guangkun = new Thread(t3, "广坤");
		Thread dana = new Thread(t3, "大拿");
		
		zhaosi.start();
		guangkun.start();
		dana.start();
		
	}

}

使用同步方法实现

package com.qfedu.test8;
/**
 * 	synchronized修饰方法 表示此方法同一时间只能有一个线程访问
 * @author WHD
 *
 */
public class T5 implements Runnable{
	int count = 10;
	@Override
	public synchronized void run() {
		while(count > 0) {
			try {
				Thread.sleep(500);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			count--;
			System.out.println(Thread.currentThread().getName() + "抢到了第"+ (10 - count)+" 张票,还剩余" + count + "张票");	
		}
	// 后续代码
}

public static void main(String[] args) {
	T5 t3 = new T5();
	Thread zhaosi = new Thread(t3, "赵四");
	Thread guangkun = new Thread(t3, "广坤");
	Thread dana = new Thread(t3, "大拿");

	zhaosi.start();
	guangkun.start();
	dana.start();

}
}

14.synchronized关键字规则

同一时刻只能有一个线程进入synchronized(this)同步代码块
当一个线程访问一个synchronized(this)同步代码块时,其他synchronized(this)同步代码块同样被锁定
当一个线程访问一个synchronized(this)同步代码块时,其他线程可以访问该资源的非synchronized(this)同步代码

posted @ 2021-07-28 22:32  码丁XIA  阅读(53)  评论(0)    收藏  举报