1.四种实现多线程的方法
1.1 继承Thread类实现多线程
java.lang.Thread是一个线程操作的核心类。新建一个线程最简单的方法是继承Thread类,而后覆写该类中的run()方法
class MyThread extends Thread{
private String title;
public MyThread(String title){
this.title = title;
}
public void run(){
for(int i=0;i<10;i++){
System.out.println(this.title+",i="+i);
}
}
}
//效果是三个线程交错执行
public class TestDemo{
public static void main(String[] args){
MyThread myThread1 = new MyThread("thread1");
MyThread myThread2 = new MyThread("thread2");
MyThread myThread3 = new MyThread("thread3");
myThread1.start();//public synchronized void start()
myThread2.start();//这个方法是启动多线程的方法
myThread3.start();//会自动调用run()方法
}
}
深入剖析一下一个线程是如何运行的:在start()方法中,会调用start0()方法,start0()是一个本机的原生系统函数。Thread类有个registerNatives本地方法,该方法就是注册一些本地方法供Thread类使用,如start0(),stop0()等。而registerNatives()方法放在一个static块中,当Thread类被加载时就会被调用。而本地方法registerNatives是定义在Thread.c文件中的(Thread.c中定义了各个操作系统平台都要用到的关于线程的公用数据和操作),至于怎么调用到run()方法(这个我还没有看懂…)
Tips:native指的是调用本机的原生系统函数
1.2 Runnable()接口实现多线程
为了打破单继承局限,用实现Runnable()接口来代替继承Thread类
Runnable()接口里有run()方法,但是多线程的启动必须由Thread方法来完成,所以在这种实现方法里要通过创造Thread类的匿名对象来进行start()方法的调用。
class MyRunnable implements Runnable { // 线程主体
private String title ;
public MyRunnable (String title) {
this.title = title;
}
@Override
public void run() { // 所有线程从此处开始执⾏
for (int i = 0; i < 10 ; i++) {
System.out.println(this.title+",i = " + i);
}
}
}
Thread类提供的构造方法:
public Thread(Runnable target)
public class TestDemo{
public static void main(String[] args){
MyThread mythread1 = new MyRunnable ("thread1");
MyThread mythread2 = new MyRunnable ("thread2");
MyThread mythread3 = new MyRunnable
("thread3");
new Thread(mythread1).start();//新建Thread类的对象来调用start()方法启动多线程
new Thread(mythread2).start();
new Thread(mythread3).start();
}
}
1.2.1 使用匿名内部类实现
public class TestDemo{
public static void main(String[] args){
new Thread(new Runnable(){
public void run(){
System.out.println("Hello World");
}
}).start();
}
}
1.2.2 使用Lamdba表达式进行Runnable对象创建
public class TestDemo{
public static void main(String[] args){
Runnable runnable = ()->System.out.println("Hello World");
new Thread(runnable).start();
}
}
Tips:Thread类实现了Runnable接口,实际上使用Runnable实现多线程是一个代理模式,Thread是资源调度的类,MyThread才是核心业务类。
使用Runnable能实现资源共享(比如卖票程序)
//多个线程卖同一批票,如果用Thread类实现的话就变成了各自卖各自的票
class MyThread implements Runnable {
private int ticket = 10 ; // ⼀共10张票
@Override
public void run() {
while(this.ticket>0){
System.out.println("剩余票数:"+this.ticket -- );
}
}
}
public class TestDemo {
public static void main(String[] args) {
MyThread mt = new MyThread();
new Thread(mt).start();
new Thread(mt).start();
}
}
1.3 Callable实现多线程
JDK1.5追加的新的开发包 java.uti.concurrent,主要在进行高并发编程时使用。包中有个接口是Callable
用Callable实现多线程的应用场景:Runnable接口里的run()方法没有返回值,但有些线程可能需要返回值,这时候就由Callable接口里的call()方法来实现
具体实现:
class MyCallable implements Callable<String> {
private int ticket = 10 ; // ⼀共10张票
@Override
public String call() throws Exception {
while(this.ticket>0){
System.out.println("剩余票数:"+this.ticket -- );
}
return "执行完成";
}
}
public class TestDemo{
public static void main(String[] args){
//FutureTask类间接实现了Runnable接口,可以作为Thread类的构造方法的参数
//此类的构造方法接收Callable接口的实例化对象,MyThread类实现了Callable接口
//因为只有Thread类的实例能调用start(),所以起承转Thread(......)
FutureTask<String> task = new FutureTask<String>(new MyCallable());
new Thread(task).start();
new Thread(task).start();
System.out.println(task.get());
}
}
1.4 线程池(常用)
public static void main(String args[]}){
//1.使用工具类获取线程池对象
ExecutorService executorService=Executors.newFixedThreadPool(10);
//2.通过线程池对象获取线程并执行MyRunnable实例
ececutorService.execute(new MyRunnable());
}
扩展:每次运行程序至少启动两个线程,一个是main线程,一个是垃圾收集线程
2.多线程的常用操作方法
由于多线程的运行状态不确定,所以对于多线程要有一个能明显标识出线程对象的信息,也就是线程名称
public Thread(Runnable target,String name)//创建线程的时候设置名称
public final synchronized void setName(String name);//设置线程名称
public final String getName();//取得线程名称
public static native Thread currentThread();//取得当前线程对象
用法:
public class TestDemoThread{
public static void main(String[] args) {
MyThread mt = new MyThread();
new Thread(mt).start();
new Thread(mt).start();
new Thread(mt,"stay").start();
}
}
class MyThread implements Runnable{
public void run(){
for(int i =0;i<10;i++){
System.out.println("当前线程"+Thread.currentThread().getName()+" i= "+i);
}
}
}
public static native void sleep(long millis) throws InterruptedException;
线程休眠方法,休眠时间使用毫秒作为单位,使线程进入阻塞态。交出CPU但不会释放锁,也就是说如果这个线程持有某个对象的锁,即使sleep了其他线程也无法访问这个对象
public static native void yield();
线程让步方法,暂停当前执行的线程,使线程进入就绪态并执行其他线程。交出CPU权限但不会释放锁。与sleep不同的是yield不能控制具体的交出CPU的时间,而且yield只能让拥有相同优先级的线程有获得CPU的机会
public final synchronized void join(long millis)
在主线程中调用此方法会让主线程休眠,让 调用该方法的线程的run方法执行完毕,再执行主线程
//运行结果保证“代码结束”在最后出现
//如果注掉thread.join(),则“代码结束”的运行顺序可能在线程A前
public class TestDemoThread2{
public static void main(String[] args) throws InterruptedException {
MyThread2 mt = new MyThread2();
Thread thread = new Thread(mt,"子线程A");
thread.start();
System.out.println(Thread.currentThread().getName());
thread.join();
System.out.println("代码结束");
}
public static void printTime(){
Date date = new Date();
DateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String time = format.format(date);
System.out.println(time);
}
}
class MyThread2 implements Runnable{
public void run(){
try{
System.out.println("主线程睡眠前的时间");
TestDemoThread2.printTime();
Thread.sleep(2000);
System.out.println(Thread.currentThread().getName());
System.out.println("睡眠结束时间");
TestDemoThread2.printTime();
}catch(InterruptedException e){
e.printStackTrace();
}
}
}