首页 > 编程语言 >java高级之多线程

java高级之多线程

时间:2023-08-14 21:14:18浏览次数:79  
标签:java Thread 高级 线程 new run ticket 多线程 public

1.什么是多线程

首先引入程序与进程概念:

  • 程序(program)

    程序是为完成特定任务、用某种语言编写的一组指令的集合。即指一段静态的代码(还没有运行起来),静态对象。

  • 进程(process)

    进程是程序的一次执行过程,也就是说程序运行起来了,加载到了内存中,并占用了cpu的资源。这是一个动态的过程:有自身的产生、存在和消亡的过程,这也是进程的生命周期。

    进程是系统资源分配的单位,系统在运行时会为每个进程分配不同的内存区域。

  • 线程(thread)

    进程可进一步细化为线程,是一个程序内部的执行路径:线程是以cpu调度分配的单位。

    若一个进程同一时间并行执行多个线程,那么这个进程就是支持多线程的。

    【当然,一个进程最少包括一个线程,要不然无意义】

2.线程创建的几种方法

2.1创建方式一:继承Thread

public class MyThread extends Thread{

}

继承Thread重写run方法:

public class MyThread extends Thread{
 public void run(){
 
 }
}

这个run,当主线程运行时就会执行run里面的程序【注:main方法就是主线程】

这里举例:

public class MyThread extends Thread{
    public void run(){
        for (int i = 0; i < 10; i++) {
            System.out.println("我是run线程=="+i);
        }
    }
}

在run中我写上for循环,以便一会演示而用。

--------

新建测试类并声明main方法

随后创建MyThread类对象

并调用start()方法启用run线程

例:

public static void main(String[] args) {
     MyThread myThread = new MyThread();
        myThread.start();

}

我在main方法中同样写上一个for循环

public static void main(String[] args) {
     MyThread myThread = new MyThread();
        myThread.start();
   
     for (int i = 0; i < 10; i++) {
            System.out.println("我是main线程=="+i);
        }
}

 此时运行,run线程与主线程同为竞争管理

因为他们会同时抢夺cpu调度资源

至于哪个线程抢的多哪个线程抢的少就全凭运气

 

 运行结果:

 确实随机...

2.1.1 线程的命名和获取

首先是获取当前线程名称:

getName();

System.out.println(this.getName()+"我是run线程=="+i);

 这里可以直接使用this用来指代当前new对象

 当然,如果你不先给线程设置名称的话就会使用默认名称,以 Thread-? 的形式

 这里再次使用 setName() 来为线程命名 

MyThread myThread = new MyThread();
myThread.setName("线程A-");//为线程命名
myThread.start();

 再次运行:

当然,getName() 虽说好用,但是他有缺陷

他只能被用于继承Thread的子类身上

你把 getName()  用在main身上就不行了

所以推出另一种万能获取线程名称的方法

Thread.currentThread().getName()

 这个不管是什么牛马,都可Thread加身,非常的牛13

System.out.println(Thread.currentThread().getName()+"我是main线程=="+i);

 这里直接加在main中,效果可见:

2.2 创建方式二:实现 Runnable  接口

public class MyThread2 implements Runnable{
  
}
 public class MyThread2 implements Runnable{
    public void run() {
        
    }
}

 我还是在其中写上一个for循环

 public class MyThread2 implements Runnable{
    public void run() {
          for (int i = 0; i < 10; i++) {
            System.out.println("我是run线程=="+i);
        }
    }
}

 

 在main方法中同样写上for循环

现在的话不单单是直接new MyThread2就能行的

还得new Thread才阔以,并将对象传入Thread中,同时还可以有第二个参数,可以直接为此线程命名

MyThread2 myThread2 = new MyThread2();
Thread thread = new Thread(myThread2,"窗口1");

 剩下的直接运行,与第一种方法相同

  优缺点:

  • 第一种确实方便,但是不利于效率,因为只能继承一个,也不能重写什么接口了,限制很大
  • 第二种相对于麻烦了一步,但是它可以同时继承,同时重写多个接口

3.线程卖票例子

3.1继承Thread方式卖票

新建MyThread3并继承Thread重写run方法

并定义变量ticket票数等于100

public class MyThread3 extends Thread{
    private int ticket = 100;
    @Override
    public void run() {
        while (ticket > 0){
            ticket--;
            System.out.println(Thread.currentThread().getName()+"卖了1张票,还剩"+ticket+"票");
        }
    }
}

 再次新建测试类并什么main方法中创建三个窗口同时卖票,【注意,是三个窗口同时各卖100张票】,创建三个窗口也就意味着是创建三个线程

public static void main(String[] args) {
    MyThread3 myThread3 = new MyThread3();
    myThread3.setName("窗口一");
    myThread3.start();

    MyThread3 myThread4 = new MyThread3();
    myThread4.setName("窗口二");
    myThread4.start();

    MyThread3 myThread5 = new MyThread3();
    myThread5.setName("窗口三");
    myThread5.start();
}

 运行测试:

太多了,太多了,太多了这里盛不下省略..................................

3.2 实现接口Runnable 方式卖票

新建MyThread4实现 Runnable 接口并重写run方法,

 并定义变量ticket票数等于100

public class MyThread4 implements Runnable{
    private int ticket = 100;

    @Override
    public void run() {

        while (ticket > 0){
            ticket--;
            System.out.println(Thread.currentThread().getName()+"卖了1张票,还剩"+ticket+"票");
        }
    }
}

 新建测试类文件并声明main方法中创建三个窗口同时卖100张票,没错,你没看错,是三个窗口同时卖同100张票

public static void main(String[] args) {
    MyThread4 myThread4 = new MyThread4();
    Thread thread = new Thread(myThread4,"窗口1");
    thread.start();

    Thread thread2 = new Thread(myThread4,"窗口2");
    thread2.start();

    Thread thread3 = new Thread(myThread4,"窗口3");
    thread3.start();
}

 运行测试:

你会发现问题的

无论是继承Thread的方式卖票,还是重写 Runnable 方法的方式卖票,结果都有一个共同点,三个窗口会重复卖掉同一张票,并且会使得库存呈现负数,这是极其不合理的

所以,这里就开始涉及到 线程安全 的方向

4.线程安全--锁的概念

4.1自动锁  synchronized(共享资源){}

这一概念用大白话来说就是一间厕所只有一个坑位,但是这个时候却有三个人要嘘嘘,当然不可能三个人同时对着坑位嘘嘘,这个时候某一个人率先出手抢到了坑位,然后给厕所门一锁

另外两人只能等在门后,待某人解决后开门,临到下一人,继续关门上锁嘘嘘,循环往复,自动锁的概念就此简单化

当然,在上锁之前,我们的代码需要简单改动一下在上锁

public class MyThread4 implements Runnable{
    private int ticket = 100;

    @Override
    public void run() {

        while (true){
            synchronized(this){
            if(ticket>0){
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                ticket--;
                System.out.println(Thread.currentThread().getName()+"卖了1张票,还剩"+ticket+"票");
            }
           else {
               break;
            }
        }
        }
    }
}

 如果你在测试的时候发现效果不明显,那就加上 Thread.sleep(100); 这个方法,效果杠杠的

至于什么作用,稍后说道说道

4.2 手动锁 lock

 演示,效果跟 那个自动锁一样,只是变成了手动

首先就是new 一个  ReentrantLock();

public static ReentrantLock lock = new ReentrantLock();
public class MyThread4 implements Runnable{
    private int ticket = 100;
   public static ReentrantLock lock = new ReentrantLock();
    @Override
    public void run() {

        while (true){
            lock.lock();//手动开启锁
            if(ticket>0){
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                ticket--;
                System.out.println(Thread.currentThread().getName()+"卖了1张票,还剩"+ticket+"票");
            }
           else {
               break;
            }
            lock.unlock();//手动关闭锁
        }
    }
}

测试运行:效果奇好

 这样大大有利于线程安全的漏洞

5.Thread类的常用方法

  • start() : 启动当前线程, 调用当前线程的run()方法
  • run() : 通常需要重写Thread类中的此方法, 将创建的线程要执行的操作声明在此方法中
  • currentThread() : 静态方法, 返回当前代码执行的线程
  • getName() : 获取当前线程的名字
  • setName() : 设置当前线程的名字
  • yield() : 释放当前CPU的执行权
  • join() : 在线程a中调用线程b的join(), 此时线程a进入阻塞状态, 知道线程b完全执行完以后, 线程a才结束阻塞状态
  • stop() : 已过时. 当执行此方法时,强制结束当前线程.
  • sleep(long militime) : 让线程睡眠指定的毫秒数,在指定时间内,线程是阻塞状态

 这里方法太多,就不一一测试了,感兴趣的小伙伴可以尝试着调用其中的方法,看看是什么效果

---------

 以上便是java高级之线程中的部分内容,如有漏缺请在下方留言告知,我会及时补充

标签:java,Thread,高级,线程,new,run,ticket,多线程,public
From: https://www.cnblogs.com/9--1/p/17629466.html

相关文章

  • java中语法糖
    概念   语法糖(SyntacticSugar),也称糖衣语法。指的是在计算机语言中添加的某种语法,这种语法对语言的编译结果和功能并没有实际影响,但是却能更方便程序员使用该语言。用处   通常来说使用语法糖能够减少代码量、增加程序的可读性,从而减少程序代码出错的机会。举例  ......
  • Java中常用的设计模式
    Java中常用的设计模式有以下几种:单例模式(SingletonPattern):确保一个类只有一个实例,并提供全局访问点。工厂模式(FactoryPattern):通过工厂类创建对象,隐藏对象的实例化过程。抽象工厂模式(AbstractFactoryPattern):提供一个创建一系列相关或相互依赖对象的接口,而无需指定具体类。建造者......
  • java_零钱通_面向过程版本
    packagechange;importjava.text.SimpleDateFormat;importjava.util.Date;importjava.util.Scanner;publicclassChange{publicstaticvoidmain(String[]args){booleanloop=true;Scannerscanner=newScanner(System.in);......
  • java_零钱通_面向对象版本
    功能packagechange2;importjava.text.SimpleDateFormat;importjava.util.Date;importjava.util.Scanner;publicclassChangeOOPFunction{//状态booleanloop=true;Scannerscanner=newScanner(System.in);Stringkey="";......
  • Java入门学习——二进制、八进制、十六进制
    一、十进制转二进制的算法十进制数转二进制数:除二取余法。  二、十进制转十进制的算法三、二进制转十进制的算法    四、八进制、十六进制介绍为了便于观察和表示二进制,推出八进制和十六进制。每3位二进制作为一个单元,最小数是0(000),最大数是7(111),共8个数字,这就是......
  • Java基础之--内部类
    1、局部内部类比如: 2、匿名内部类 packagecom.lwx.inner;publicclassAnonymousInnerClass{publicstaticvoidmain(String[]args){Outer02outer02=newOuter02();outer02.m1();}}classOuter02{privateintn1=20;......
  • JavaScript建造者模式:构建复杂对象的利器
    JavaScript建造者模式JavaScript建造者模式是一种创建对象的设计模式,它可以帮助我们构建复杂的对象,同时保持代码的可读性和可维护性。在本文中,我们将介绍JavaScript建造者模式的基本概念和使用方法,并通过一个实际的例子来说明它的应用。什么是JavaScript建造者模式?JavaScript建......
  • JavaSE基础知识
    1.JavaSE基础知识1.数据类型1.1.基本类型1子节=8位1byte=8bit整数型类型占用字节范围byte1-128(-2^7)~127(2^7-1)short2-2^15~2^15-1int4-2^31~2^31-1long8-2^63~2^63-1浮点型类型占用字节float4double8......
  • Idea创建JavaEE项目
    1.创建一个空项目     2创建空项目完成。然后通过idea来管理tomcat2.1idea中引入tomcat 2.2设置整个项目的通用tomcat开关     2.3启动/关闭tomcat 3.创建网站,并编写Servlet。在project目录下,一个module即是一个网站。3.1创建网站mo......
  • Java基础实现加油站圈存机系统
    加油站圈存机系统​ 对于加油卡而言,圈存是将用户账户中已存入的资金划转到所持的加油卡上后方可使用。通俗一点的说法就是您在网点把钱存入主卡中,再分配到下面的副卡,由于副卡都在使用车辆的驾驶员手中,需要在加油的时候在加油站让加油站员工划一下即可,就是所谓的圈存。圈存操作流......