Java基础第十六课(多线程的讲解)

所谓的多线程即在同一时间,可以做多件事情。
创建多线程有3种方式,分别是继承线程类,实现Runnable接口,匿名类

首先要理解进程(Processor)和线程(Thread)的区别
进程: 启动一个LOL.exe就叫一个进程。 接着又启动一个DOTA.exe,这叫两个进程。
线程: 线程是在进程内部同时做的事情,比如在LOL里,有很多事情要同时做,比如"盖伦” 击杀“提莫”,同时“赏金猎人”又在击杀“盲僧”,这就是由多线程来实现的。

一、创建多线程

1、继承线程类的方法创建

package com.baidu.demo.test11;

//继承线程类
public class ExtendsThread extends Thread {
	// 重写run方法 run方法里面的代码就是要进行线程执行的代码
	@Override
	public void run() {
		for(int i = 0;i < 10;i++){
			//Thread.currentThread获取当前代码段所在线程的引用。  .getName就是获取线程的名字
			System.out.println(Thread.currentThread().getName()+ "--->" + i);
		}
	}
}


2.实现Runnable接口创建多线程

package com.baidu.demo.test11;

//实现Runnalbe接口
public class ImplementsThread implements Runnable{

	// 重写run方法 run方法里面的代码就是要进行线程执行的代码
	@Override
	public void run() {
		
		for(int i = 0;i < 10;i++) {
			//Thread.currentThread获取当前代码段所在线程的引用。  .getName就是获取线程的名字
			System.out.println(Thread.currentThread().getName() + "--->" +i);
		}
		
	}
	
}

3.匿名类创建多线程
创建匿名多线程只需要直接new一个Thread就可以重写run方法
这个我直接写到了下面的主方法里面了

//匿名类创建多线程
		Thread thread2 = new Thread() {
			// 重写run方法 run方法里面的代码就是要进行线程执行的代码
			@Override
			public void run() {
				for(int i = 0;i < 10;i++) {
					System.out.println(Thread.currentThread().getName() + "--->" +i);
				}
			}
		};

线程的开启
这里我开启了上面创建的三个线程
代码如下

package com.baidu.demo.test11;

public class Demo1 {

	public static void main(String[] args) {
	
		// 继承线程类的方法创建多线程
		ExtendsThread extendsThread = new ExtendsThread();
		//修改线程名称
		extendsThread.setName("线程1");
		//开启线程
		extendsThread.start();

		//实现Runnable接口创建多线程
		Thread thread = new Thread(new ImplementsThread());
		thread.setName("线程2");
		thread.start();
		
		//匿名类创建多线程
		Thread thread2 = new Thread() {
			// 重写run方法 run方法里面的代码就是要进行线程执行的代码
			@Override
			public void run() {
				for(int i = 0;i < 10;i++) {
					System.out.println(Thread.currentThread().getName() + "--->" +i);
				}
			}
		};
		thread2.setName("线程3");
		thread2.start();
	}

}

运行结果:
在这里插入图片描述
每次运行的结果都不一样哦 这就是最基本的多线程

二、常见线程方法

这个直接代码演示了吧
知识点在注释里 大家认真看

package com.baidu.demo.test11;

public class Demo2 {

	public static void main(String[] args) {
		
		//使用匿名类的方法创建多线程
		Thread thread = new Thread() {
			//重写run run里面的代码就是需要进行多线程执行的代码
			@Override
			public void run() {
				for(int i = 0;i < 10;i++ ) {
					//Thread.sleep是暂停当前线程的意思 1000表示的是1000毫秒 也就是一秒
					try {
						Thread.sleep(1000);
						System.out.println("等待一秒后执行");
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
					System.out.println(Thread.currentThread().getName() + "-->" +i);
				}
			}
		};
		thread.setName("x1");
		thread.start();
		
		
		Thread thread2 = new Thread() {
			// 重写run run里面的代码就是需要进行多线程执行的代码
			@Override
			public void run() {
				for (int i = 0; i < 10; i++) {
					//线程.join表示的是把线程加入到当前线程中 这里表示当i=3时thread线程会加入进来 加入进来后会先执行完thread线程才会执行thread2线程
					if(i == 3) {
						try {
							thread.join();
						} catch (InterruptedException e) {
							// TODO Auto-generated catch block
							e.printStackTrace();
						}
					}
					System.out.println(Thread.currentThread().getName() + "-->" + i);
				}
			}
		};
		//设置线程的优先级 Thread.MAX_PRIORITY说明该线程是最高优先级 也就是说cpu会先尽量优先运行该线程 这里处理的数据少所以可能看不出来效果 你们自己可以下来加大数据试一下  Thread.MIN_PRIORITY说明是最低优先级
		thread2.setPriority(Thread.MAX_PRIORITY);
		thread2.setName("x2");
		thread2.start();
		
		Thread thread3 = new Thread() {
			// 重写run run里面的代码就是需要进行多线程执行的代码
			@Override
			public void run() {
				//临时暂停该线程,使得其它线程可以占用CPU资源 这里处理的数据少所以可能看不出来效果 你们自己可以下来加大数据试一下
				Thread.yield();
				for (int i = 0; i < 10; i++) {
					//线程.join表示的是把线程加入到当前线程中 这里表示当i=3时thread线程会加入进来 加入进来后会先执行完thread线程才会执行thread2线程
					if(i == 3) {
						try {
							thread.join();
						} catch (InterruptedException e) {
							// TODO Auto-generated catch block
							e.printStackTrace();
						}
					}
					System.out.println(Thread.currentThread().getName() + "-->" + i);
				}
				System.out.println("守护线程结束");
			}
		};
		/* 守护线程的概念是: 当一个进程里,所有的线程都是守护线程的时候,结束当前进程。
		*	就好像一个公司有销售部,生产部这些和业务挂钩的部门。
		*	除此之外,还有后勤,行政等这些支持部门。如果一家公司销售部,生产部都解散了,那么只剩下后勤和行政,那么这家公司也可以解散了。
		*	 守护线程就相当于那些支持部门,如果一个进程只剩下守护线程,那么进程就会自动结束。守护线程通常会被用来做日志,性能统计等工作。
		*/
		//设置成守护线程
		thread3.setDaemon(true);
		thread3.setPriority(Thread.MIN_PRIORITY);
		thread3.setName("x3");
		thread3.start();

	}

}

运行结果:
在这里插入图片描述
这个的运行除了join加入线程、sleep暂停线程、setDaemon设置守护线程外 其它的每次运行都是不一样的
大家认真看代码的注释哦 自己也复制代码运行试一下 因为这个确实不太好演示 所以你们可能不太好理解
没事 多看几遍就懂了 我也是这么过来的 实在不懂的可以来问我

三、多线程的同步问题

多线程的同步问题指的是多个线程同时修改一个数据的时候,可能导致的问题
多线程的问题,又叫Concurrency 问题

首先,我们来进行一个同步问题的演示:
代码如下:

package com.baidu.demo.test11;

public class Demo3 {

	//声明一个int变量并赋值10000
	public static int n = 10000;
	
	public static void main(String[] args) {
		
		//循环运行10000遍,每遍都创建两个线程,一个线程是给n加1,另一个线程是给n减1
		//理论上加一次再减一次,最后输出还是n的值10000(事实并非如此)
		//可能最后输出是10000,但是多运行几次,你会发现这个值不一定的
		for(int i = 0;i < n ;i++) {
			//第一个线程,用来给n加1
			Thread t = new Thread() {
				@Override
				public void run() {
					n++;
				}
			};
			t.start();
			
			//第二个线程,用来给n减1
			Thread t1 = new Thread() {
				@Override
				public void run() {
					n--;
				}
			};
			t1.start();
			//输出每循环一次后的值
			System.out.println(n);
		}
		//输出最终结果
		System.out.println("最后n的结果为:"+n);
		
	}

}

运行结果:
在这里插入图片描述

分析同步问题产生的原因:
1、假设增加线程先进入,得到的hp是10000
2、进行增加运算
3.、正在做增加运算的时候,还没有来得及修改hp的值,减少线程来了
4.、减少线程得到的hp的值也是10000
5.、减少线程进行减少运算
6.、增加线程运算结束,得到值10001,并把这个值赋予n
7.、减少线程也运算结束,得到值9999,并把这个值赋予n
n,最后的值就是9999
虽然经历了两个线程各自增减了一次,本来期望还是原值10000,但是却得到了一个9999
这个时候的值9999是一个错误的值,在业务上又叫做脏数据

解决思路:
总体解决思路是: 在增加线程访问hp期间,其他线程不可以访问hp
1.、增加线程获取到n的值,并进行运算
2.、在运算期间,减少线程试图来获取n的值,但是不被允许
3.、增加线程运算结束,并成功修改n的值为10001
4.、减少线程,在增加线程做完后,才能访问n的值,即10001
5.、减少线程运算,并得到新的值10000

解决上述问题就需要用到synchronized关键字
使用方法为(其实所谓的线程安全的类就是用了该关键字):

Object someObject =new Object();
synchronized (someObject){
  //此处的代码只有占有了someObject后才可以执行
}

解决上述同步问题的代码如下:

package com.baidu.demo.test11;

public class Demo4 {

	//声明一个int变量并赋值10000
	public static int n = 10000;
	
	public static void main(String[] args) {
		
		//声明一个对象
		Object someObject = new Object();
		
		//循环运行10000遍,每遍都创建两个线程,一个线程是给n加1,另一个线程是给n减1
		for(int i = 0;i < n ;i++) {
			//第一个线程,用来给n加1
			Thread t = new Thread() {
				@Override
				public void run() {
					//相当于占用了someObject
					synchronized (someObject) {
						//占用后才执行该代码
						n++;
					}
				}
			};
			t.start();
			
			//第二个线程,用来给n减1
			Thread t1 = new Thread() {
				@Override
				public void run() {
					//相当于占用了someObject
					synchronized (someObject) {
						//占用后才执行该代码
						n--;
					}
				}
			};
			t1.start();
			//输出每循环一次后的值
			System.out.println(n);
		}
		//输出最终结果
		System.out.println("最后n的结果为:"+n);
		
	}

}

运行结果:
在这里插入图片描述


好啦,就这样吧
不讲太深了,怕你们一下子接受不了,想学更多的可以直接联系我QQ497099936
24小时不定时在线解答问题
同时,也欢迎撩骚
b( ̄▽ ̄)d
好啦,顾得白

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值