栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

java多线程

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

java多线程

目录

多线程的引入

多线程并行和并发的区别

Java程序运行原理和JVM的启动是多线程的吗

多线程程序实现的方式

继承Thread

实现runnable

 实现Runnable的原理

两种方式的区别

匿名内部类实现线程的两种方式

获取名字和设置名字

获取当前线程对象

休眠线程

守护线程

加入线程

礼让线程

设置线程的优先级

同步代码块

同步方法

线程安全问题

死锁

学过的类


多线程的引入

什么是线程

线程是程序执行的一条路径,一个进程中可以包含多条线程多线程并发执行可以提高程序的执行效率,同时可以完成多项工作

多线程的应用场景

红蜘蛛同时共享屏幕给多个电脑迅雷开始多条线程一起下载qq同时和多个人一起视频服务器同时处理多个客户端请求

多线程并行和并发的区别

并行就是两个任务同时运行,甲任务进行的同时,乙任务 也在同时进行,需要多核cpu

并发就是两个任务都请求运行,而处理器只能接受一个任务,就把这两个任务安排轮流进行,由于时间间隔较短,使人感觉两个任务都在运行

Java程序运行原理和JVM的启动是多线程的吗

java程序运行原理

        java命令会启动java虚拟机,启动JVM,等于启动了一个应用程序,也就是启动了一个进程,该进程会自动启动一个主线程,然后主线程取调用某个类的main方法

JVM的启动是多线程的吗

JVM启动至少启动了垃圾回收程和主线程,所以是多线程的

多线程程序实现的方式

继承Thread

定义类继承Thread重写run方法把新线程要做的事写在run方法中创建线程对象开启新线程,内部会自动执行run方法 

public class test1 {
    public static void main(String[] args) {
        MyThread mt = new MyThread();
        mt.run();

        for(int i = 0;i<1000;i++) {
            System.out.println("bbb");
        }
    }
}

class MyThread extends Thread {
    public void run() {
        for(int i=0;i<1000;i++) {
            System.out.println("aaaaaaaaaaaa");
        }
    }
}

 没有开启线程,应该重写start方法,否则就是都在主线程上运行。

将main方法中的mt.run()改为mt.start()

实现runnable

定义类实现Runnable接口实现run方法把新线程要做的事写在run方法中创建自定义的Runnable的子类对象创建Thread对象,传入Runnable调用strat()开启新线程,内部会自动调用Runnable的run方法

public class test2 {
    public static void main(String[] args) {
        MyRunnable mr = new MyRunnable();
        Thread t = new Thread(mr);      //将其当作参数传递给Thread的构造函数
        t.start();


        for(int i = 0;i<1000;i++) {
            System.out.println("2222");
        }
    }
}

class MyRunnable implements Runnable {
    public void run() {
        for(int i=0 ; i<1000 ; i++) {
            System.out.println("1111111111111111111");
        }
    }
}

 实现Runnable的原理

看Thread类的构造函数,传递了Runnable接口的引用通过init()方法找到传递的target给成员变量target赋值查看run方法,发现run方法中有判断,如果target不为null就会调用Runnable接口子类对象的run方法

两种方式的区别

查看源码的区别:

        继承Thread:由于子类重写了Thread类的run(),当调用start()时,直接找子类的run()方法

        实现Runnable:构造函数中传入了Runnable的引用,成员变量记住了它,start()调用run()方法时,内部判断成员变量Runnable的引用是否为空,不为空编译时看的是Runnable的run(),运行时 执行的是子类的run()方法

继承Thread:

         好处是:可以直接使用Thread类中的方法,代码简单

        弊端是:如果已经有了父类就不能用这种方法

实现Runnable接口

        好处是:即使自己定义的线程类有了父类也没有关系,因为有了父类也可以实现接口,而且接口是可以多实现的

        弊端是:不能直接使用Thread中的方法需要先获取到线程对象后,才能得到Thread的方法,代码复杂。

匿名内部类实现线程的两种方式
public class test3 {
    public static void main(String[] args ) {
        new Thread() {
            public void run() {
                for(int i = 0;i < 3000 ;i++) {
                    System.out.println("aaaaaaaaaaaa");
                }
            }
        }.start();
        for(int i=0;i<10000;i++){
            System.out.println("bb");
        }
     }
}
public class test4 {
    public static void main(String[] args) {
        new Thread(new Runnable() {
          public void run() {
              for(int i = 0;i<10000;i++){
                  System.out.println("q");
              }
          }
        }).start();
        for(int i=0;i<10000;i++){
            System.out.println("bb");
        }
    }
}

获取名字和设置名字
public class test5 {
    public static void main(String[] args) {
        new Thread("ddd") {
            public void run() {
                //this.setName("aaaaaa");
                System.out.println(this.getName());
            }
        }.start();
        new Thread() {
            public void run() {
                System.out.println(this.getName());
            }
        }.start();
    }
}

获取当前线程对象
public class test6 {
    public static void main(String[] args) {
        new Thread() {
            public void run() {
                System.out.println(getName()+"aaaaaaaaaaaaa");
            }
        }.start();
        new Thread(new Runnable() {
            //Thread.currentThread()获取当前执行线程
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName()+"bb");
            }
        }).start();
    }
}

休眠线程
public class test7 {
    public static void main(String[] args) throws InterruptedException {
        for(int i = 20;i>=0;i--) {
            Thread.sleep(1000);  //中断异常
            System.out.println("倒计时第"+ i + "秒");
        }
    }
}

传进去的参数是毫秒

守护线程

该线程不会单独执行,当其他非守护线程都执行结束后,自动退出。

public class test8 {
    public static void main(String[] args) {
        Thread t1 = new Thread() {
            public void run() {
                for(int i = 0; i<2;i++) {
                    System.out.println(getName() + "aaaaaaaaa");
                }
            }
        };
        Thread t2 = new Thread() {
            public void run() {
                for(int i = 0; i<50;i++) {
                    System.out.println(getName() + "bbb");
                }
            }
        };
        t2.setDaemon(true); //当传入true意为设置成守护线程
        t1.start();
        t2.start();
    }
}

偶尔会有时间缓冲,因此t2在t1线程结束之后会有缓冲

加入线程

join(),当前线程暂停,等待指定的线程执行结束后,当前线程再继续

join(int),可以等待指定的毫秒之后继续

public class test8 {
    public static void main(String[] args) {
        final Thread t1 = new Thread() {
            public void run() {
                for(int i = 0; i<200;i++) {
                    System.out.println(getName() + "aaaaaaaaa");
                }
            }
        };
        Thread t2 = new Thread() {
            public void run() {
                for(int i = 0; i<50;i++) {
                    if(i == 2) {
                        try {
                            //t1.join();
                            t1.join(1);//插队1ms
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    System.out.println(getName() + "bbb");

                }
            }
        };
        t1.start();
        t2.start();
    }
}

礼让线程
public class test9 {
    public static void main(String[] args) {
        new Mytread().start();
        new Mytread().start();
    }
}
class Mytread extends Thread {
    public void run() {
        for(int i = 1;i<=1000;i++){
            if(i%10==0) {
                Thread.yield(); //让出cpu
            }
            System.out.println(getName()+"...."+i);
        }
    }
}

设置线程的优先级
public class test10 {
    public static void main(String[] args) {
        Thread t1 = new Thread() {
            public void run() {
                for(int i =0 ;i <=1000;i++) {
                    System.out.println(getName()+"aaaaaaa");
                }
            }
        };
        Thread t2= new Thread() {
            public void run() {
                for(int i =0 ;i <=1000;i++) {
                    System.out.println(getName()+"b");
                }
            }
        };
       // t1.setPriority(10);
       // t2.setPriority(1);

        t1.setPriority(Thread.MIN_PRIORITY);  //设置最小的线程优先级
        t2.setPriority(Thread.MAX_PRIORITY);  //设置最大的线程优先级
        t1.start();
        t2.start();
    }
}

同步代码块

什么情况下需要同步:

        当多线程并发,有多段代码同时执行时,我们希望某一段代码执行过程中cpu不要切换到其他线程工作,这时就需要同步

        如果两段代码是同步的,那么同一时间段只能执行一段,在一段代码没执行结束之前,不会执行另一端代码

同步代码块:

        使用synchronized关键字加上一个锁对象来定义一段代码,这就叫同步代码快

        多个同步代码块如果使用相同的锁对象,那么他们就是同步的

锁对象不能用匿名对象,因为匿名对象不是一个

同步方法

使用synchronized关键字修饰一个方法,该方法中所有的代码都是同步的

非静态同步函数的锁是:this

静态同步函数的锁是字节码对象

线程安全问题

多线程开发操作统一数据就有可能出现线程安全问题

使用同步技术可以解决这种问题,把操作数据的代码同步,不需要多个线程一起操作

死锁

多线程同步的时候如果同步代码嵌套,使用相同锁,就可能出现死锁

学过的类

vector是线程安全的,ArrayList是线程不安全的

StringBuffer是线程安全的,StringBuild是线程不安全的

HashTable是线程安全的 HashMap是线程不安全的

synchronized(XXX)可以将线程不安全的转为线程安全的

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/771273.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号