线程的用法

提示:这是我学完线程的部分理解,对线程有兴趣的小伙伴可以查看“JDK 11 API中文帮助文档”。

JDK 11 API帮助文档


提示:以下是本篇文章正文内容,下面案例可供参考

一、线程是什么?

Java 给多线程编程提供了内置的支持。 一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。

多线程是多任务的一种特别的形式,但多线程使用了更小的资源开销。

这里定义和线程相关的另一个术语 - 进程:一个进程包括由操作系统分配的内存空间,包含一个或多个线程。一个线程不能独立的存在,它必须是进程的一部分。一个进程一直运行,直到所有的非守护线程都结束运行后才能结束。

多线程能满足程序员编写高效率的程序来达到充分利用 CPU 的目的。

线程是一个动态执行的过程,它也有一个从产生到死亡的过程。

二、使用步骤

  1. 通过实现 Runnable 接口;
  2. 通过继承 Thread 类本身;
  3. 通过 Callable 和 Future 创建线程。

1.Thread常用的方法

方法功能
public void start()使该线程开始执行;Java 虚拟机调用该线程的 run 方法。
public final void setName(String name)改变线程名称,使之与参数 name 相同。
public final void setPriority(int priority)更改线程的优先级。
public void interrupt()中断线程。
public final boolean isAlive()测试线程是否处于活动状态。

代码如下(示例):

  1. Thread,在方法体上和类上需要继承Tread父类,然后需要重写父类的run()方法,下面分别是用主方法实现的主线程和另一个类实现的子线程。
package Thread;
public class Main1 {
    public static void main(String[] args) throws InterruptedException {
        myThread m=new myThread();
        m.start();
        for (int i = 0; i < 10; i++) {
            System.out.println("我是主方法!"+i);
            Thread.sleep(1000);
        }
    }
}
package Thread;

public class myThread extends Thread{
    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            System.out.println("我是子线程!"+i);
        }
    }
}

运行结果为: 主线程和子线程会交替运行,每次的运行的结果都不相同。
会交替运行

2.Runnbale接口的实现

代码如下(示例):

package Thread;

public class myRunnable implements Runnable{
    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            System.out.println("我是Runnable方法"+i);
        }
    }
}

package Thread;

public class MainRunnable {
    public static void main(String[] args) {
        myRunnable m=new myRunnable();
        Thread m1=new Thread(m);
        m1.start();
        for (int i = 0; i < 10; i++) {
            System.out.println("我是主方法!"+i);
        }
    }
}

运行结果和上面的Thread的运行结果一样,都会交替执行。


3.通过 Callable 和 Future 创建线程

  1. 创建 Callable 接口的实现类,并实现 call() 方法,该 call() 方法将作为线程执行体,并且有返回值。

  2. 创建 Callable 实现类的实例,使用 FutureTask 类来包装 Callable 对象,该 FutureTask 对象封装了该 Callable 对象的 call() 方法的返回值。

  3. 使用 FutureTask 对象作为 Thread 对象的 target 创建并启动新线程。

  4. 调用 FutureTask 对象的 get() 方法来获得子线程执行结束后的返回值。

4.对比Thread和Runnable

提示:这里对两种实现进行总结:

继承Thread父类和实现Runnable接口效果一样。不过考虑到Java是单继承多实现,如果只有一个子线程,两种方法都可以。但是在后期实现的时候,可能会有多个线程一起执行,这个时候就需要实现Runable接口来实现。

5.安全问题

大家都知道线程会交替执行,但是其中就会出现一个新的问题,就是线程的安全问题,这样才能保证线程有序并且按照我们的意愿去执行,这就需要一把“锁”来实现。

原理为:需要一把公共的锁,在一个线程执行的时候,把锁给这个线程。同时其他的线程排队等待执行线程解开锁,注意:排队是随机的,不是先到先得,是按照优先级来获取的。

下面用代码来体现:卖票系统

package ThreadSafe;

public class Demo1 {
    public static void main(String[] args) {
       Runnable r=new myRunnable();
        //创建三个线程代表卖票的窗口
        new Thread(r).start();
        new Thread(r).start();
        new Thread(r).start();
    }
    static  class  myRunnable implements  Runnable{
         private  Object o=new Object();//锁
         private  int count=10;//票数
        //重写run方法
        @Override
        public void run() {
               while (true){//用while循环 一直循环直到票被卖完
                   synchronized (o){//把锁 o 传入  代表谁执行谁拿到锁
                       if(count>0){
                           System.out.println("准备卖票!");
                           try {
                               Thread.sleep(1000);//让线程睡眠一秒   1秒=1000毫秒
                           } catch (InterruptedException e) {
                               e.printStackTrace();
                           }
                           count--;
                           System.out.println(Thread.currentThread().getName()+"正在卖票,余票为:"+count);
                       }else {
                           break;
                       }
                   }
               }
        }
    }
}

这里是另一种实现方式:在方法上注明这个方法是锁方法,public static synchronized boolean sale()效果也可以达到。

 /**
           * 卖票方法
           * @return
           */
        public static synchronized boolean sale() {

            if (count > 0) {
                System.out.println("准备卖票!");
                try {
                    Thread.sleep(1000);//让线程睡眠一秒   1秒=1000毫秒
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                count--;
                //Thread.currentThread().getName() :得到当前执行线程的名字
                System.out.println(Thread.currentThread().getName() + "正在卖票,余票为:" + count);
                return true;
            }
            return false;

        }

三、线程池

线程池的优势

(1)、降低系统资源消耗,通过重用已存在的线程,降低线程创建和销毁造成的消耗;
(2)、提高系统响应速度,当有任务到达时,通过复用已存在的线程,无需等待新线程的创建便能立即执行;
(3)方便线程并发数的管控。因为线程若是无限制的创建,可能会导致内存占用过多而产生OOM,并且会造成cpu过度切换(cpu切换线程是有时间成本的(需要保持当前执行线程的现场,并恢复要执行线程的现场))。
(4)提供更强大的功能,延时定时线程池。

提供了四种线程方法

Executors类提供了4种不同的线程池

  • newCachedThreadPool—缓存线程池
  • newFixedThreadPool—定长线程池
  • newScheduledThreadPool—周期线程线程池
  • newSingleThreadExecutor–单线程线程池

总结

线程是非常重要的,同时对于的安全问题也是需要重视的,在创建多个线程时,尽量使用线程池来创建线程,如果考虑到多个线程,这个时候就必须需要用“锁”来保护线程稳定,从而让程序有序的走完。

评论 10
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小高求学之路

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值