多线程
Process与Thread
注意:
很多多线程是模拟出来的,真正的多线程是指多个CPU即多核,如多个服务器,如果是模拟出来的就是在一个CPU的情况下在同一个 时间切换不同的工作,切换时间很短所以照成同时执行的错觉
- 进程与程序相关联,程序师指令和数据的有序集合,其本身没有任何运行的含义,是一个静态的概念
- 进程是执行程序的一次执行过程,是一个动态的概念,是系统资源分配的单位
- 一个进程可以包含若干个线程,一个进程必须有一个线程不然这个进程没有意义,线程就是CPU调度和执行的单位
继承Thread
- 写一个类继承Thread
- 重写run方法
- 调用start()方法启动线程
- 线程交替执行
- 线程不一定立即执行,由CPU调度执行
- 不建议使用,避免OOP单继承局限性
public class Test extends Thread {
public static void main(String[] args) {
Test test = new Test();
test.start();
for (int i = 0; i <= 10; i++) {
System.out.printf("执行主线程" + i + "\n");
}
}
@Override
public void run() {
for (int i = 0; i <= 10; i++) {
System.out.printf("执行子线程" + i + "\n");
}
}
}
执行主线程0
执行子线程0
执行主线程1
执行子线程1
执行主线程2
执行子线程2
实现Runable接口
- 实现Runable接口
- 重写run方法
- New一个Thread丢入Runable的实现类,start()启动
- 推荐使用,避免单继承局限性,灵活方便,方便同一个对象被多个线城使用
public class Test implements Runnable {
public static void main(String[] args) {
Test test = new Test();
new Thread(test).start();
}
@Override
public void run() {
for (int i = 0; i < 6; i++) {
System.out.printf("测试" + i + "\n");
}
}
}
测试0
测试1
测试2
测试3
测试4
测试5
静态代理
- 找一个中间商实现附属功能,自己只需要做当前的事情即可
- 例子:A结婚,委托婚庆公司B,A只需要去结婚即可,B帮助其布置场地以及收尾等等
//封装一个接口,即要做的事情
public interface Marry {
//去结婚
void toMarry();
}
/*
代理角色,中间商
实现Marry接口,即实现要做的事情
在重写方法中实现附属工作,即布置场地收尾等等
然后调用Body()方法,Body()就是要去结婚的人
*/
public class Server implements Marry {
public static void main(String[] args) {
new Server(new Body()).toMarry();
}
private Body body;
public Server(Body body) {
this.body = body;
}
@Override
public void toMarry() {
System.out.printf("布置现场" + "\n");
body.toMarry();
System.out.printf("收钱回家" + "\n");
}
}
//结婚的人,只需要去结婚就行
public class Body implements Marry {
@Override
public void toMarry() {
System.out.printf("男孩正在结婚" + "\n");
}
}
输出结果:
布置现场
男孩正在结婚
收钱回家
线程五种状态
1. new Thread()代表新生
2. 调用start()进入就绪状态,等待CPU调度,不一定立即执行
3. CPU调度线程两种情况
1. 阻塞状态
1. 当调用sleep/wait/同步锁定时,线程进入阻塞状态,阻塞状态结束后继续进入就绪状态等段CPU调度
2. 死亡状态
1. 线程中断/结束,一旦进入该状态线程就不能再次启动
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KOJv173E-1614066334796)(C:\Users\Administrator\Desktop\aa.png)]
线程停止
- 不推荐使用JDK提供的stop()/destory()方法
- 推荐线程自己销毁停止
- 建议使用一个线程销毁变量来停止线程操作
/*
假停止
返回值为True代码中执行停止线程操作,否则相反
*/
static boolean isStop = false;//默认不停止
public static boolean isStopThread(int a) {
if (a > 10) {
isStop = true;
} else {
isStop = false;
}
return isStop;
}
线程休眠
- sleep存在异常InterrruptedExcetion
- sleep时间达到后线程进入就绪状态
- sleep可以模拟网络延时,倒计时等
- 每一个对象都有一个锁,sleep不会释放锁
// sleep模拟倒计时
public static void main(String[] args) throws InterruptedException {
toTest();
}
public static void toTest() throws InterruptedException {
int number = 10;
while (true) {
Thread.sleep(1000);
System.out.printf("" + number--);
if (number <= 0) {
break;
}
}
}
线程礼让
- yield 让当前执行的线程停止,不阻塞
- 将线程转为就绪状态
- 礼让不一定成功,看CPU心情
public class TestYield {
public static void main(String[] args) {
YirldThread yirldThread = new YirldThread();
new Thread(yirldThread, "a").start();
new Thread(yirldThread, "b").start();
}
static class YirldThread implements Runnable {
@Override
public void run() {
System.out.printf(Thread.currentThread().getName() + "线程开始" + "\n");
Thread.yield();//礼让
System.out.printf(Thread.currentThread().getName() + "线程停止" + "\n");
}
}
}
线程强制执行
- Join合并线程,待此线程执行完成之后其他线程继续,其他线程阻塞
- 通俗理解:插队
public class TestJoin implements Runnable {
public static void main(String[] args) throws InterruptedException {
TestJoin testJoin = new TestJoin();
Thread thread = new Thread(testJoin);
thread.start();
for (int i = 0; i < 6; i++) {
if (i == 3){//当Main线程运行到3的时候TestJoin线程插队,等待TestJoin运行完成Main线程继续
thread.join();
}
System.out.printf("主线程" + i+"\n");
}
}
@Override
public void run() {
for (int i = 0; i < 6; i++) {
System.out.printf("子线程" + i+"\n");
}
}
}
主线程0
子线程0
主线程1
子线程1
主线程2
子线程2
子线程3
子线程4
子线程5
主线程3
主线程4
主线程5
线程优先级
- 优先级用数字表示范围1-10
public class TestState {
public static void main(String[] args) {
A a = new A();
Thread thread = new Thread(a);
thread.setPriority(10);//设置优先级
thread.start();
System.out.printf("" + thread.getPriority());//获取优先级
}
static class A implements Runnable {
@Override
public void run() {
}
}
}
守护线程Daemon
- 线程分为用户线程和守护线程
- 虚拟机必须确保用户线程执行完毕
- 虚拟机不用等待守护线程执行完毕
- 比如:后台记录操作日志,监控内存,垃圾回收等守护线程
- 用户线程执行完毕对应守护线程也随之结束
public class TestDaemon {
public static void main(String[] args) {
You you = new You();
Server server = new Server();
Thread thread = new Thread(server);
thread.setDaemon(true);//设置守护线程
thread.start();
new Thread(you).start();//用户线程
}
//用户线程
static class You implements Runnable {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.printf("凡人" + i + "\n");
}
}
}
//守护线程
static class Server implements Runnable {
@Override
public void run() {
while (true) {
System.out.printf("上帝" + "\n");
}
}
}
}
线程同步(安全性)
- 由于一个进程的多个线程共享一块存储空间,所有存在冲突问题,为啦保证访问时的数据的正确性,在访问时加入锁synchronized,当一个线程获取这个对象的排它锁时,独占资源,其他线程必须等待,执行完成释放即可
- 比如买票多个人抢一张票
- 存在隐患
- 一个线程持有锁会导致其他同样使用该锁的线程挂起
- 在多线程竞争下,加锁释放锁会导致性能问题
- 如果高优先级等待低优先级释放锁会导致性能倒置问题
- synchronized锁的时当前this
- 锁的对象一定是变化的量,增删改
//买票 同步锁(锁一个方法)
public class UnSecurity {
public static void main(String[] args) {
BuyPaio buyPaio = new BuyPaio();
new Thread(buyPaio, "张三").start();
new Thread(buyPaio, "李四").start();
new Thread(buyPaio, "王五").start();
}
static class BuyPaio implements Runnable {
private int num = 11;//余票
boolean flag = true;//手动停止线程
@Override
public void run() {
while (flag) {
try {
toBuy();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
//synchronized同步方法 安全锁线程同步,不加锁可能买到负数票
public synchronized void toBuy() throws InterruptedException {
if (num <= 0) {
flag = false;
System.out.printf("暂无余票" + "\n");
return;
} else {
Thread.sleep(100);
num--;
System.out.printf(Thread.currentThread().getName() + "买到第" + num + "张票" + "\n");
}
}
}
}
//锁一个代码块
synchronized(Object){
}
死锁
- 某一个同步代码块拥有两个以上对象的锁就可以死锁
Lock
- 显示定义同步锁
public class TestLock {
public static void main(String[] args) {
BuyPaio buyPaio = new BuyPaio();
new Thread(buyPaio, "张三").start();
new Thread(buyPaio, "李四").start();
new Thread(buyPaio, "王五").start();
}
static class BuyPaio implements Runnable {
//定义Lock锁
ReentrantLock lock = new ReentrantLock();
private int num = 11;//余票
boolean flag = true;//手动停止线程
@Override
public void run() {
while (flag) {
try {
lock.lock();//加锁
toBuy();
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
lock.unlock();//解锁
}
}
}
//synchronized同步方法 安全锁线程同步,不加锁可能买到负数票
public void toBuy() throws InterruptedException {
if (num <= 0) {
flag = false;
System.out.printf("暂无余票" + "\n");
return;
} else {
Thread.sleep(100);
num--;
System.out.printf(Thread.currentThread().getName() + "买到第" + num + "张票" + "\n");
}
}
}
}
线程池
public class Main {
public static void main(String[] args) {
//创建线程池
ExecutorService service = Executors.newFixedThreadPool(10);
service.execute(new Test());
service.execute(new Test());
service.execute(new Test());
service.execute(new Test());
//关闭
service.shutdown();
}
}
class Test implements Runnable {
@Override
public void run() {
System.out.printf("线程执行" + "\n");
}
}