Java学习笔记44(多线程一)

Posted xuyiqing

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java学习笔记44(多线程一)相关的知识,希望对你有一定的参考价值。

多线程的概念:略

多线程的目的:提高效率

 

主线程:

package demo;
//主线程
public class Demo {
    public static void main(String[] args) {
        function();
        System.out.println(1);
    }
    
    public static void function(){
        for (int i = 0; i < 10000; i++) {
            System.out.println(i);
        }
    }
}

这段简单的代码,我们发现:

必须要先执行方法输出完10000次的数字后才可以打印第二行的数字1

那么有没有方法,可以做到在执行方法的同时执行第二行的输出?

 

Thread类

创建新线程的两种方法:

第一种:

package demo;

public class SubThread extends Thread {
    //重写run方法
    public void run(){
        for(int i = 0 ; i< 50 ; i++){
            System.out.println(i+"run");
        }
    }
}
package demo;

public class ThreadDemo {
    public static void main(String[] args) {
        SubThread st1 = new SubThread();
        st1.start();
        for (int i = 0; i < 50; i++) {
            System.out.println(i+"main");
        }
    }
}

这里输出时候,发现打印的run和main随机出现,交错出现,而不像以前那样按顺序打印

原因:创建了新的线程,两条线程由cpu选择执行,我们无法控制

 

start方法开启新的线程,继承了Thread类因为只有继承了它才可以操作线程

重写run方法因为,Thread类本身没有写入有意义的run方法,相当于一个模板,供开发者使用

 

线程名:

每个线程都有自己的名字,主线程名:main,其他新建线程默认名:Thread-n

获取、修改线程名:

 

package demo1;

public class NameThread extends Thread {
    public void run(){
        System.out.println(super.getName());
        //输出:默认是Thread-0,如果修改了,就是hello
    }
}
package demo1;

public class ThreadDemo {
    public static void main(String[] args) {
        NameThread nt1 = new NameThread();
        nt1.setName("hello");
        //修改线程名为hello,主线程不能改名
        nt1.start();
        
        Thread t = Thread.currentThread();
        System.out.println(t.getName());
        //输出:main
    }
}

 

Thread类的一个实用方法:

package demo1;

public class ThreadDemo {
    public static void main(String[] args) throws InterruptedException {
        for (int i = 0; i < 10; i++) {
            Thread.sleep(1000);
            System.out.println(i);
        }
        //每次打印都会等待一秒,参数是毫秒值
    }
}

 

 

第二种创建线程方法:

package demo1;

public class SubRunnable implements Runnable {
    public void run() {
        for (int i = 0; i < 50; i++) {
            System.out.println(i + "run");
        }
    }
}
package demo1;

public class ThreadDemo {
    public static void main(String[] args) {
        SubRunnable sr1 = new SubRunnable();
        Thread t1 = new Thread(sr1);
        t1.start();
        for (int i = 0; i < 50; i++) {
            System.out.println(i + "main");
        }
    }
}

 

这种方式好处:

1.接口可以多实现,避免了单继承的局限性

2.线程和方法分离,更符合面向对象的特点

3.资源实现共享

 

这两种方式可以实用匿名内部类实现:

package demo1;

public class ThreadDemo {
    public static void main(String[] args) {
        // 继承方式
        new Thread() {
            public void run() {
                System.out.println("1");
            }
        }.start();

        // 实现接口方式
        new Thread(new Runnable() {
            public void run() {
                System.out.println(2);
            }
        }).start();

    }
}

 

线程的状态:

1.新建状态:new Thread()创建线程对象

2.运行状态:使用了start()方法进入运行状态

3.退出状态:run方法结束,或者调用了stop方法(已过时,不建议实用)

4.阻塞状态:有时候使用了start方法,但不一定立即运行,或者运行之后CPU由于一些原因不再分配,由运行状态转到阻塞状态

5.休眠状态:前边提到的sleep方法就是这种状态,也有可能转到阻塞状态或者运行状态

6.等待状态:wait方法,无限等待,notify方法可以唤醒线程,可能转到运行或阻塞状态

注意:受阻塞是等待CPU的资源,休眠等待是放弃CPU的执行

 

线程池的概念:

 一个容器,存入多个线程,需要时候,拿出执行,

运行结束后线程再回到容器中,这种方式可以提高效率

实现线程池:

package demo1;

public class ThreadPoolRunnable implements Runnable {
    public void run(){
        System.out.println(Thread.currentThread().getName()+"线程提交任务");
        //输出: pool-1-thread-1线程提交任务
        //        pool-1-thread-2线程提交任务
    }
package demo1;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class ThreadPoolDemo {
    public static void main(String[] args) {
        //调用工厂类的方法创建线程池
        ExecutorService es1 = Executors.newFixedThreadPool(2);
        es1.submit(new ThreadPoolRunnable());
        es1.submit(new ThreadPoolRunnable());
        //运行后不会停
        
        es1.shutdown();//销毁线程池,不常用
    }
}

 

实现线程的Callable接口方式:

它弥补了Runnable方式的缺陷:无法抛出异常,并且有返回值

使用方法和Runnable方式基本一致:

示例:

package demo1;

import java.util.concurrent.Callable;

public class ThreadPoolCallable implements Callable<String>{
    public String call(){
        return "a";
    }
}
package demo1;

import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

public class ThreadPoolDemo {
    public static void main(String[] args) throws InterruptedException, ExecutionException {
        ExecutorService es1 = Executors.newFixedThreadPool(2);
        Future<String>f1 = es1.submit(new ThreadPoolCallable());
        String s1 = f1.get();
        System.out.println(s1);
        //得到返回值,输出a
    }
}

 

 

简单应用:多线程异步计算

使用两个线程计算求和:

package demo1;

import java.util.concurrent.Callable;

public class GetSumCallable implements Callable<Integer> {
    private int a;

    public GetSumCallable(int a) {
        this.a = a;
    }

    public Integer call() {
        int sum = 0;
        for (int i = 0; i <= a; i++) {
            sum += i;
        }
        return sum;
    }
}
package demo1;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

public class ThreadPoolDemo {
    public static void main(String[] args) throws Exception {
        ExecutorService es1 = Executors.newFixedThreadPool(2);
        Future<Integer> f1 = es1.submit(new GetSumCallable(100));
        Future<Integer> f2 = es1.submit(new GetSumCallable(300));
        System.out.println(f1.get());
        System.out.println(f2.get());
        es1.shutdown();
    }
}

 



以上是关于Java学习笔记44(多线程一)的主要内容,如果未能解决你的问题,请参考以下文章

Java学习笔记—多线程(java.util.concurrent.locks包,转载)

Java学习笔记—多线程

Java多线程学习笔记

Java多线程学习笔记— “Lambda表达式”

Java多线程学习笔记— “Lambda表达式”

多线程Java多线程学习笔记 | 多线程基础知识