首页 > 编程语言 >Multithreading in Java

Multithreading in Java

时间:2024-07-22 18:29:30浏览次数:19  
标签:Java Thread void ticketNumber 线程 static Multithreading public

What is multithread?

multithread(多线程)可以让程序/系统同时做多件事情。用于提升效率。这里要着重介绍四个概念。

process(进程),进程具有自包含的独立运行环境(self-contained excesive environment),并且有着自己的内存空间(own memory space)。

thread(线程),线程和进程都提供了一个运行环境,但线程(thread)被包含于进程(process)中。

用白话来说,就是一个正在运行的程序便为一个进程,一个进程至少有一个线程,叫做主线程(main thread),主线程可以创造新线程。(王者在更新的时候,更新的进程为主进程,并且可以同时解析文件/发出bgm/呈现动画)

并发(concurrency),程序或系统可以在同时交替做两件或多件事。(一个核心做多件事)

并行,多个系统或程序在同时做多个事情。(多个核心做多件事)

How to use multithread?

三种实现方式

继承Thread类实现多线程

首先需要定义一个自己的线程并继承Thread类。并且重写run方法。下面的进程为输出0-99的值。

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

接下来我们在test类中进行测试。下面的程序为创建两个同时运行的线程。

public class TestMultiThread{
    /*        1.创建MyThread对象
     *        2.调用start方法进行线程运行
     */ 
    public static void main(String[] args){     
     MyThread mt1 = new  MyThread();
     MyThread mt2 = new  MyThread();
     mt1.setName("线程1");
     mt2.setName("线程2");
     mt1.start();
     mt2.start();}}

 利用Runnable接口实现多线程

首先需要定义一个自己的类并implements Runnable接口。并且重写run方法。因为没有继承Thread类,所以不能直接用getName()方法,用到了Thread.currentThread()方法,相当于创建了一个对象为现在正在run的thread,便可以使用getName方法。

public class MyRun implements Runnable{
@Override
    public void run(){
        for(int i= 0;i<100;i++){
            System.out.println(Thread.currentThread().getName()+"i ="+i);}        
}

在test类中进行测试。下面的程序为创建两个同时运行的线程。 

public class TestMultiThread{
    /*        1.创建MyRun对象
     *        2.创建thread对象
     *        2.调用start方法进行线程运行
     */ 
    public static void main(String[] args){
     MyRun mr = new MyRun();     
     MyThread mt1 = new  MyThread(mr);
     MyThread mt2 = new  MyThread(mr);
     mt1.setName("线程1");
     mt2.setName("线程2");
     mt1.start();
     mt2.start();}}

利用Callable接口和Future接口实现多线程

定义MyCall类并implements Callable接口。值得注意的是,Callable接口是有泛型的存在,只要规定了泛型,则call方法的返回值也随之确定。

public class Mycall implements Callable<Integer>{

    @Override
    public Integer call() throws Exception {
         int sum =0;
         for(int i= 0;i<100;i++){
            sum += i;
         return sum;}}

 在test类中进行测试。

public class Multithread {

    /*       1.创建MyCall对象
     *       2.创建FutureTask对象
     *       3.创建thread对象
     *       4.线程运行
     *       5.通过FutureTask类的.get()方法得到返回值
     */
    public static void main(String[] args) throws InterruptedException, ExecutionException {       MyCall mc = new MyCall();
        FutureTask<Integer> ft =new FutureTask<>(mc);
        Thread t1 =new Thread(ft);
        t1.start();
        System.out.print(ft.get());
    }   
}

Thread类中一些方法。

String getName();                  //找名字
void setName(String name);         //取名字
static Thread currentThread();     //找到现在运行的Thread
static void sleep(long time);      //睡眠time毫秒
void setPriority(int newpriority); //设置优先级
final int getPriority();           //找优先级
final void setDaemon(boolean );    //设置为守护线程
public static void yield();        //礼让线程
public static void join();         //插入线程

getName();setName();

Thread t = new Thread();
t.setName("AAA");
System.out.println(t.getName());       //输出AAA

currentThread(); 

System.out.print(Thread.currentThread().getName())
//输出现在运行进程名称

sleep();

注意,有Exception不能抛出,只能try catch。

System.out.println("AAA");
Thread.sleep(5000);              //暂停5s
System.out.println("BBB");
//输出AAA,过5s输出BBB

 setPriority();getPriority();

初始priority值为5,范围1-10,越大优先级越高。

Thread.setPriority(10);                 //设置main优先级为10
int priority =Thread.getPriority();     //查找main的优先级

 setDaemon();

设置进程为守护线程,我个人称为伴随线程,非守护的线程结束后,守护线程会陆续停止。

MyThread1 t1 = new MyThread1();
MyThread2 t2 = new MyThread2();

t2.setDaemon();             //设置t2为守护线程。
t1.start();
t2.start();

如果规定t1执行10次,t2执行1000次,在t1执行结束后,t2会慢慢结束(不是立即结束)。

yield(); 

出让线程是为了让程序分布更均匀一些,每次运行到yield之后会出让cpu的执行。不同线程重新进行cpu抢夺。

public class MyThread extends Thread{
@Override
    public void run(){
        for(int i= 0;i<100;i++){
            System.out.println(getName()+"i ="+i);
            Thread.yield();}     //每次执行后出让cpu        
}

join();

public class Multithread {
    public static void main(String[] args) {       
        MyThread t1 =new MyThread(ft);
        t1.start();
        t1.join();                //将t1插入到main线程之前,否则会先运行main中的code

        for(int i =0;i<10;i++){
            yourcode......}
    }   
}

MultiThread的利弊。 

1.它适用于开发反应性(reactive)系统,它可以连续监测传感器阵列,并根据传感器读数对控制系统作出反应。

2.它使应用程序对用户输入的响应性(responsive)更快。

3.它允许一个服务器处理多个客户端(multiple clients)。

4.它可以通过并行地在不同的内核上执行线程来利用多个处理器内核的可用性。

与此同时,弊端也有:

1.不同线程之间的交互和合作可能会导致安全(safety)和存在性(liveness)问题。

2.由于线程创建、调度和同步的成本,多线程程序涉及到一定的开销。

example:设计一个程序通过3个窗口去卖100张票。

​public class MyThread extends Thread{
 static int ticketNumber =0;            //类中共享static值
@Override
    public void run(){
        while(true){                    //线程循环运行具有随机性
            try{Thread.sleep(1000);}catch(InterruptedException e){e.printStackTrace();}
            if(ticketNumber < 100){
            ticketNumber++;
            System.out.println("我们在"+Thread.getName()+"卖"+ticketNumber+"张票");}
             else{break;}}}}

​

再在test中new3个线程,发现可能三个窗口会卖同一张票,而且有可能卖超。

同步代码块,同步方法(Sycchronization) 

想要解决这个问题,我们可以将可能起冲突的代码块在一个进程执行的时候锁住,结束后开锁。

同步代码块。

​public class MyThread extends Thread{
 static int ticketNumber =0;            //类中共享static值
@Override
    public void run(){
        while(true){                    //线程循环运行具有随机性
            Synchronized(MyThread.class){ //锁对象要唯一
            try{Thread.sleep(1000);}catch(InterruptedException e){e.printStackTrace();}
            if(ticketNumber < 100){
            ticketNumber++;
            System.out.println("我们在"+Thread.getName()+"卖"+ticketNumber+"张票");}
             else{break;}}}}}

​
​public class MyRunnable implements Runnable{
 static int ticketNumber =0;            //类中共享static值
@Override
    public void run(){
        while(true){                    //线程循环运行具有随机性
            Synchronized(MyRunnable.class){ //锁对象要唯一
            try{Thread.sleep(1000);}catch(InterruptedException e){e.printStackTrace();}
            if(ticketNumber < 100){
            ticketNumber++;
            System.out.println("我们在"+Thread.getName()+"卖"+ticketNumber+"张票");}
             else{break;}}}}}

​

 同步方法。

​public class MyThread extends Thread{
 static int ticketNumber =0;            //类中共享static值
@Override
    public void run(){
        while(true){                    //线程循环运行具有随机性
            if(method()) break;}}

​public synchronized static boolean method(){     //继承Thread的话要加static
    if(ticketNumber == 100){return true;}
    else{
try{Thread.sleep(1000);}catch(InterruptedException e){e.printStackTrace();}
        ticketNumber++; syso(.....);return false;}}
​public class MyRunnable implements Runnable{
 static int ticketNumber =0;            //类中共享static值
@Override
    public void run(){
        while(true){                    //线程循环运行具有随机性
            if(method()) break;}}

​public synchronized boolean method(){     //runnable不用加static
    if(ticketNumber == 100){return true;}
    else{
try{Thread.sleep(1000);}catch(InterruptedException e){e.printStackTrace();}
        ticketNumber++; syso(.....);return false;}}

锁(lock)

lock是一个interface,所以不能用lock来new对象,只能用实例方法来new对象。

Lock lock =new ReentrantLock();

实现方法 

​public class MyThread extends Thread{
 static int ticketNumber =0;            //类中共享static值
 static Lock lock =new entrantLock();   //类中共享lock
@Override
    public void run(){
        while(true){                    //线程循环运行具有随机性
            lock.lock();                //关锁
​            try{Thread.sleep(1000);
                if(ticketNumber < 100){
                ticketNumber++;
                System.out.println("我们在"+Thread.getName()+"卖"+ticketNumber+"张票");}
                else{break;}
            catch(InterruptedException e){e.printStackTrace();}
            finally{lock.unlock();}     //不管什么情况,关锁。
}
}
​public class MyRunnable implements Runnable{
 static int ticketNumber =0;            //类中共享static值
 Lock lock =new entrantLock();   
@Override
    public void run(){
        while(true){                    //线程循环运行具有随机性
            lock.lock();                //关锁
​            try{Thread.sleep(1000);
                if(ticketNumber < 100){
                ticketNumber++;
                System.out.println("我们在"+Thread.getName()+"卖"+ticketNumber+"张票");}
                else{break;}
            catch(InterruptedException e){e.printStackTrace();}
            finally{lock.unlock();}     //不管什么情况,关锁。
}
}

死锁(DeadLock) 

两个线程都需要两个锁,但是第一个线程抢到了第一个锁,第二个线程抢到了第二个锁,线程一在等待第二个锁,线程二在等待第一个锁,就成了死锁。

代码中不要出现锁的嵌套,可以避免。资源排序(resource ordering),也可以避免。

Thread的生命周期。

 一个线程分为五个阶段,new(创造对象),start后变为ready(有执行资格,没有执行权,需要抢cpu),通过run之后变为running(有执行资格,有执行权),如果顺利运行结束变为finished;如果遇到sleep或者其他block的方法,则变为blocked(没有执行资格,没有执行权)等待阻塞结束,之后重新变为ready。

在Java中,没有定义running状态,而是交给操作系统,还有两个waiting和time_waiting状态,一共六个状态。

生产者(producer)和消费者(consumer)问题。

通俗来说,一个进程生产,一个进程消费,还需要一个中间商进行判断。

中间商:

public class MiddlePerson {
    /*
     *       控制生产者与消费者
     */
    public static int numbers = 0; //1中间人有东西,0中间人没东西。
    public static int maxCount = 100;//一共交易多少次。
    public static Object lock =new Object(); //定义锁
}

消费者:

public class Consumer extends Thread{
@Override
    public void run(){    
    whlie(true){
        synchronized(middlePerson.lock){
        if(middlePerson.maxCount == 0){break;}
        else{
             if(middlePerson.numbers == 0){middlePerson.lock.wait();   //用锁对象调用wait方法,有问题trycatch掉
             else{middlePerson.maxCount--;
                  System.out.println("消费一次,还能消费"+middlePerson.maxCount+"次");
                  middlePerson.lock.notifyAll(); //唤醒其他与锁相关的线程
                  middlePerson.numbers == 0;
}}}}}}

 生产者:

public class Producer extends Thread{
@Override
    public void run(){    
    whlie(true){
        synchronized(middlePerson.lock){
        if(middlePerson.maxCount == 0){break;}
        else{
             if(middlePerson.numbers == 1){middlePerson.lock.wait();   //用锁对象调用wait方法,有问题trycatch掉
             else{
                  System.out.println("生产一次");
                  middlePerson.numbers == 1;
                  middlePerson.lock.notifyAll(); //唤醒其他与锁相关的线程
}}}}}}

测试代码 :

Producer p = new Producer();
Consumer c = new Consumer();

p.start();
c.start();

//pc会交替运行

标签:Java,Thread,void,ticketNumber,线程,static,Multithreading,public
From: https://blog.csdn.net/weixin_48438939/article/details/140577241

相关文章

  • JavaScript笔记总结(Xmind格式):第一天
    Xmind鸟瞰图:简单文字总结:js使用方法:        1.行内样式(需要特定条件才可以使用)        2.内部样式(script尽量写在body最下面)        3.外部样式(在script标签中通过src引入外部的js文件)window对象的方法(window可以省略):        1.alert......
  • JavaScript笔记总结(Xmind格式):第二天
    Xmind鸟瞰图:简单文字总结:数据类型检测:可以使用typeof检测数据类型数据类型转换:  1.其它类型转换为Boolearn    ①数字类型转换Boolean:只有0会被转换为false,其它的非0数字都会转换为true    ②字符串类型转换为Boolean:只有空字符串会被转换为false,......
  • JavaScript笔记总结(Xmind格式):第三天
    Xmind鸟瞰图:简单文字总结:数组的创建:  1.数组的特性:    ①数组中,可以添加任意的数据类型    ②数组是一个对象,属于复杂数据类型    ③直接创建的数组可以在中间添加空值    ④构造函数创建的数据不可以添加空值,会直接报错  2.......
  • 基于java+springboot+vue实现的公司日常考勤系统(文末源码+Lw)132
     基于SpringBoot+Vue的实现的公司日常考勤系统(源码+数据库+万字Lun文+流程图+ER图+结构图+开题报告+演示视频+软件包)选题背景及意义:分析企业的考勤管理系统过程可以看到,考勤管理系统中主要要解决的是:1.考勤信息的管理;2.考勤、出勤信息的请假及申请;3.给系统设定用户登录权......
  • 基于java+springboot+vue实现的在线课程管理系统(文末源码+Lw)133
     基于SpringBoot+Vue的实现的在线课程管理系统(源码+数据库+万字Lun文+流程图+ER图+结构图+演示视频+软件包)系统功能:本在线课程管理系统有管理员,教师,学生。管理员功能有个人中心,学生管理,教师管理,在线课程管理,课件信息管理,知识要点管理,教学计划管理,考试大纲管理,科目类型管理,......
  • 基于java+springboot+vue实现的在线课程管理系统(文末源码+Lw)133
     基于SpringBoot+Vue的实现的在线课程管理系统(源码+数据库+万字Lun文+流程图+ER图+结构图+演示视频+软件包)系统功能:本在线课程管理系统有管理员,教师,学生。管理员功能有个人中心,学生管理,教师管理,在线课程管理,课件信息管理,知识要点管理,教学计划管理,考试大纲管理,科目类型管理,......
  • 深入理解 Java 类加载机制:Arthas classloader 命令解析
    引言Java虚拟机(JVM)的类加载机制是Java应用运行的基础。了解类加载器(ClassLoader)的工作原理对于解决类冲突、热部署、资源查找等问题至关重要。Arthas,作为一个强大的Java诊断工具,提供了classloader命令,帮助开发者深入理解JVM的类加载机制。本文将详细介绍classloa......
  • Java 中的线程
    创建线程的三种方式方式一:继承Thread类实现步骤:继承Thread类并重写run()方法;创建线程并启动。代码实现:publicclassMyThreadextendsThread{  @Override  publicvoidrun(){    for(inti=0;i<100;i++){      System.out.pri......
  • JAVA值传递和引用传递
    值传递在调用方法时,将实参传递给了形参,但方法中无法通过改变形参直接改变实参。//值传递publicclassDemo{publicstaticvoidmain(String[]args){inta=1;System.out.println(a);//1Demo04.change(a);System.out.println(a);......
  • 深入理解Java中的equals和hashCode方法
    序言:在Java编程中,equals和hashCode方法是两个非常重要的概念。它们直接关系到对象的比较和哈希表的使用效率。本文将详细介绍这两个方法的工作原理、如何正确重写它们以及一些常见的误区。一、equals方法equals方法的作用equals方法用于判断两个对象是否相等,返回一个布......