多线程

1、线程、进程的概念

进程:是一个正在执行中的程序,每一个进程执行都有一个执行顺序,该顺序是一个执行路径或者叫一个控制单元
线程:是操作系统能够进行操作运算的最小单位,就是进程中的一个独立控制单元,线程在控制着进程的执行。
多线程:一个进程中不只有一个线程。
  为什么要用多线程:
    ①、为了更好的利用cpu的资源,如果只有一个线程,则第二个任务必须等到第一个任务结束后才能进行,如果使用多线程则在主线程执行任务的同时可以执行其他任务,而不需要等待;
    ②、进程之间不能共享数据,线程可以;
    ③、系统创建进程需要为该进程重新分配系统资源,创建线程代价比较小;
    ④、Java语言内置了多线程功能支持,简化了java多线程编程。

2、线程的生命周期

(1)新建 :从新建一个线程对象到程序start() 这个线程之间的状态,都是新建状态;
(2)就绪 :线程对象调用start()方法后,就处于就绪状态,等到JVM里的线程调度器的调度;
(3)运行 :就绪状态下的线程在获取CPU资源后就可以执行run(),此时的线程便处于运行状态,运行状态的线程可变为就绪、阻塞及死亡三种状态。
(4)等待/阻塞/睡眠 :在一个线程执行了sleep(睡眠)、suspend(挂起)等方法后会失去所占有的资源,从而进入阻塞状态,在睡眠结束后可重新进入就绪状态。
(5)终止 :run()方法完成后或发生其他终止条件时就会切换到终止状态。

3、线程的创建

1、继承Thread类:
步骤:定义继承类Thread;
复写Thread类的run方法;
目的:将自定义代码存储在run方法,让线程运行
   调用线程的start方法:
    该方法有两步:启动线程,调用run方法。

public class ThreadDemo1 {
    public static void main(String[] args) {
        //创建两个线程
        ThreadDemo td = new ThreadDemo("zhangsan");
        ThreadDemo tt = new ThreadDemo("lisi");
        //执行多线程特有方法,如果使用td.run();也会执行,但会以单线程方式执行。
        td.start();
        tt.start();
        //主线程
        for (int i = 0; i < 5; i++) {
            System.out.println("main" + ":run" + i);
        }
    }
}
//继承Thread类
class ThreadDemo extends Thread{
    //设置线程名称
    ThreadDemo(String name){
        super(name);
    }
    //重写run方法。
    public void run(){
        for(int i = 0; i < 5; i++){
        System.out.println(this.getName() + ":run" + i);  //currentThread()  获取当前线程对象(静态)。  getName() 获取线程名称。
        }
    }
}

2、实现Runnable接口: 接口应该由那些打算通过某一线程执行其实例的类来实现。类必须定义一个称为run 的无参方法。
    实现步骤: (1)定义类实现Runnable接口
          (2)覆盖Runnable接口中的run方法
             将线程要运行的代码放在该run方法中。
          (3)通过Thread类建立线程对象。
           (4)将Runnable接口的子类对象作为实际参数传递给Thread类的构造函数。
             自定义的run方法所属的对象是Runnable接口的子类对象。所以要让线程执行指定对象的run方法就要先明确run方法所属对象
           (5)调用Thread类的start方法开启线程并调用Runnable接口子类的run方法。

public class RunnableDemo {
    public static void main(String[] args) {
        RunTest rt = new RunTest();
        //建立线程对象
        Thread t1 = new Thread(rt);
        Thread t2 = new Thread(rt);
        //开启线程并调用run方法。
        t1.start();
        t2.start();
    }
}
//定义类实现Runnable接口
class RunTest implements Runnable{
    private int tick = 10;
    //覆盖Runnable接口中的run方法,并将线程要运行的代码放在该run方法中。
    public void run(){
        while (true) {
            if(tick > 0){
                System.out.println(Thread.currentThread().getName() + "..." + tick--);
            }
        }
    }
}

3、通过Callable和Future创建线程:
    实现步骤:(1)创建Callable接口的实现类,并实现call()方法,改方法将作为线程执行体,且具有返回值。
         (2)创建Callable实现类的实例,使用FutrueTask类进行包装Callable对象,FutureTask对象封装了Callable对象的call()方法的返回值
         (3)使用FutureTask对象作为Thread对象启动新线程。
         (4)调用FutureTask对象的get()方法获取子线程执行结束后的返回值。

public class CallableFutrueTest {
    public static void main(String[] args) {
        CallableTest ct = new CallableTest();                        //创建对象
        FutureTask<Integer> ft = new FutureTask<Integer>(ct);        //使用FutureTask包装CallableTest对象
        for(int i = 0; i < 100; i++){
            //输出主线程
            System.out.println(Thread.currentThread().getName() + "主线程的i为:" + i);
            //当主线程执行第30次之后开启子线程
            if(i == 30){
                Thread td = new Thread(ft,"子线程");
                td.start();
            }
        }
        //获取并输出子线程call()方法的返回值
        try {
            System.out.println("子线程的返回值为" + ft.get());
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }
}
class CallableTest implements Callable<Integer>{
    //复写call() 方法,call()方法具有返回值
    public Integer call() throws Exception {
        int i = 0;
        for( ; i<100; i++){
            System.out.println(Thread.currentThread().getName() + "的变量值为:" + i);
        }
        return i;
    }
}

currentThread方法可以返回正在被哪个线程调用的信息

4、三种方法对比:

继承Thread:线程代码存放在Thread子类run方法中。

优势:编写简单,可直接用this.getname()获取当前线程,不必使用Thread.currentThread()方法。

劣势:已经继承了Thread类,无法再继承其他类。

实现Runnable:线程代码存放在接口的子类的run方法中。

优势:避免了单继承的局限性、多个线程可以共享一个target对象,非常适合多线程处理同一份资源的情形。

劣势:比较复杂、访问线程必须使用Thread.currentThread()方法、无返回值。

实现Callable:

优势:有返回值、避免了单继承的局限性、多个线程可以共享一个target对象,非常适合多线程处理同一份资源的情形。

劣势:比较复杂、访问线程必须使用Thread.currentThread()方法

4、线程的状态管理

1、线程睡眠-sleep
(1)原因:线程执行的太快,需要强制执行到下一个线程
(2)方法:sleep(long millis)在指定的毫秒数内让正在执行的线程休眠。
sleep(long millis,int nanos)在指定的毫秒数加指定的纳秒数内让正在执行的线程休眠。

public class SynTest {
public static void main(String[] args) {
new Thread(new CountDown(),“倒计时”).start();
}
}

class CountDown implements Runnable{
int time = 10;
public void run() {
while (true) {
if(time>=0){
System.out.println(Thread.currentThread().getName() + “:” + time–);
try {
Thread.sleep(1000); //睡眠时间为1秒
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
2、线程让步–yield
这个方法是Thread类提供的一个静态方法,可以让正在执行的线程暂停,但不会进入阻塞状态,而是直接进入就绪态。也就是说他让当前线程暂停一下,然后进入就绪的线程池中,让线程调度器重新调度一次。也会出现某个线程调用yield方法后暂停,但之后调度器又将其调度出来重新进入到运行状态。

public class SynTest {
    public static void main(String[] args) {
        yieldDemo ms = new yieldDemo();
        Thread t1 = new Thread(ms,"张三吃完还剩");
        Thread t2 = new Thread(ms,"李四吃完还剩");
        Thread t3 = new Thread(ms,"王五吃完还剩");
        t1.start();
        t2.start();
        t3.start();
    }
}
class yieldDemo implements Runnable{
    int count = 20;
    public void run() {
        while (true) {
                if(count>0){
                    System.out.println(Thread.currentThread().getName() + count-- + "个瓜");
                    if(count % 2 == 0){
                        Thread.yield();                  //线程让步
                    }
            }
        }
    }
}

区别:
(1)sleep方法声明抛InterruptedException ,调用时需要捕获该异常,yield没有
(2)sleep方法暂停当前线程后,线程进入阻塞态,只有睡眠时间到了才进入运行态,而yield方法调用后,线程直接进入就绪态
3、线程合并–join
其用来加入临时线程,比如b线程执行到了a线程的.join方法时就会等待,等a执行完毕后再执行
        java中的多线程

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值