JAVA-多线程
1、 线程和进程
进程和线程的区别?
什么是进程
任务管理器中看到的各种,如:QQ、网易云音乐、Steam、dffice办公软件、IDEA、任务管理器自己等等
这一些我们称之为进程。
什么是线程
线程是每一个进程中可能存在多个线程多并执行,
我们生活中的例子:田径比赛,整体看作进程,每位参赛选手看作线程,只有当所有参赛选手都跑完,这一个进程才算完全结束。
一个进程是由一个或多个线程组成的。
魔兽世界中可能存在多个线程,每个线程都可以回去到相同的魔兽世界的资源,比如用用户登录信息。但是他不可能获取别的进程,如网易云音乐。魔兽世界中的线程不能区播放网易云音乐进程中的歌曲。
2、 多线程
比如,一个进程开始运作,同时开辟四条线程:做登录、打广告、放视频、最后加载页面。
CPU存在单核单线程CPU,多核多线程CPU。
首先如果你的CPU是支持多线程,俺么我们开盘多线程肯定很好理解。
但是如果你的CPU是单线程CPU此时你就只能单线程么?
答案是否定的
3、 Thread类
public static void main(String[] args) {
//通过Thread.currentThread()方法获取当前的线程
Thread t=Thread.currentThread();
//通过线程对象t调用他的getName()方法,过去当前线程的名称
System.out.println(t.getname());
//通过setName可以修改线程的名称
t.setName("这个main方法的名字被我改了");
System.out.println(t.getName());
}
手动创建自己的线程有两种方法:
第一个继承Thread类
第二个实现Runnable接口
4、继承Thread类实现多线程
//第一步继承Thread类
public class MyThread1 extends Thread{
public MyThread1(String name){
super(name);
}
//第二步,我们通过重写run()方法来实现线程的自定义
@override
public void run(){
for(int i=0;i<1000;i++){
System.out.println(Thread.currentthread().getName()+i);
}
}
public static void main(String[] args){
//第三步,实例化我们继承的Thread类的类
MyThread1 myThread1=new MyThread1("我的第一个线程");
MyThread1 myThread2=new MyThread1("我的第二个线程");
MyThread1 myThread3=new MyThread1("我的第三个线程");
//第四步,通过start()方法去调用run()方法的内容
//虽然我们没有去重写start()方法,但是Thresd类会通过start方法调用run方法
//只有通过start()方法调用才是真正起到了开辟一个新线程
//如果是用run()调用,那么依旧是在主线中跑的
myThread1.start();
myThread2.start();
myThread3.start();
for(int i=0;i<1000;i++;){
System.out.println(Thread.currentThread().getName()+i);
}
}
}
5、 实现Runnable接口实现多线程
//第一步,新增一个类实现Runnable接口
public class MyThread2 implements Runnable{
//第二步,重写run()方法
@Override
public void run(){
for(int i=0;i<10000;i++){
System.out.printlm(Thread.currentThread().gatName()+i)
}
}
public static void main(String[] args){
//第三步,将实现Runnable接口的类实例化
MyThread2 myThread1=new MyThread2();
MyThread2 myThread2=new MyThread();
MyThread2 myThread3=new MyThread();
//第四步,将实例化的对象参数传入Thread类实例化的对象
Thread t1=new Thread(myThread1,"1线程");
Thread t2=new Thread(myThread2,"2线程");
Thread t3=new Thread(myThread3,"3线程");
//第三步和第四步可一步实现,一步到位
//Thread t1=new Thread(new MyThread(),"1线程");
//Thread t2=new Thread(new MyThread(),"2线程");
//Thread t3=new Thread(new MyThread(),"3线程");
//第五步,通过Thread类的实例化对象调用他的start()方法
t1.start();
t2.start();
t3.start();
for(int i=1;i<10000;i++){
System.out.println(Thread.currentThread().getName()+i);
}
}
}
可以使用Landba表达式去实例化一个匿名内部类Runable接口,之后正常创建线程
public static void main(String[] args){
//匿名内部类实现
Thread t=new Thread(new Runnable);{
@Override
public void run(){
for(int i=0;i<10;i++){
System.out.println(Thread.currentThread().getName()+i);
}
}
});
//也可以通过Lamdba表达式
Thread t1=new Thread(()->{
for(int i=;i<0;i++){
System.out.println(Thread.currentThread().getName()+i);
}
});
t.setName("匿名内部类的方式调用");
t1.setName("Lamdba表达式实现接口");
t.start();
t1.start();
}
6、 继承Thread类和实现Runnable接口的比较
总结一下,无论你是Thread类的继承,还是Runnable接口的实现,你都需要通过Thread这个类的start();方法区实现多线程的使用.如果你是调用的 run();方法,那么你肯定是单线程。
第二点,Thread类和Runnable接口实现的不同,由于java类只存再单继承,多实现,所以我们优先推荐大家使用接口的方式调用多线程,因为你继承了thread类,你就无法继承别的类,可能这个方法对于后期的代码扩展维护存在部分的问题。
切记,实现Runnable接口是将他当作参数传入新的Thread类中!!!!
7、 start()和run()的区别
调用start()方法
调用run()方法
8、 生命周期
9、 线程调度方法
9.1、 stop(); 停止线程
这个方法会让线程进入死亡状态。注意!这个方法是过时的方法,我们不应该这样做,这样去做有可能会造成你线程中的数据丢失,因为我们run方法后面可能存在保存数据的内容,但是由于某些奇奇怪怪的原因,在没有执行到保存数据的方法的时候,县城就被其余地方杀死了,进入死亡状态,期间的所有数据均出现丢失。
public static void main(String[] args){
public MyThread1(String name){
super(name);
}
#Override
public void run(){
System.out.print("线程启动");
for(int i=0;i<100000;i++){
System.out.println(Thread.currentThread().getName()+i);
}
System.out.print("线程代码执行结束");
}
public static void main(String[] args){
MyThread1 m=new MyThread1("test");
m.start();
try{
//线程休眠500ms(毫秒)
Thread.sleep(500);
}catch(InterruotedException e){
e.printStackTrace();
}
//类似于任务管理器结束进程,没保存的数据就丢失了
m.stop();
}
}
科学停止线程方法:
public class Mythread3 extends Thread{
//我们有一个标记位置,这个线程的开关
static boolean flag=true;
@Override
public void run(){
while(true){
if(!flag){
//进入这个方法块,说明我们的代码线程被关闭了,我们可以再这里执行保存功能
System.out,println("执行保存代码");
return;
}
System.out.println("------------------------------")
}
}
public static void main(String[] args){
MyThread3 thread3=new MyThread();
thread3.strat();
try{
Thread.sleep(2000);
}catch(InterruptedException e){
e.printStackTrace();
}
//相当于关上我们线程的开关
MyThread3.flag=flase;
}
}
9.2、 设置优先级 setPriority();
public static void main(String[] args){
MyThread1 thread1=new MyThread("1号");
MyThread1 thread1=new MyThread("2号");
MyThread1 thread1=new MyThread("3号");
MyThread1 thread1=new MyThread("4号");
//最大值不能大于MAX_PRIORITY(10),最小值不能小于MIN_PRIORITY(1)
//默认是5
//数字越大代表越容易抢到资源,但是不是说一定优先级高
thread1.setPriority(10);
thread2.setPriority(Thread.MAX_PRIORITY);//10
thread3.setPrioriy(6);
thread4.setPriority(Thread.MIN_PRIORITY);//1
thread1.start();
thread2.start();
thread3.start();
thread4.start();
}
9.3、 线程休眠 sleep();
相当于让线程进入阻塞状态
public static void main(String[] args){
MyThread1 m=new MyThread1("test");
m.start();
try{
//线程休眠500ms(毫秒)
//线程休眠属于静态方法,如果我们是使用对象去调用,他依旧会把他视同为类
//如
m.sleep(500);
//此时是使用m对象调用这个方法,但是编译之后还是会变成如下代码;
//Thread.sleep(500);
//所以休眠的线程依旧是当前写在这里的线程,而不是m这个对象的线程
}catch(InterruptedException e){
e.printStackTrace();
}
//类似于任务管理器结束进程,没保存的数据丢失了
m.stop();
}
}
9.4、 强制运行(阻塞所有其他人)join();
join();方法相当于阻塞当前的线程,至于哪个对象调用,说明需要等到对应对象跑完(进入死亡阶段),那他才能继续跑
public static void main(String[] args){
MyThread1 myThread1=new MyThread1("我的第一个线程");
MyThread1 myThread2=new MyThread1("我的第二个线程");
myThread1.start();
myThread2.start();
for(int i=0;i<10000;i++){
System.out.println(Thread.currentThread().getName()+i);
if(i==5000){
try{
//相当于当主线程的循环执行到i==5000时,我们让当前线程阻塞
//让myThread1加入
//知道myThread1跑完了才会让
//相当于我们对mian方法发令“你别动,然他先加入”
myThread1.join();
//这一行代码写不写的区别在于:
//看我们main线程何时从堵塞状态恢复到就绪状态
//如果不写,那就是myThread1跑完,他就回复就绪
//然后main和myThread2一起争夺资源
//如果写了,就是要等myThread2跑完
myThread2.join();
}catch(InterruptedException e){
e.printStackTrace();
}
}
}
}
9.5、 线程的礼让 yield ();
相当于让线程进入就绪状态
public class Mythread4 extends Thread{
@Override
public void run(){
for(int i;i<10;i++){
if(i%3==0){
System.out.println("线程象征性执行了一次礼让");
Thread.yield();
}
System.out.println("thread4对象的"+i);
}
}
public static void main(String[] args){
MyThread4 myThread4=new MyThread4();
thread4.setPriority(1);
thread4.start();
for(int i=0;i<100;i++){
System.out.println("mian方法的"+i);
}
}
}
由于是进入了就绪状态,所以其实还是有点看运气,不知道是哪个时点
9.6、 打断睡眠 interrup();
public class Mythread5 extends Thread{
@Override
public void run(){
for(int i=0;i<1000;i++){
//写睡眠时间或者类似这种写法需要毫秒值时,建议使用如下写法
try{
//会执行到i==500时休眠一年
Thread.sleep(1000*60*60*24*365);
}catch(InterruptedExceptoin e){
System.out.println("这里的睡眠被中断了,需要做什么你就做什么。");
}
}
System.out.println("thread5对象的"+i);
}
public static void main(String[] args){
MyThread5 myThread5=new MyThread4();
thread5.start();
try{
Thread.sleep(5000);
}catch(InterrupedException e){
e.printStackTrace();
}
//执行把thread5叫醒
//相当于让我们的thread5再执行睡眠时抛出一个InterruptedException
//通过try-catch异常处理机制,实现睡眠被唤醒
thread5.interrupt();
}
}
10、 需要解决线程并发是发生的数据冲突问题
如果我们需要使用多线程,这些数据存在共同性,可能两个线程同时操作一个数据,比如银行取钱,2张卡是一个账户,两个线程对应一个账号,这个时候我们就要考虑会不会因为并发(多个线程异步执行)会造成数据影响问题。
同步执行
我跑,你跟在我的后头跑,我跑到终点,你再跑到重点
异步执行
我跑我的,你跑你的,我在错做数据的时候你也在操作
异步的效率更高,但是由于数据是异步的,无法同步,所以需要考虑数据是否会影响结果
public class RobTicket implements Runnable{
private int count=10;//总票数
private int num=1;//当前票
@Override
public void run(){
while(true){
if(count>0){
System.out.println(Thread.currentThread().getName()+
"抢到了第"+num+"张票,余票数还剩"+count+"张");
num++;
count--;
}else{
System.out.println(Thread.currentTread().getName()+
"没抢到票,因为没票了");
return;
}
try{
Tread.sleep(500);
}catch(InterrupedException e){
e.printStckTracr();
}
}
}
public static void main(String[] args){
RobTicket robTicket =new RobTicket();
Thread t1=new Thread(robTicket);
Thread t2=new Thread(robTicket);
Thread t3=new Thread(robTicket);
t1.setName("淘票票");
t2.setName("12306");
t3.setName("大麦网");
t1.start();
t2.start();
t3.start();
}
}
11、synchronized关键字解决问题
public class RobTicket1 implements Runnable{
private int count=10;//总票数
private int num=1;//当前票
@Override
public void run (){
while(count>0){
sale();
}
}
public synchronized void sale(){
if(count>0){
System.out.println(Thread.currentThread().getName()+
"抢到了第"+num+"张票,余票数还剩"+count+"张");
num++;
count--;
}else{
System.out.println(Thread.currentTread().getName()+
"没抢到票,因为没票了");
return;
}
try{
Tread.sleep(500);
}catch(InterrupedException e){
e.printStckTracr();
}
}
public static void main(String[] args){
RobTicket robTicket =new RobTicket();
Thread t1=new Thread(robTicket);
Thread t2=new Thread(robTicket);
Thread t3=new Thread(robTicket);
t1.setName("淘票票");
t2.setName("12306");
t3.setName("大麦网");
t1.start();
t2.start();
t3.start();
}
}
第二种写法:
注意核心难点 :synchronized() 括号内的内容!!!
public class RobTicket2 implements Runnable{
private int count=10;//总票数
private int num=1;//当前票
@Override
public void run(){
while(true){
//()括号中写的内容比较复杂
//这里面需要写的是一个对象,我们会根据这个对象的内存地址去创建一把锁
//如果你们的这个对象是一样的,那么,就需要使用这一把锁;如果是不一样的,就相当于不同的锁
//是否相同对象,如果是相同的对象就要一起排队,如果是不同的对象就比用一起排队
synchronized(this){
if(count>0){
System.out.println(Thread.currentThread().getName()+
"抢到了第"+num+"张票,余票数还剩"+count+"张");
num++;
count--;
}else{
System.out.println(Thread.currentTread().getName()+
"没抢到票,因为没票了");
return;
}
try{
Tread.sleep(500);
}catch(InterrupedException e){
e.printStckTracr();
}
}
}
}
public static void main(String[] args){
RobTicket robTicket =new RobTicket();
Thread t1=new Thread(robTicket);
Thread t2=new Thread(robTicket);
Thread t3=new Thread(robTicket);
t1.setName("淘票票");
t2.setName("12306");
t3.setName("大麦网");
t1.start();
t2.start();
t3.start();
}
}
12、 附加内容
java.util.concurrent提供了一系列方便并发编程的类
阻塞队列BolckingQueue
延迟队列DelayQueue
闭锁CountDownQueue
线程池ExcutorService
…………
第三种创建线程的方式
Callable:能够获取线程执行结果——该结构使用call调用,可以存在返回值,而Runnable时无返回值的
二哈喇子!: 是不是java啊
冷山寒水: Typora许可证过期之后重装导致前端的笔记全都丢了,只能用备份的图片了
babbfqb93: 直接放图片有点水啊