多线程
1.概念
1.1 进程
进程:进行(执行)中的应用程序,我们称之为进程。进程属于CPU分配资源的最小单位。
1.2 线程
线程:线程属于CPU执行调度的最小单位。
1.3 进程和线程的关系
1.线程包含在进程之中,一个进程至少包含一个线程。
举例:进程和线程就像车身和车轮,车轮不是越多越好(线程不是越多越好,要根据实际的硬件环境)
1.4 线程的执行
多个线程是由CPU根据每个线程分配的时间片来决定随机轮流执行的。每个线程最多占用20ms,过了这个时间将切换的其他的线程。
1.5 并发和并行
举例:比如你去饭店点了两盘菜,最后你将这两盘菜吃完了,从饭店老板的角度来看属于同时吃完的,但是严格意义上来说,属于轮流交替吃完的。这中属于并发。并行是指比如你和你的同桌两个人,一人点一盘菜,这样才属于并行。
并发:同时发生,轮流交替来执行
并行:真正意义上的同时执行
2.线程的创建
2.1 继承Thread类
1.继承Thread类,重写run()方法
2.适用于单继承,书写简单
package com.qfedu.test1;
/**
* 创建线程方式1:
* 继承Thread类 重写run方法 创建线程对象
* @author WHD
*
*/
public class Test2 extends Thread{
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "在 执行");
}
public static void main(String[] args) {
Test2 th1 = new Test2();
th1.start();
// th1.run();
Test2 th2 = new Test2();
th2.start();
}
}
2.2 实现Runnable接口
实现Runnable接口,重写run()方法
1.避免了单继承的局限性
2.便于数据的共享
package com.qfedu.test1;
/**
* 创建线程方式2:
* 实现Runnable接口 重写run方法
*
* @author WHD
*
*/
public class Test3 implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "在执行");
}
public static void main(String[] args) {
Test3 t = new Test3();
Thread th1 = new Thread(t, "线程A");
th1.start();
// th1.run();
}
}
2.3 调用start方法和调用run方法的区别(面试题)
调用start方法将会开启一个新的线程,但是调用run方法不会开启新的线程,只是使用main线程调用方法。
3.线程的状态(面试题)
1.创建 new Thread对象
2.就绪 start方法
3.运行 run方法
4.阻塞 sleep 、join方法
5.死亡 线程执行完毕
package com.qfedu.test2;
/**
* 线程的状态
* @author WHD
*
*/
public class Test2 implements Runnable{
@Override
public void run() { // 执行
try {
// 阻塞
Thread.sleep(2000); // 休眠属于阻塞状态的表现之一
} catch (InterruptedException e) {
e.printStackTrace();
}
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName() + "线程" + i);
}
// 死亡
}
public static void main(String[] args) {
Test2 t1 = new Test2();
Thread th1 = new Thread(t1,"线程A"); // 创建
Thread th2 = new Thread(t1,"线程B");
th1.start(); // 就绪
th2.start();
}
}
4.线程常用的方法
currentThread() 获取当前线程对象
getName() 获取线程名称
setName() 设置线程名称
getPriority() 获取线程优先级
stop() 停止当前线程
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dp5tmCjH-1603197207912)(.\img\线程方法1.png)]
package com.qfedu.test2;
/**
* 线程的优先级
* 默认为5 最大10 最小1
* MAX_PRIORITY 10
* MIN_PRIORITY 1
* NORM_PRIORITY 5
* setPriority()设置优先级
* getPriority()获取优先级
* @author WHD
*
*/
public class Test3 extends Thread{
@Override
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName() + "线程执行" + i);
}
}
public static void main(String[] args) {
Test3 th1 = new Test3();
Test3 th2 = new Test3();
th1.setName("线程A");
th2.setName("线程B");
th1.setPriority(Thread.MAX_PRIORITY);
th2.setPriority(Thread.MIN_PRIORITY);
System.out.println(th1.getPriority());
System.out.println(th2.getPriority());
th1.start();
th2.start();
}
}
package com.qfedu.test2;
/**
* 线程的休眠
* sleep(long 毫秒)
* sleep(long 毫秒,int 纳秒)
* @author WHD
*
*/
public class Test4 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) {
Test4 th1 = new Test4();
th1.start();
}
}
package com.qfedu.test2;
/**
* 线程的插队
* join() 直到插队线程执行完毕 被插队线程再继续执行
* join(long 毫秒) 被插队线程等待指定的时间 时间过后继续轮流交替来执行
* join(long 毫秒,int 纳秒) 被插队线程等待指定的时间 时间过后继续轮流交替来执行
* @author WHD
*
*/
public class Test5 extends Thread{
@Override
public void run() {
for (int i = 0; i < 20; i++) {
System.out.println(Thread.currentThread().getName() + "在执行" + i + "============================");
}
}
public static void main(String[] args) throws InterruptedException {
Test5 th1 = new Test5();
th1.start();
for (int i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getName() + "主线程" + i);
if(i == 10) {
th1.join();
}
}
}
}
package com.qfedu.test2;
/**
* 线程的礼让
* 并不能保证一定会礼让其他线程 只是提供一种可能 用于缓解并发的问题
* yield() 礼让
* @author WHD
*
*/
public class Test6 extends Thread{
@Override
public void run() {
for (int i = 0; i < 20; i++) {
if(i == 3) {
System.out.println("线程礼让");
Thread.yield(); // 当前线程会礼让 如果此时本来执行的是线程A 执行完礼让 有可能会执行线程B
}
System.out.println(Thread.currentThread().getName() + "在执行");
}
}
public static void main(String[] args) {
Test6 th1 = new Test6();
Test6 th2 = new Test6();
th1.setName("线程A");
th2.setName("线程B");
th1.start();
th2.start();
}
}
package com.qfedu.test2;
/**
* 线程的中断(停止)
* interrupt() 设置线程中断标识为true 标识 此线程可以被中断
* interrupted() 打印当前线程的是否可被中断
* stop() 停止线程
* @author WHD
*
*/
public class Test7 extends Thread{
@Override
public void run() {
for (int i = 0; i < 10; i++) {
if(i == 5) {
Thread.currentThread().interrupt();
Thread.currentThread().stop();
}
System.out.println(Thread.currentThread().getName() + "再执行" + i + "是否可被中断状态" + Thread.interrupted());
}
}
public Test7(String name) {
super(name);
}
public static void main(String[] args) {
Test7 th1 = new Test7("线程A");
th1.start();
}
}
package com.qfedu.test2;
/**
* isAlive() 判断线程是否还存活
* @author WHD
*
*/
public class Test8 extends Thread{
@Override
public void run() {
for (int i = 0; i < 10; i++) {
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "执行" + i);
}
}
public static void main(String[] args) throws InterruptedException {
Test8 th1 = new Test8();
th1.setName("线程A");
th1.start();
Thread.sleep(1000);
System.out.println("是否还存活" + th1.isAlive());
Thread.sleep(5000);
System.out.println("是否还存活" + th1.isAlive());
}
}
5.线程安全synchronized关键字
在多线程访问同一个资源的情况,因为线程运行的特点:线程是随机轮流交替来执行的,所以多个线程访问同一个数据,会出现问题,我们可以使用同步关键字来解决
synchronized:
1.用于修饰代码块
a.同一时间只能有一个线程访问同步代码块中的内容
b.但是 其他线程可以访问没有被同步代码块包括的代码
c.其他的同步代码块 同样被锁定 (这些多个同步代码块所锁定的是同一个对象 比如 this)
2.用于修饰方法
表示被同步关键字修饰的方法同一时间只能有一个线程访问
package com.qfedu.test4;
/**
* 假设有10张票
* 三个人抢票
* 票号是唯一的 总数不能超过10张
* 目前存在问题:
* 数量不能保证10个
* 票号不能保证唯一
* synchronized 同步的意思
* 适用场景
* 1.同步代码块
* a.同一时间只能有一个线程访问同步代码块中的内容
* b.但是 其他线程可以访问没有被同步代码块包括的代码
* c.其他的同步代码块 同样被锁定 (这些多个同步代码块所锁定的是同一个对象 比如 this)
* 2.同步方法
* a.表示当前方法同一时间只有一个线程可以访问
* @author WHD
*
*/
public class BuyTicket2 implements Runnable{
private int ticketCount = 10;
Object obj = new Object();
@Override
public void run() {
while(true) {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
// synchronized 同步
synchronized (obj) {
if(ticketCount == 0) {
break;
}
ticketCount --;
System.out.println(Thread.currentThread().getName() + "抢到第"+(10 - ticketCount) +"张票,还剩余" + ticketCount);
}
// synchronized (this) {
// System.out.println("hello word");
// }
// 其他逻辑代码
}
}
public static void main(String[] args) {
BuyTicket2 bt = new BuyTicket2();
Thread zhaosi = new Thread(bt, "赵四");
Thread guangkun = new Thread(bt, "广坤");
Thread dana = new Thread(bt, "大拿");
zhaosi.start();
guangkun.start();
dana.start();
}
}
package com.qfedu.test4;
/**
* 假设有10张票
* 三个人抢票
* 票号是唯一的 总数不能超过10张
* 目前存在问题:
* 数量不能保证10个
* 票号不能保证唯一
* synchronized 同步的意思
* 适用场景
* 1.同步代码块
* a.同一时间只能有一个线程访问同步代码块中的内容
* b.但是 其他线程可以访问没有被同步代码块包括的代码
* c.其他的同步代码块 同样被锁定 (这些多个同步代码块所锁定的是同一个对象 比如 this)
* 2.同步方法
* a.表示当前方法同一时间只有一个线程可以访问
* @author WHD
*
*/
public class BuyTicket3 implements Runnable{
private int ticketCount = 10;
Object obj = new Object();
@Override
public synchronized void run() {
while(ticketCount > 0) {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
ticketCount --;
System.out.println(Thread.currentThread().getName() + "抢到第"+(10 - ticketCount) +"张票,还剩余" + ticketCount);
}
}
public static void main(String[] args) {
BuyTicket3 bt = new BuyTicket3();
Thread zhaosi = new Thread(bt, "赵四");
Thread guangkun = new Thread(bt, "广坤");
Thread dana = new Thread(bt, "大拿");
zhaosi.start();
guangkun.start();
dana.start();
}
}
6. 线程安全的类
线程安全:StringBuffer,Vector,Hashtable 以上这些类的方法都使用同步关键字修饰
线程不安全:StringBuilder,ArrayList,LinkedList,HashSet,HashMap,TreeSet…… 没有使用同步关键字修饰
() + “抢到第”+(10 - ticketCount) +“张票,还剩余” + ticketCount);
}
}
public static void main(String[] args) {
BuyTicket3 bt = new BuyTicket3();
Thread zhaosi = new Thread(bt, "赵四");
Thread guangkun = new Thread(bt, "广坤");
Thread dana = new Thread(bt, "大拿");
zhaosi.start();
guangkun.start();
dana.start();
}
}
#### 6. 线程安全的类
> 线程安全:StringBuffer,Vector,Hashtable 以上这些类的方法都使用同步关键字修饰
>
> 线程不安全:StringBuilder,ArrayList,LinkedList,HashSet,HashMap,TreeSet…… 没有使用同步关键字修饰