大作业是选课系统需要多线程。疯狂学习中。为了避免遗忘以blog记录一下~
首先,并行是真正的同时执行,是多个处理器同时运行多个指令,但是并发只是指在一段时间同时进行,本质上还是有先后顺序的,同一时刻完成的是一个任务
java中使用Thread类来表示线程,所以所有的线程对象都是Thread或者是他的子类。
方法一、定义Thread类的子类,在里面重写run方法,run里面的是线程真正要执行的任务。(run是void函数哦)
但是需要注意,在启动线程的时候要使用start而不是run。如果是调用run会立刻执行,调用start是把他当做线程,根据线程的实际调度情况来执行。
如果你使用了run,那this.getName()返回的是对象而不是线程名(甚至可能返回情况跟刚才是一样的)
但是如果用Thread.currentThread().getName(),这种情况下会发现调用run的并没有产生新的线程,还是main的。
应用过run的已经不是新的了,也不可以再用start(刚新建的可以start)
主线程(也就是main)本身也是一个线程。程序在执行过程中至少会有一个主线程
Thread类本身有静态方法和实例方法,静态方法currentThread直接Thread.currentThread()就可以,getName()这种实例方法使用具体对象调用。也可以使用setName修改名称。(eg:this.setName(String name))
public class FirstThread extends Thread
{
private int i;
public void run()
{
for(;i<100;i++)
{
System.out.println(this.getName()+""+i);
}
}
public static void main(String args[])
{
for(int i=0;i<100;i++)
{
System.out.println(Thread.currentThread().getName()+""+i);
if(i==20)
{
new FirstThread().start();
new FirstThread().start();
}
}
}
}
FirstThread
这里面的i在三个线程里面是独立的,因此结果输出的i是乱序的。
另外,虽然从i=20开始启动两个新的线程,但是顺序仍然是不确定的,(在本次执行中,main70多才出现Thread0)
thread0和thread1顺序也是不稳定的
thread010是thread0的,thread10是在thread1里面的0号。(两者不是一一交替进行的,具有一定的随机性,这也是在真正的多线程中需要锁和信号量的主要原因。)
方法二、通过Runnable接口创建线程类
这里面创建出来的并不是真正的线程类,新建的时候还是要new Thread的,这里面在使用同一个SecondThread建出来的thread,公用私有参数的(同样的理由不能用this指定thread,要用静态类Thread下的currentThread)
public class SecondThread implements Runnable
{
private int i;
public void run()
{
for(;i<100;i++)
{
System.out.println(Thread.currentThread().getName()+""+i);
}
}
public static void main(String args[])
{
for(int i=0;i<100;i++)
{
System.out.println(Thread.currentThread().getName()+""+i);
if(i==20)
{
SecondThread st=new SecondThread();
new Thread(st,"新线程1").start();
new Thread(st,"新线程2").start();
}
}
}
}
SecondThread
方法三、通过Callable和Future创建线程
这个类似Runnable的升级版,注意的是,Callable出来的东西可以带返回值,但是不能直接使用,要用FutureTask包装才有Runnable接口,call的返回值在try中捕获
具体方法,对象.get(),直接在main函数里面写就可以,不用run了。
注意一下(第一次跑的时候意外,main全部在前面执行了,实际上从i到20开新的线程以后他们是交替进行的。)
使用的是Lambda表达式。
Callable和Future都是有import的,并且直接util.*不太好用。。折腾了半天,记得看eclipse的提醒
package threadtry;
import java.io.*;
import java.util.*;
import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;
public class ThirdThread {
public static void main(String args[])
{
ThirdThread rt=new ThirdThread();
FutureTask<Integer>task=new FutureTask<Integer>((Callable<Integer>)()->{
int i=0;
for(;i<100;i++)
{
System.out.println(Thread.currentThread().getName()+""+i);
}
return i;
});
for(int i=0;i<500;i++) {
System.out.println(Thread.currentThread().getName()+""+i);
if(i==20)
{
new Thread(task,"有返回值的线程").start();
}
}
try
{
System.out.println("子线程的返回值"+task.get());
}
catch(Exception ex)
{
ex.printStackTrace();
}
}
}
ThirdThread
线程的三种创建方式中,继承thread比较基础,但是不能继承其他父类。
线程的生命周期经过:新建->就绪->运行->阻塞->死亡五种状态。
new是新建
start以后进入就绪状态
运行是根据JVM里面的实际调度来确认的
关于线程休眠(比如我现在在i==20要立刻执行子线程),用到sleep函数,但是可能有exception,需要处理,格式如下。Thread.sleep(100),休眠的是现在正在运行的线程,单位是根据编译环境在变的
package threadtry;
public class FirstThread extends Thread
{
private int i;
public void run()
{
for(;i<100;i++)
{
System.out.println(Thread.currentThread().getName()+""+i);
}
}
public static void main(String args[])
{
for(int i=0;i<100;i++)
{
System.out.println(Thread.currentThread().getName()+""+i);
if(i==20)
{
try
{
Thread.sleep(100);
}
catch(InterruptedException e)
{System.out.println(e);}
new FirstThread().start();
new FirstThread().start();
}
}
}
}
sleep函数
在其他线程执行开始以后,他们的地位就和主线程是平等的,并不会因为主线程的结束而结束。判断线程是否死亡(在就绪、运行、阻塞状态认为非死亡)isAlive()函数,死亡以后不能重启的哦
join函数可以保证插入的这个线程都运行结束以后才可以运行调用的线程(注意对其他线程不产生干扰的)
package threadtry;
import java.util.*;
import java.io.*;
public class JoinThread extends Thread
{
public JoinThread(String name)
{
super(name);
}
public void run()
{
for(int i=0;i<100;i++)
{
System.out.println(getName()+""+i);
}
}
public static void main(String args[])
{
new JoinThread("新线程").start();
for(int i=0;i<100;i++)
{
if(i==20)
{
JoinThread jt=new JoinThread("被Join的线程");
jt.start();
try{
jt.join();
}
catch(InterruptedException e)
{
System.out.println(e);
}
}
System.out.println(Thread.currentThread().getName()+""+i);
}
}
}
Join
这里面,在i==20的时候是main调用了jt,jt开始运行(main的20已经开始了,但是因为在最后面输出,所以被阻塞了,要jt全完事儿才可以。)中途就是jt和命名为新线程的两个东西争夺内存。
后台线程 Daemon,在前台线程全部死亡以后,后台线程自动死亡(注意一下extends Thread)
package threadtry;
import java.io.*;
import java.util.*;
public class DaemonThread extends Thread
{
public void run()
{
for(int i=0;i<1000;i++)
{
System.out.println(getName()+""+i);
}
}
public static void main(String args[])
{
DaemonThread t=new DaemonThread();
t.setDaemon(true);
t.start();
for(int i=0;i<10;i++)
{
System.out.println(Thread.currentThread().getName()+""+i);
}
}
}
View Code
sleep与yield最大的区别是,yield只会给优先级相同或者更高的线程执行机会,更建议使用sleep