栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

多线程基础

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

多线程基础

多线程基础

Java.thread

1、线程简介

任务:进程,线程,多线程

现实中很多的例子,其实本质上大脑CPU统一时间依旧只做了一件事情!

  • 线程就是独立的执行路径;

  • 在程序运行时,即使没有自己创建线程,后台也会有多个线程,如主线程,gc线程;

main()称之为主线程,为系统的入口,用于执行整个程序;

  • 在一个进程中,如果开辟了多个线程,线程的运行由调度器安排调度,调度器是与操作系统紧密相关的,先后顺序是不能人为的干预的
  • 对同一份资源操作时,会存在资源抢夺的问题,需要加入并发控制;
  • 线程会带来额外的开销,如cpu调度时间,并发控制开销。
  • 每个线程在自己的工作内存交互,内存控制不当会造成数据不一致
Process与Thread
  • 程序:静态的一组指令和数据的集合

  • 进程:执行程序的一次执行过程,是动态的;进程是系统资源分配的单位;

  • 线程:线程就是独立执行的路径,一个进程中包含1-n个线程;线程是CPU调度和执行的单位

2、线程实现(重点)

任务:Thread、Runnable、Callable

三种创建方式

1、基础Thread 类

2、实现Runnable 接口

3、实现Callable 接口

Thread

start方法:线程交替执行

run方法:按顺序执行


//1、继承Thread类
public class PrimeThread extends Thread {
    long minPrime;

    PrimeThread(long minPrime) {
 this.minPrime = minPrime;
    }

    //2、重写run 方法
    @Override
    public void run() {
 for (int i = 1; i <= 2000; i++) {
     System.out.println("线程循环:" + i);
 }
    }

    public static void main(String[] args) {
 //3、start 启动运行
 PrimeThread p = new PrimeThread(143);
 p.start();

 for (int i = 1; i <= 2000; i++) {
     System.out.println("主线程循环:" + i);
 }
    }
}
Runnable

推荐使用:同一个对象可以被多次调用,避免OOP单继承的局限性


//1、实现Runnable接口
public class PrimeRunable implements Runnable {
    long minPrime;
    PrimeRunable(long minPrime) {
 this.minPrime = minPrime;
    }
    //2、重写run方法
    @Override
    public void run() {
 System.out.println("启动线程" + minPrime);
    }
    public static void main(String[] args) {
 //3、创建线程对象,调用start()方法启动线程
 PrimeRunable p = new PrimeRunable(143);
 new Thread(p, "线1").start();
 new Thread(p, "线2").start();
 new Thread(p, "线3").start();
    }
}
案例:分析多线程下载图片的顺序
import org.apache.commons.io.FileUtils;

import java.io.File;
import java.io.IOException;
import java.net.URL;


public class PictureThread extends Thread {

    private String url;//图片地址
    private String name;//保存的图片名

    public PictureThread(String url, String name) {
 this.url = url;
 this.name = name;
    }

    //下载图片的执行体
    @Override
    public void run() {
 WebDownloader webDownloader = new WebDownloader();
 webDownloader.downloader(url, name);
 System.out.println("下载了文件名为:" + name);
    }

    public static void main(String[] args) {
 PictureThread p1 = new PictureThread("https://cdn2.jianshu.io/assets/default_avatar/2-9636b13945b9ccf345bc98d0d81074eb.jpg","1.jpg");
 PictureThread p2 = new PictureThread("http://seopic.699pic.com/photo/50163/5313.jpg_wh1200.jpg","2.jpg");
 PictureThread p3 = new PictureThread("https://highpic.originoo.com/highpic/detail/161025/RM/bji81201635.jpg", "3.jpg");

 p1.start();
 p2.start();
 p3.start();
    }
}

//下载器
class WebDownloader {
    //下载方法
    public void downloader(String url, String name) {
 try {
     FileUtils.copyURLToFile(new URL(url), new File(name));
 } catch (IOException e) {
     e.printStackTrace();
     System.out.println("IO异常,downloader方法出现问题");
 }
    }
}

结果每次都不一样,并不是我们想的1,2,3的顺序

案例:龟兔赛跑-Race

public class Race implements Runnable {

    //胜利者
    private static String winner;

    @Override
    public void run() {
 for (int i = 0; i <= 100; i++) {
     //模拟兔子休眠
     if (Thread.currentThread().getName().equals("兔子") && i % 50 == 0) {
  try {
      Thread.sleep(2);
  } catch (InterruptedException e) {
      e.printStackTrace();
  }
     }
     //判断比赛是否结束
     boolean flag = gameOver(i);
     //如果比赛结束了就停止程序
     if (flag) {
  break;
     }
     System.out.println(Thread.currentThread().getName() + "-->跑了" + i + "步!");
 }
    }

    //判断是否完成比赛
    private boolean gameOver(int steps) {
 if (winner != null) {//已经存在胜利者了
     return true;
 }
 if (steps >= 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();
    }
}

乌龟取得了胜利,兔子走到50步的时候睡觉去了!

案例:初始并发问题

public class BuyTickets implements Runnable {

    //票数
    private int tickeNums = 10;

    @Override
    public void run() {
 while (true) {
     if (tickeNums <= 0) {
  break;
     }
     //模拟延时
     try {
  Thread.sleep(200);
     } catch (InterruptedException e) {
  e.printStackTrace();
     }
     System.out.println(Thread.currentThread().getName() + "-->拿到了第" + tickeNums-- + "票");
 }
    }

    public static void main(String[] args) {
 BuyTickets b = new BuyTickets();
 new Thread(b, "小周").start();
 new Thread(b, "肖鑫").start();
 new Thread(b, "黄牛").start();
 new Thread(b, "小明").start();
 new Thread(b, "肖红").start();
 new Thread(b, "萧兰").start();
    }
}

并发操作同一个对象

Callable了解即可

1. 实现 Callable 接口,需要返回值类型

2. 重写 call 方法,需要抛出异常

3. 创建目标对象

4. 创建执行服务: ExecutorService ser = Executors.newFixedThreadPool(1);

5. 提交执行: Future result1 = ser.submit(t1);

6. 获取结果: boolean r1 = result1.get()

7. 关闭服务: ser.shutdownNow();

演示:利用callable改造下载图片案例

有返回值

可以抛出异常

//1、实现Callable接口,需要返回值类
public class PrimeCallable implements Callable {

    private String url;//图片地址
    private String name;//保存的图片名

    public PrimeCallable(String url, String name) {
 this.url = url;
 this.name = name;
    }

    //2、重写call方法,需要抛出异常
    @Override
    public Boolean call() throws Exception {
 Boolean bool;
 WebDownloader2 webDownloader = new WebDownloader2();
 bool = webDownloader.downloader(url, name);
 System.out.println("下载了文件名为:" + name);
 return bool;
    }

    public static void main(String[] args) throws Exception {
 //3、创建目标对象
 PrimeCallable p1 = new PrimeCallable("https://cdn2.jianshu.io/assets/default_avatar/2-9636b13945b9ccf345bc98d0d81074eb.jpg", "1.jpg");
 PrimeCallable p2 = new PrimeCallable("http://seopic.699pic.com/photo/50163/5313.jpg_wh1200.jpg", "2.jpg");
 PrimeCallable p3 = new PrimeCallable("https://highpic.originoo.com/highpic/detail/161025/RM/bji81201635.jpg", "3.jpg");
 //4、创建执行服务
 ExecutorService ser = Executors.newFixedThreadPool(3);
 //5、提交执行
 Future result1 = ser.submit(p1);
 Future result2 = ser.submit(p2);
 Future result3 = ser.submit(p3);
 //6、获取结果
 Boolean r1 = result1.get();
 Boolean r2 = result2.get();
 Boolean r3 = result3.get();
 //7、关闭服务
 ser.shutdownNow();
 System.out.println("r1:" + r1 + "tr2:" + r2 + "tr3:" + r3);
    }
}

//下载器
class WebDownloader2 {
    //下载方法
    public boolean downloader(String url, String name) {
 try {
     FileUtils.copyURLToFile(new URL(url), new File(name));
 } catch (IOException e) {
     e.printStackTrace();
     System.out.println("IO异常,downloader方法出现问题");
     return false;
 }
 return true;
    }
}

结果:如何只创建一个池子 Executors.newFixedThreadPool(1); 结果就是1-2-3!

3、线程状态

五大状态:

1、对象一旦创建就进入到了新建状态

2、当调用start()方法,线程立即进入就绪状态,但不意味着立即调度执行

3、cpu调度后进入运行状态,线程才真正执行线程体的代码块

4、当调用sleep,wait 或同步锁定时,线程进入阻塞状态,就是代码不往下执行,阻塞事件解除后,重新进入就绪状态,等待cpu调度执行。

5、线程中断或者结束,一旦进入死亡状态,就不能再次启动

线程方法

方 法 说 明
setPriority(int newPriority) 更改线程的优先级
static void sleep(long millis) 在指定的毫秒数内让当前正在执行的线程休眠
void join() 等待该线程终止
static void yield() 暂停当前正在执行的线程对象,并执行其他线程
void interrupt() 中断线程,别用这个方式
boolean isAlive() 测试线程是否处于活动状态
终止线程

建议线程正常停止

建议使用标志位

不建议使用过期的JDK方法stop/destory等


public class StopThread implements Runnable {

    //1、定义一个标志位
    boolean flag = true;
    int count;

    @Override
    public void run() {
 while (flag) {
     System.out.println("跑步,run跑了-->" + count++ + "步");
 }
    }

    //2、设置公开的停止方法
    public void stop() {
 flag = false;
    }

    public static void main(String[] args) {
 StopThread p = new StopThread();
 new Thread(p).start();
 Timer timer = new Timer();
 timer.schedule(new TimerTask() {
     int timeno = 0;
     @Override
     public void run() {
  timeno++;
  System.err.println("timeno:"+timeno);
  if(timeno==10){
      p.stop();
      timer.cancel();
      System.out.println("线程停止,关闭定时器");
  }
     }
 },0,100);
    }
}

线程休眠 sleep

sleep不会是否锁,每个对象都有一个锁

sleep后的单位是ms,存在异常InterruptedException

休眠后进入就绪状态,可以用来模拟网络延时,倒计时

public class CountDownSheep {
    public static void main(String[] args) throws InterruptedException {
 tenDown();
    }

    private static void tenDown() throws InterruptedException {
 //获取系统当前时间
 Date now = new Date(System.currentTimeMillis());
 while (true) {
     Thread.sleep(1000);
     System.out.println(new SimpleDateFormat("HH:mm:ss").format(now));
     now = new Date(System.currentTimeMillis());
 }
    }
}

结果每秒输出一次

线程礼让 yield

让当前执行的线程暂停,但不阻塞;线程由运行状态转为就绪状态,等待CPU调度


public class YieldThread {
    public static void main(String[] args) {
 Mythread p = new Mythread();
 new Thread(p,"A").start();
 new Thread(p,"B").start();
    }
}

class Mythread implements Runnable{
    @Override
    public void run() {
 System.out.println(Thread.currentThread().getName()+"--》线程开始执行");
 Thread.yield();//礼让
 System.out.println(Thread.currentThread().getName()+"--》线程停止执行");
    }
}

礼让成功和礼让不成功的结果

线程插队 join

线程插队,其他线程阻塞,让插队线程先执行


public class JsonThread implements Runnable {

    @Override
    public void run() {
 for (int i = 0; i < 100; i++) {
     System.out.println("线程VIP来了-->" + i);
 }
    }

    public static void main(String[] args) throws InterruptedException {
 JsonThread p = new JsonThread();
 Thread thread = new Thread(p);
 thread.start();

 //主线程
 for (int i = 0; i < 500; i++) {
     if (i == 200) {
  thread.join();//插队
     }
     System.out.println("main==》" + i);
 }
    }
}

查看JDK帮助文档


public class StateThread {
    public static void main(String[] args) throws InterruptedException {
 Thread thread = new Thread(() -> {
     for (int i = 0; i < 5; i++) {
  try {
      Thread.sleep(1000);
  } catch (InterruptedException e) {
      e.printStackTrace();
  }
     }
     System.out.println(".....");
 });

 //观察状态
 Thread.State state = thread.getState();
 System.out.println(state);//NEW

 //观察启动后
 thread.start();//启动线程
 state = thread.getState();
 System.out.println(state);//RUNNABLE

 while (state != Thread.State.TERMINATED) {//TERMINATED 已退出的线程处于此状态
     Thread.sleep(100);
     state = thread.getState();//更新线程状态
     System.out.println(state);//输出转态
 }
    }
}

线程优先级 priority

Java提供了一个线程调度器 来监控就绪状态的线程,按照优先级1-10来决定调度那个线程先执行

优先级高的不一定先运行,只是概率高

MIN_PRIORITY = 1;
NORM_PRIORITY = 5;
MAX_PRIORITY = 10;


public class PriorityThread {
    public static void main(String[] args) {
 //默认优先级
 System.out.println(Thread.currentThread().getName() + "-->" + Thread.currentThread().getPriority());
 MyPriority p = new MyPriority();
 Thread t1 = new Thread(p,"t1");
 Thread t2 = new Thread(p,"t2");
 Thread t3 = new Thread(p,"t3");
 Thread t4 = new Thread(p,"t4");
 Thread t5 = new Thread(p,"t5");
 Thread t6 = new Thread(p,"t6");
 //先设置优先级再启动
 t1.start();
 t2.setPriority(1);
 t2.start();
 t3.setPriority(4);
 t3.start();
 t4.setPriority(Thread.MAX_PRIORITY);//10
 t4.start();
 t5.setPriority(8);
 t5.start();
 t6.setPriority(11);
 t6.start();
    }
}

class MyPriority implements Runnable {
    @Override
    public void run() {
 //打印线程名--》优先级
 System.out.println(Thread.currentThread().getName() + "-->" + Thread.currentThread().getPriority());
    }
}

守护线程 daemon

线程分为用户线程和守护线程

daemon = false; 默认是false 用户线程

虚拟机必须确保用户线程执行完毕 eg:main()

虚拟机不用等待守护线程执行完毕 eg:gc()


public class DaemonThread {
    public static void main(String[] args) {
 God god = new God();
 You you = new You();
 Thread thread = new Thread(god);
 thread.setDaemon(true);//默认是false 用户线程,这里设置为true 守护线程
 thread.start();//上帝守护线程启动

 new Thread(you).start();//你 用户线程启动
    }
}

//上帝
class God implements Runnable{
    @Override
    public void run() {
 while (true){
     System.out.println("上帝保佑着你");
 }
    }
}

//你 人生不过3w天
class You implements Runnable {
    @Override
    public void run() {
 //100年
 for (int i = 0; i < 36500; i++) {
     System.out.println("你一生都开心的活着");
 }
 System.err.println("============goodbye world!");//Hello World!
    }
}

转载请注明:文章转载自 www.mshxw.com
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号