本文目录一览:
java 多线程
所要执行的指令,也包括了执行指令所需的系统资源,不同进程所占用的系统资源相对独立。所以进程是重量级的任务,它们之间的通信和转换都需要操作系统付出较大的开销。
线程是进程中的一个实体,是被系统独立调度和分派的基本单位。线程自己基本上不拥有系统资源,但它可以与同属一个进程的其他线程共享进程所拥有的全部资源。所以线程是轻量级的任务,它们之间的通信和转换只需要较小的系统开销。
Java支持多线程编程,因此用Java编写的应用程序可以同时执行多个任务。Java的多线程机制使用起来非常方便,用户只需关注程序细节的实现,而不用担心后台的多任务系统。
Java语言里,线程表现为线程类。Thread线程类封装了所有需要的线程操作控制。在设计程序时,必须很清晰地区分开线程对象和运行线程,可以将线程对象看作是运行线程的控制面板。在线程对象里有很多方法来控制一个线程是否运行,睡眠,挂起或停止。线程类是控制线程行为的唯一的手段。一旦一个Java程序启动后,就已经有一个线程在运行。可通过调用Thread.currentThread方法来查看当前运行的是哪一个线程。
class ThreadTest{
public static void main(String args[]){
Thread t = Thread.currentThread();
t.setName("单线程"); //对线程取名为"单线程"
t.setPriority(8);
//设置线程优先级为8,最高为10,最低为1,默认为5
System.out.println("The running thread: " + t);
// 显示线程信息
try{
for(int i=0;i3;i++){
System.out.println("Sleep time " + i);
Thread.sleep(100); // 睡眠100毫秒
}
}catch(InterruptedException e){// 捕获异常
System.out.println("thread has wrong");
}
}
}
多线程的实现方法
继承Thread类
可通过继承Thread类并重写其中的run()方法来定义线程体以实现线程的具体行为,然后创建该子类的对象以创建线程。
在继承Thread类的子类ThreadSubclassName中重写run()方法来定义线程体的一般格式为:
public class ThreadSubclassName extends Thread{
public ThreadSubclassName(){
..... // 编写子类的构造方法,可缺省
}
public void run(){
..... // 编写自己的线程代码
}
}
用定义的线程子类ThreadSubclassName创建线程对象的一般格式为:
ThreadSubclassName ThreadObject =
new ThreadSubclassName();
然后,就可启动该线程对象表示的线程:
ThreadObject.start(); //启动线程
应用继承类Thread的方法实现多线程的程序。本程序创建了三个单独的线程,它们分别打印自己的“Hello World!”。
class ThreadDemo extends Thread{
private String whoami;
private int delay;
public ThreadDemo(String s,int d){
whoami=s;
delay=d;
}
public void run(){
try{
sleep(delay);
}catch(InterruptedException e)
System.out.println("Hello World!" + whoami
+ " " + delay);
}
}
public class MultiThread{
public static void main(String args[]){
ThreadDemo t1,t2,t3;
t1 = new ThreadDemo("Thread1",
(int)(Math.random()*2000));
t2 = new ThreadDemo("Thread2",
(int)(Math.random()*2000));
t3 = new ThreadDemo("Thread3",
(int)(Math.random()*2000));
t1.start();
t2.start();
t3.start();
}
}
实现Runnable接口
编写多线程程序的另一种的方法是实现Runnable接口。在一个类中实现Runnable接口(以后称实现Runnable接口的类为Runnable类),并在该类中定义run()方法,然后用带有Runnable参数的Thread类构造方法创建线程。
创建线程对象可用下面的两个步骤来完成:
(1)生成Runnable类ClassName的对象
ClassName RunnableObject = new ClassName();
(2)用带有Runnable参数的Thread类构造方法创建线程对象。新创建的线程的指针将指向Runnable类的实例。用该Runnable类的实例为线程提供 run()方法---线程体。
Thread ThreadObject = new Thread(RunnableObject);
然后,就可启动线程对象ThreadObject表示的线程:
ThreadObject.start();
在Thread类中带有Runnable接口的构造方法有:
public Thread(Runnable target);
public Thread(Runnable target, String name);
public Thread(String name);
public Thread(ThreadGroup group,Runnable target);
public Thread(ThreadGroup group,Runnable target,
String name);
其中,参数Runnable target表示该线程执行时运行target的run()方法,String name以指定名字构造线程,ThreadGroup group表示创建线程组。
用Runnable接口实现的多线程。
class TwoThread implements Runnable{
TwoThread(){
Thread t1 = Thread.currentThread();
t1.setName("第一主线程");
System.out.println("正在运行的线程: " + t1);
Thread t2 = new Thread(this,"第二线程");
System.out.println("创建第二线程");
t2.start();
try{
System.out.println("第一线程休眠");
Thread.sleep(3000);
}catch(InterruptedException e){
System.out.println("第一线程有错");
}
System.out.println("第一线程退出");
}
public void run(){
try{
for(int i = 0;i 5;i++){
System.out.println(“第二线程的休眠时间:”
+ i);
Thread.sleep(1000);
}
}catch(InterruptedException e){
System.out.println("线程有错");
}
System.out.println("第二线程退出");
}
public static void main(String args[]){
new TwoThread();
}
}
程序运行结果如下:
正在运行的线程: Thread[第一主线程,5,main
创建第二线程
第一线程休眠
第二线程的休眠时间:0
第二线程的休眠时间:1
第二线程的休眠时间:2
第一线程退出
第二线程的休眠时间:3
第二线程的休眠时间:4
第二线程退出
另外,虚机团上产品团购,超级便宜
如何使用Java编写多线程程序(1)
一、简介1、什么是线程要说线程,就必须先说说进程,进程就是程序的运行时的一个实例。线程呢可以看作单独地占有CPU时间来执行相应的代码的。对早期的计算机(如DOS)而言,线程既是进程,进程既是进程,因为她是单线程的。当然一个程序可以是多线程的,多线程的各个线程看上去像是并行地独自完成各自的工作,就像一台一台计算机上运行着多个处理机一样。在多处理机计算机上实现多线程时,它们确实可以并行工作,而且采用适当的分时策略可以大大提高程序运行的效率。但是二者还是有较大的不同的,线程是共享地址空间的,也就是说多线程可以同时读取相同的地址空间,并且利用这个空间进行交换数据。 2、为什么要使用线程为什么要使用多线程呢?学过《计算机体系结构》的人都知道。将顺序执行程序和采用多线程并行执行程序相比,效率是可以大大地提高的。比如,有五个线程thread1, thread2, thread3, thread4, thread5,所耗的CPU时间分别为4,5,1,2,7。(假设CPU轮换周期为4个CPU时间,而且线程之间是彼此独立的)顺序执行需要花费1Array个CPU时间,而并行需要的时间肯定少于1Array个CPU时间,至于具体多少时间要看那些线程是可以同时执行的。这是在非常小规模的情况下,要是面对大规模的进程之间的交互的话,效率可以表现得更高。 3、java中是如何实现多线程的与其他语言不一样的是,线程的观念在java是语言中是重要的,根深蒂固的,因为在java语言中的线程系统是java语言自建的, java中有专门的支持多线程的API库,所以你可以以最快的速度写一个支持线程的程序。在使用java创建线程的时候,你可以生成一个Thread类或者他的子类对象,并给这个对象发送start()消息(程序可以向任何一个派生自 Runnable 接口的类对象发送 start() 消息的),这样一来程序会一直执行,直到run返回为止,此时该线程就死掉了。在java语言中,线程有如下特点:§ 在一个程序中而言,主线程的执行位置就是main。而其他线程执行的位置,程序员是可以自定义的。值得注意的是对Applet也是一样。 § 每个线程执行其代码的方式都是一次顺序执行的。 § 一个线程执行其代码是与其他线程独立开来的。如果诸线程之间又相互协作的话,就必须采用一定的交互机制。 § 前面已经说过,线程是共享地址空间的,如果控制不当,这里很有可能出现死锁。 各线程之间是相互独立的,那么本地变量对一个线程而言就是完全独立,私有的。所以呢,线程执行时,每个线程都有各自的本地变量拷贝。对象变量(instance variable)在线程之间是可以共享的,这也就是为什么在java中共享数据对象是如此的好用,但是java线程不能够武断地访问对象变量:他们是需要访问数据对象的权限的。二、准备知识 在分析这个例子之前,然我们先看看关于线程的几个概念,上锁,信号量,和java所提供的API。 上锁对于大多数的程序而言,他们都需要线程之间相互的通讯来完成整个线程的生命周期,二实现线程之间同步的最简单的办法就是上锁。为了防止相互关联的两个线程之间错误地访问共享资源,线程需要在访问资源的时候上锁和解锁,对于锁而言,有读锁,写锁和读写锁等不同的同步策略。在java中,所有的对象都有锁;线程只需要使用synchronized关键字就可以获得锁。在任一时刻对于给定的类的实例,方法或同步的代码块只能被一个线程执行。这是因为代码在执行之前要求获得对象的锁。 信号量通常情况下,多个线程所访问为数不多的资源,那怎么控制呢?一个比较非常经典而起非常简单的办法就是采用信号量机制。信号量机制的含义就是定义一个信号量,也就是说能够提供的连接数;当有一个线程占用了一个连接时,信号量就减一;当一个线程是放了连接时,信号量就加一。
java 如何实现多线程
java中多线程的实现方式有两种,一种是继承java.lang.Thread类,另一种是实现java.lang.Runnable接口。下面是两种方式的简单代码。继承Thread类方式:import java.lang.Thread; //用集成Thread类方式实现多线程。 public class Test{ public static void main(String arg[]){ T t1=new T(); T t2=new T(); //更改新线程名称 t1.setName("t1"); t2.setName("t2"); //启动线程 t1.start(); t2.start(); } } class T extends Thread{ //重写run()方法 public void run(){ System.out.println(this.getName()); } }输出结果为:t1t2实现Runnable接口方式:在使用Runnable接口时需要建立一个Thread实例。因此,无论是通过Thread类还是Runnable接口建立线程,都必须建立Thread类或它的子类的实例。import java.lang.*; //用实现Runnable接口的方式实现多线程。 public class Test{ public static void main(String arg[]){ T t1=new T(); T t2=new T(); //一定要实例化Thread对象,将实现Runnable接口的对象作为参数传入。 Thread th1=new Thread(t1,"t1"); Thread th2=new Thread(t2,"t2"); //启动线程 th1.start(); th2.start(); } } class T implements Runnable{ //重写run()方法 public void run(){ System.out.println(Thread.currentThread().getName()); } }输出结果为:t1t2public void run()方法是JAVA中线程的执行体方法,所有线程的操作都是从run方法开始,有点类似于main()方法,即主线程。
java多核多线程
java可以实现多线程.
但是不能判断某个线程再哪个CPU上执行.
java实现多线程有两种方法。
一种是继承Thread类
class MyThread extends Thread{
@override
public void run(){
//要执行的代码
}
}
调用时,使用new MyThread().start()就可以了
另一种方法是实现Runnable接口
class MyThread implements Runnable{
@override
public void run(){
//要执行的代码
}
}
调用方法:
new Thread(new MyThread()).start()
多线程回顾笔记总结
三种创建方式:
// 练习Thread,实现多线程同步下载图片
public class TestThread1 extends Thread {
private String name; // 保存文件名
// 创建一个构造器
public TestThread1(String url, String name) {
this.url = url;
this.name = name;
}
public static void main(String[] args) {
TestThread1 testThread1 = new TestThread1(";z=0ipn=falseword=qq%E5%A4%B4%E5%83%8Fhs=0pn=4spn=0di=179850pi=0rn=1tn=baiduimagedetailis=0%2C0ie=utf-8oe=utf-8cl=2lm=-1cs=1462782358%2C2840615474os=332435882%2C2135675601simid=3516664974%2C458125993adpicid=0lpn=0ln=30fr=alafm=sme=cg=headbdtype=0oriquery=qq%E5%A4%B4%E5%83%8Fobjurl=http%3A%2F%2F;fromurl=ippr_z2C%24qAzdH3FAzdH3Fooo_z%26e3B8-jyj_z%26e3BvgAzdH3FQQ%25E0%25l9%25AF%25EF%25Bl%25ba%25E0%25ln%25A0%25Em%25BE%25Bm%25Em%25lD%25l8%25Ec%25b9%25lA_z%26e3Bip4sgsm=5islist=querylist=","1.jpg");
TestThread1 testThread2 = new TestThread1(";z=0ipn=falseword=qq%E5%A4%B4%E5%83%8Fhs=0pn=8spn=0di=18480pi=0rn=1tn=baiduimagedetailis=0%2C0ie=utf-8oe=utf-8cl=2lm=-1cs=1756505232%2C2129310927os=3725694615%2C3696011658simid=3576571828%2C474240706adpicid=0lpn=0ln=30fr=alafm=sme=cg=headbdtype=0oriquery=qq%E5%A4%B4%E5%83%8Fobjurl=http%3A%2F%2F;fromurl=ippr_z2C%24qAzdH3FAzdH3F80qq_z%26e3Bv54AzdH3Fqqp57xtwg2AzdH3F8899l9m_rn_z%26e3Bip4sgsm=5islist=querylist=","2.jpg");
TestThread1 testThread3 = new TestThread1(";z=0ipn=falseword=qq%E5%A4%B4%E5%83%8Fhs=0pn=25spn=0di=132220pi=0rn=1tn=baiduimagedetailis=0%2C0ie=utf-8oe=utf-8cl=2lm=-1cs=1226648351%2C4217836os=1652635041%2C3404961290simid=3325093720%2C338083432adpicid=0lpn=0ln=30fr=alafm=sme=cg=headbdtype=0oriquery=qq%E5%A4%B4%E5%83%8Fobjurl=http%3A%2F%2F;fromurl=ippr_z2C%24qAzdH3FAzdH3Fooo_z%26e3Bprbb_z%26e3BgjpAzdH3Fp57xtwg2p7rtwgAzdH3Fgwgfijg2p57xtwg2AzdH3Fda8lAzdH3F8d89AzdH3F8dnb0_z%26e3Bip4sgsm=5islist=querylist=","3.jpg");
testThread1.start();
testThread2.start();
testThread3.start();
/* 结果,说明线程不是按代码先后顺序执行的,是同时执行的
下载了文件,名为:3.jpg
下载了文件,名为:1.jpg
下载了文件,名为:2.jpg
*/
}
// 下载图片的线程执行体
@Override
public void run() {
WebDownLoader webDownLoader = new WebDownLoader();
webDownLoader.downLoader(url,name);
System.out.println("下载了文件,名为:"+name);
}
}
// 下载器
class WebDownLoader {
// 下载方法
public void downLoader(String url,String name) {
try {
// copyURLToFile(),这个方法就是把网上的一个url变成一个文件
FileUtils.copyURLToFile(new URL(url),new File(name));
} catch (IOException e) {
e.printStackTrace();
System.out.println("IO异常,downLoader方法出错");
}
}
}
// 创建线程方式2:实现Runnable接口,重写run()方法,执行线程需要丢入Runnable接口实现类,调用start()方法
public class TestThread2 implements Runnable {
public static void main(String[] args) {
// 创建Runnable 接口实现类对象
TestThread2 testThread2 = new TestThread2();
// 创建线程对象,通过线程对象来开启我们的线程,代理
Thread thread = new Thread(testThread2);
thread.start();
//new Thread(testThread2).start(); // 简写方法
for (int i = 0; i 2000; i++) {
System.out.println("我们在学习多线程---"+i);
}
}
@Override
public void run() {
for (int i = 0; i 2000; i++) {
System.out.println("我们在玩 游戏 啦---"+i);
}
}
}
发现问题:多个线程操作同一个资源情况下,线程不安全,数据紊乱,出现重复数据
// 模拟龟兔赛跑
public class Race implements Runnable {
// 胜利者
private static String winner;
@Override
public void run() {
for (int i = 0; i = 100) {
winner = Thread.currentThread().getName();
System.out.println("winner is " + winner);
return true;
}
}
return false;
}
public static void main(String[] args) {
Race race = new Race();
new Thread(race, "兔子").start();
new Thread(race, "乌龟").start();
}
}
关闭服务:executorService.shutdownNow();
// 线程创建方式三:实现Callable接口
/*
* Callable的好处
* 1.可以定义返回值
* 2.可以抛出异常
**/
public class TestCallable implements Callable {
private String name; // 保存文件名
// 创建一个构造器
public TestCallable(String url, String name) {
this.url = url;
this.name = name;
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
TestCallable testThread1 = new TestCallable(";z=0ipn=falseword=qq%E5%A4%B4%E5%83%8Fhs=0pn=4spn=0di=179850pi=0rn=1tn=baiduimagedetailis=0%2C0ie=utf-8oe=utf-8cl=2lm=-1cs=1462782358%2C2840615474os=332435882%2C2135675601simid=3516664974%2C458125993adpicid=0lpn=0ln=30fr=alafm=sme=cg=headbdtype=0oriquery=qq%E5%A4%B4%E5%83%8Fobjurl=http%3A%2F%2F;fromurl=ippr_z2C%24qAzdH3FAzdH3Fooo_z%26e3B8-jyj_z%26e3BvgAzdH3FQQ%25E0%25l9%25AF%25EF%25Bl%25ba%25E0%25ln%25A0%25Em%25BE%25Bm%25Em%25lD%25l8%25Ec%25b9%25lA_z%26e3Bip4sgsm=5islist=querylist=","1.jpg");
TestCallable testThread2 = new TestCallable(";z=0ipn=falseword=qq%E5%A4%B4%E5%83%8Fhs=0pn=8spn=0di=18480pi=0rn=1tn=baiduimagedetailis=0%2C0ie=utf-8oe=utf-8cl=2lm=-1cs=1756505232%2C2129310927os=3725694615%2C3696011658simid=3576571828%2C474240706adpicid=0lpn=0ln=30fr=alafm=sme=cg=headbdtype=0oriquery=qq%E5%A4%B4%E5%83%8Fobjurl=http%3A%2F%2F;fromurl=ippr_z2C%24qAzdH3FAzdH3F80qq_z%26e3Bv54AzdH3Fqqp57xtwg2AzdH3F8899l9m_rn_z%26e3Bip4sgsm=5islist=querylist=","2.jpg");
TestCallable testThread3 = new TestCallable(";z=0ipn=falseword=qq%E5%A4%B4%E5%83%8Fhs=0pn=25spn=0di=132220pi=0rn=1tn=baiduimagedetailis=0%2C0ie=utf-8oe=utf-8cl=2lm=-1cs=1226648351%2C4217836os=1652635041%2C3404961290simid=3325093720%2C338083432adpicid=0lpn=0ln=30fr=alafm=sme=cg=headbdtype=0oriquery=qq%E5%A4%B4%E5%83%8Fobjurl=http%3A%2F%2F;fromurl=ippr_z2C%24qAzdH3FAzdH3Fooo_z%26e3Bprbb_z%26e3BgjpAzdH3Fp57xtwg2p7rtwgAzdH3Fgwgfijg2p57xtwg2AzdH3Fda8lAzdH3F8d89AzdH3F8dnb0_z%26e3Bip4sgsm=5islist=querylist=","3.jpg");
// 创建执行服务
ExecutorService executorService = Executors.newFixedThreadPool(3);
// 提交执行
Future submit1 = executorService.submit(testThread1);
Future submit2 = executorService.submit(testThread2);
Future submit3 = executorService.submit(testThread3);
// 获取结果
Boolean b1 = submit1.get();
Boolean b2 = submit2.get();
Boolean b3 = submit3.get();
// 关闭服务
executorService.shutdownNow();
System.out.println(b1);
System.out.println(b2);
System.out.println(b3);
/* 结果,说明线程不是按代码先后顺序执行的,是同时执行的
下载了文件,名为:3.jpg
下载了文件,名为:1.jpg
下载了文件,名为:2.jpg
*/
}
// 下载图片的线程执行体
@Override
public Boolean call() {
WebDownLoader webDownLoader = new WebDownLoader();
webDownLoader.downLoader(url,name);
System.out.println("下载了文件,名为:"+name);
return true;
}
}
// 下载器
class WebDownLoader {
// 下载方法
public void downLoader(String url,String name) {
try {
// copyURLToFile(),这个方法就是把网上的一个url变成一个文件
FileUtils.copyURLToFile(new URL(url),new File(name));
} catch (IOException e) {
e.printStackTrace();
System.out.println("IO异常,downLoader方法出错");
}
}
}
静态代理模式总结:
真实对象和代理对象都要实现同一个接口
代理对象要代理真实角色
代理模式的好处:
代理对象可以做很多真实对象做不了的事情
真实对象专注做自己的事情就可以了, 其他的事情交给代理公司来做
new Thread(()- System.out.println("我爱你啊")).start();
public abstract void run();
}
五大状态:
// 测试Stop
// 1.建议线程正常停止,利用次数,不建议死循环
// 2.建议使用标志位,设置一个标志位
// 3.不要使用stop(),destroy()方法,已过时
public class StopTest implements Runnable {
// 1 设置一个标志位
private boolean flag = true;
public static void main(String[] args) {
StopTest stopTest = new StopTest();
// 开启线程
new Thread(stopTest).start();
for (int i = 0; i 1000; i++) {
System.out.println("main Thread is running..."+i);
if (i == 900) {
// 调用stop方法切换标志位停止线程
stopTest.stop();
System.out.println("Thread is stopped...");
}
}
}
// 设置一个公开的方法停止线程,转换标志位
public void stop() {
this.flag = false;
}
@Override
public void run() {
int i = 0;
while (flag) {
System.out.println("Thread is running..."+ i++);
}
}
}
yield
jion
daemon
// 化妆的方法,互相持有对方的锁,就是需要拿到对方的资源
private void makeup() throws InterruptedException {
if (choice == 0) {
synchronized (lipstick) { // 获得口红的锁
System.out.println(this.girlName+"获得口红的锁");
Thread.sleep(1000);
synchronized (mirror) { // 一秒后想获得镜子的锁
System.out.println(this.girlName+"获得镜子的锁");
}
}
} else {
synchronized (mirror) { // 获得镜子的锁
System.out.println(this.girlName+"获得镜子的锁");
Thread.sleep(2000);
synchronized (lipstick) { // 一秒后想获得口红的锁
System.out.println(this.girlName+"获得口红的锁");
}
}
}
}
这样synchronized 块包裹着,就会导致程序卡死,只要不包裹着,就可以正常运行,如下:
// 死锁,多个线程互相抱着对方需要的资源,然后形成僵持
public class DeadLock {
public static void main(String[] args) {
Makeup bestGirl = new Makeup(0, "婕儿");
Makeup betterGirl = new Makeup(1, "珂儿");
bestGirl.start();
betterGirl.start();
}
}
// 口红
class Lipstick {
}
// 镜子
class Mirror {
}
// 化妆
class Makeup extends Thread {
// 需要的资源只有一份,用static来保证只有一份
static Mirror mirror = new Mirror();
static Lipstick lipstick = new Lipstick();
int choice; // 选择
String girlName; // 使用化妆品的人
public Makeup(int choice, String girlName) {
this.choice = choice;
this.girlName = girlName;
}
@Override
public void run() {
// 化妆
try {
makeup();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 化妆的方法,互相持有对方的锁,就是需要拿到对方的资源
private void makeup() throws InterruptedException {
if (choice == 0) {
synchronized (lipstick) { // 获得口红的锁
System.out.println(this.girlName+"获得口红的锁");
Thread.sleep(1000);
}
synchronized (mirror) { // 一秒后想获得镜子的锁
System.out.println(this.girlName+"获得镜子的锁");
}
} else {
synchronized (mirror) { // 获得镜子的锁
System.out.println(this.girlName+"获得镜子的锁");
Thread.sleep(2000);
}
synchronized (lipstick) { // 一秒后想获得口红的锁
System.out.println(this.girlName+"获得口红的锁");
}
}
}
}
上面列出了死锁的四个条件,我们只要想办法破其中任意一个,或多个条件就可以避免死锁发生
1.这是一个线程同步问题,生产者和消费者共享同一个资源,并且生产者和消费者之间相互依赖,互为条件.
2.Java提供了几个方法解决线程之间的通信问题
3.解决方式1
并发协作模型“生产者/消费者模式” —管程法
生产者将生产好的数据放入缓冲区,消费者从缓冲区拿出数据
4.解决方式2
// 测试线程池
public class PoolTest {
public static void main(String[] args) {
// 创建服务,创建池子
// newFixedThreadPool,参数为:线程池池子大小
ExecutorService service = Executors.newFixedThreadPool(10);
service.execute(new MyThread());
service.execute(new MyThread());
service.execute(new MyThread());
service.execute(new MyThread());
// 2.关闭连接
service.shutdown();
}
}
class MyThread implements Runnable {
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
}
在Java 中多线程的实现方法有哪些,如何使用
Java多线程的创建及启动
Java中线程的创建常见有如三种基本形式
1.继承Thread类,重写该类的run()方法。
复制代码
1 class MyThread extends Thread {
2
3 private int i = 0;
4
5 @Override
6 public void run() {
7 for (i = 0; i 100; i++) {
8 System.out.println(Thread.currentThread().getName() + " " + i);
9 }
10 }
11 }
复制代码
复制代码
1 public class ThreadTest {
2
3 public static void main(String[] args) {
4 for (int i = 0; i 100; i++) {
5 System.out.println(Thread.currentThread().getName() + " " + i);
6 if (i == 30) {
7 Thread myThread1 = new MyThread(); // 创建一个新的线程 myThread1 此线程进入新建状态
8 Thread myThread2 = new MyThread(); // 创建一个新的线程 myThread2 此线程进入新建状态
9 myThread1.start(); // 调用start()方法使得线程进入就绪状态
10 myThread2.start(); // 调用start()方法使得线程进入就绪状态
11 }
12 }
13 }
14 }
复制代码
如上所示,继承Thread类,通过重写run()方法定义了一个新的线程类MyThread,其中run()方法的方法体代表了线程需要完成的任务,称之为线程执行体。当创建此线程类对象时一个新的线程得以创建,并进入到线程新建状态。通过调用线程对象引用的start()方法,使得该线程进入到就绪状态,此时此线程并不一定会马上得以执行,这取决于CPU调度时机。
2.实现Runnable接口,并重写该接口的run()方法,该run()方法同样是线程执行体,创建Runnable实现类的实例,并以此实例作为Thread类的target来创建Thread对象,该Thread对象才是真正的线程对象。
复制代码
1 class MyRunnable implements Runnable {
2 private int i = 0;
3
4 @Override
5 public void run() {
6 for (i = 0; i 100; i++) {
7 System.out.println(Thread.currentThread().getName() + " " + i);
8 }
9 }
10 }
复制代码
复制代码
1 public class ThreadTest {
2
3 public static void main(String[] args) {
4 for (int i = 0; i 100; i++) {
5 System.out.println(Thread.currentThread().getName() + " " + i);
6 if (i == 30) {
7 Runnable myRunnable = new MyRunnable(); // 创建一个Runnable实现类的对象
8 Thread thread1 = new Thread(myRunnable); // 将myRunnable作为Thread target创建新的线程
9 Thread thread2 = new Thread(myRunnable);
10 thread1.start(); // 调用start()方法使得线程进入就绪状态
11 thread2.start();
12 }
13 }
14 }
15 }
复制代码
相信以上两种创建新线程的方式大家都很熟悉了,那么Thread和Runnable之间到底是什么关系呢?我们首先来看一下下面这个例子。
复制代码
1 public class ThreadTest {
2
3 public static void main(String[] args) {
4 for (int i = 0; i 100; i++) {
5 System.out.println(Thread.currentThread().getName() + " " + i);
6 if (i == 30) {
7 Runnable myRunnable = new MyRunnable();
8 Thread thread = new MyThread(myRunnable);
9 thread.start();
10 }
11 }
12 }
13 }
14
15 class MyRunnable implements Runnable {
16 private int i = 0;
17
18 @Override
19 public void run() {
20 System.out.println("in MyRunnable run");
21 for (i = 0; i 100; i++) {
22 System.out.println(Thread.currentThread().getName() + " " + i);
23 }
24 }
25 }
26
27 class MyThread extends Thread {
28
29 private int i = 0;
30
31 public MyThread(Runnable runnable){
32 super(runnable);
33 }
34
35 @Override
36 public void run() {
37 System.out.println("in MyThread run");
38 for (i = 0; i 100; i++) {
39 System.out.println(Thread.currentThread().getName() + " " + i);
40 }
41 }
42 }
复制代码
同样的,与实现Runnable接口创建线程方式相似,不同的地方在于
1 Thread thread = new MyThread(myRunnable);
那么这种方式可以顺利创建出一个新的线程么?答案是肯定的。至于此时的线程执行体到底是MyRunnable接口中的run()方法还是MyThread类中的run()方法呢?通过输出我们知道线程执行体是MyThread类中的run()方法。其实原因很简单,因为Thread类本身也是实现了Runnable接口,而run()方法最先是在Runnable接口中定义的方法。
1 public interface Runnable {
2
3 public abstract void run();
4
5 }
我们看一下Thread类中对Runnable接口中run()方法的实现:
复制代码
@Override
public void run() {
if (target != null) {
target.run();
}
}
复制代码
也就是说,当执行到Thread类中的run()方法时,会首先判断target是否存在,存在则执行target中的run()方法,也就是实现了Runnable接口并重写了run()方法的类中的run()方法。但是上述给到的列子中,由于多态的存在,根本就没有执行到Thread类中的run()方法,而是直接先执行了运行时类型即MyThread类中的run()方法。
3.使用Callable和Future接口创建线程。具体是创建Callable接口的实现类,并实现clall()方法。并使用FutureTask类来包装Callable实现类的对象,且以此FutureTask对象作为Thread对象的target来创建线程。
看着好像有点复杂,直接来看一个例子就清晰了。
复制代码
1 public class ThreadTest {
2
3 public static void main(String[] args) {
4
5 CallableInteger myCallable = new MyCallable(); // 创建MyCallable对象
6 FutureTaskInteger ft = new FutureTaskInteger(myCallable); //使用FutureTask来包装MyCallable对象
7
8 for (int i = 0; i 100; i++) {
9 System.out.println(Thread.currentThread().getName() + " " + i);
10 if (i == 30) {
11 Thread thread = new Thread(ft); //FutureTask对象作为Thread对象的target创建新的线程
12 thread.start(); //线程进入到就绪状态
13 }
14 }
15
16 System.out.println("主线程for循环执行完毕..");
17
18 try {
19 int sum = ft.get(); //取得新创建的新线程中的call()方法返回的结果
20 System.out.println("sum = " + sum);
21 } catch (InterruptedException e) {
22 e.printStackTrace();
23 } catch (ExecutionException e) {
24 e.printStackTrace();
25 }
26
27 }
28 }
29
30
31 class MyCallable implements CallableInteger {
32 private int i = 0;
33
34 // 与run()方法不同的是,call()方法具有返回值
35 @Override
36 public Integer call() {
37 int sum = 0;
38 for (; i 100; i++) {
39 System.out.println(Thread.currentThread().getName() + " " + i);
40 sum += i;
41 }
42 return sum;
43 }
44
45 }
复制代码
首先,我们发现,在实现Callable接口中,此时不再是run()方法了,而是call()方法,此call()方法作为线程执行体,同时还具有返回值!在创建新的线程时,是通过FutureTask来包装MyCallable对象,同时作为了Thread对象的target。那么看下FutureTask类的定义:
1 public class FutureTaskV implements RunnableFutureV {
2
3 //....
4
5 }
1 public interface RunnableFutureV extends Runnable, FutureV {
2
3 void run();
4
5 }
于是,我们发现FutureTask类实际上是同时实现了Runnable和Future接口,由此才使得其具有Future和Runnable双重特性。通过Runnable特性,可以作为Thread对象的target,而Future特性,使得其可以取得新创建线程中的call()方法的返回值。
执行下此程序,我们发现sum = 4950永远都是最后输出的。而“主线程for循环执行完毕..”则很可能是在子线程循环中间输出。由CPU的线程调度机制,我们知道,“主线程for循环执行完毕..”的输出时机是没有任何问题的,那么为什么sum =4950会永远最后输出呢?
原因在于通过ft.get()方法获取子线程call()方法的返回值时,当子线程此方法还未执行完毕,ft.get()方法会一直阻塞,直到call()方法执行完毕才能取到返回值。
上述主要讲解了三种常见的线程创建方式,对于线程的启动而言,都是调用线程对象的start()方法,需要特别注意的是:不能对同一线程对象两次调用start()方法。
你好,本题已解答,如果满意
请点右下角“采纳答案”。