多线程--Thread

进程和线程

在介绍java的多线程前,首先了解一下进程与线程的概念
进程:是资源分配的最小单位,是应用程序的执行示例,具有独立的内存空间个系统资源,包含1-n个线程
线程:是CPU调度和分派的基本单位。
如图,任务管理器中有一个IntellJ IDEA进程,其下又有4个线程
在这里插入图片描述

多线程

多线程通俗说就是:在一个进程中同时运行了多个线程,用来完成不同的工作。在计算机中,多个线程交替占用CPU资源,而非真正的并发执行。

多线程的好处

①充分利用CPU的资源
②简化编程模型
③带来良好的用户体验

主线程

main()方法即为主线程入口,产生其他子线程的线程,必须最后完成执行,因为它执行各种关闭动作。
示例
获取当前主线程的名称,并重新命名

public static void main(String args[]) {
	// Thread.currentThread()用于获取当前线程
	Thread t = Thread.currentThread(); 
	System.out.println("当前线程是: "+t.getName()); 
	// setName()用于对线程命名
	t.setName("MyJavaThread"); 
	System.out.println("当前线程名是: "+t.getName()); 
}

线程的创建

线程创建的方式有三种:
1、继承Thread类
2、实现Runnable接口
3、实现Callable接口
下面将一一介绍三种方法:

继承Thread类创建创建线程

Thread是由java.lang.Thread类提供的支持多线程编程的类
使用Thread类创建线程步骤:
①定义MyThread类继承Thread类
②重写run()方法,编写线程执行体
③创建线程对象,调用start()方法启动线程
示例

// TestThread继承Thread类
public class TestThread extends Thread{
	// 想要自定义操作,必须重写run()方法
    @Override
    public void run() {
    	// 循环输出线程名称和i的值
        for (int i = 0; i <5 ; i++) {
            System.out.println(Thread.currentThread().getName()+":"+i);
        }
    }
}
// 测试类
public class TestThread extends Thread{
    public static void main(String[] args) {
    	// 使用线程的方式一:直接实例化TestThread对象
    	TestThread tt1 = new TestThread();
        tt1.setName("线程1");
        tt1.start();
        // 方式二:将TestThread的实例对象作为Thread的参数创建线程:
        TestThread tt2 = new TestThread();
        Thread t1 = new Thread(tt2,"线程1");
        tt1.start();
    }
}

多个线程交替执行,不是真正的“并行”,线程每次执行时长由分配的CPU时间片长度决定
注意
启动线程是否可以直接调用run()方法?
在这里插入图片描述
实事上,如果直接调用run()方法,则会想调用普通类的普通方法一样,都在主线程中执行,有且仅有这一条线程;而调用start()方法,则会创建子进程,与主进程并行执行。
示例
继承Thread类,创建两个子线程,每个线程均输出20次消息数字、“你好”、线程名

public class Test2 extends Thread {
    @Override
    public void run(){
        for (int i = 1; i <= 20 ; i++) {
            System.out.println(i+".你好,来自线程"+Thread.currentThread().getName());
        }
    }
    public static void main(String[] args) {
        Test2  t = new Test2();
        Test2 t1 = new Test2();
        t.start();
        t1.start();
    }
}
实现Runnable接口创建线程

实现Runnable接口创建线程步骤:
①定义MyRunnable类实现Runnable接口
②实现run()方法,编写线程执行体
③创建线程对象,调用start()方法启动线程
示例
改写上题,改写成实现Runnable创建线程

public class Test1 implements Runnable{
    @Override
    public void run() {
        for (int i = 1; i <= 20 ; i++) {
            System.out.println(i+".你好,来自线程"+Thread.currentThread().getName());
        }
    }

    public static void main(String[] args) {
        Thread t1 = new Thread(new Test1());
        Thread t2 = new Thread(new Test1());
        t1.start();
        t2.start();
    }
}
两种方法创建线程的比较

继承Thread类:编写简单,可直接操作线程,适用于单继承
实现Runnable接口:避免单继承局限性,便于共享资源

实现Callable接口创建线程

示例:

public class TestCallable implements Callable<Integer> {
    @Override
    public Integer call() throws Exception {
        for (int i = 0; i < 10 ; i++) {
            System.out.println(Thread.currentThread().getName()+":"+(i+1));
        }
        return 3;
    }
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        FutureTask<Integer> f = new FutureTask<Integer>(new TestCallable());
        Thread t = new Thread(f);
        t.start();
        System.out.println(f.get());
    }
}

线程的状态

线程一共有五种状态:创建、就绪、运行、阻塞、死亡
在这里插入图片描述

线程调度

线程调度:按照特定机制为多个线程分配CPU的使用权
在这里插入图片描述

线程优先级

线程优先级由1~10表示,1最低,默认优先级为5。优先级高的线程获得CPU资源的概率较大
可通过setPriority()对线程优先级进行设置
示例
设置线程的优先级

public static void main(String[] args) {
	Thread t1 = new Thread("线程A");
	Thread t2 = new Thread("线程B");
	// MAX_PRIORITY为常量10,MIN_PRIORITY为常量1
	t1.setPriority(Thread.MAX_PRIORITY);
	t2.setPriority(Thread.MIN_PRIORITY);
}
线程休眠

线程休眠:让线程暂时睡眠指定时长,线程进入阻塞状态,睡眠时间过后线程会再进入可运行状态
可通过sleep()方法对线程进行休眠设置,参数为毫秒
示例:

public class TestThread extends Thread{
    @Override
    public void run() {
        for (int i = 0; i <5 ; i++) {
            System.out.println(Thread.currentThread().getName()+":"+i);
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
线程的强制执行

强制执行是指:使当前线程暂停执行,等待其他线程结束后再继续执行本线程
可以使用join()方法
示例:

// 父线程
public class FatherThread implements Runnable{
   @Override
   public void run(){
      System.out.println("父线程执行");
      Thread son =  new Thread( new SonThread());
      son.start();
      System.out.println("父线程:执行完毕。");
   }
}
// 子线程
public class SonThread implements Runnable{
   @Override
   public void run(){
      System.out.println("子线程执行");
      System.out.println("子线程:执行完毕。");
   }
}
// 测试类
public static void main(String[] args){
     System.out.println("主线程:开始执行");
     Thread fa =   new Thread(new FatherThread());
     fa.start();
     System.out.println("主线程:执行完毕");
}
//执行结果:
/**
*主线程:开始执行
*主线程:执行完毕
*父线程执行
*父线程:执行完毕。
*子线程执行
*子线程:执行完毕。
*/

加入join()方法改写程序

// 子类线程不变
// 父线程改写
public class FatherThread implements Runnable{
   @Override
   public void run(){
      System.out.println("父线程执行");
      Thread son =  new Thread( new SonThread());
      son.start();
      try{
         son.join();
      }catch(InterruptedException e){
         e.printStackTrace();
      }
      System.out.println("父线程:执行完毕。");
   }
}
// 测试类
public static void main(String[] args){
    System.out.println("主线程:开始执行");
	Thread fa =   new Thread(new FatherThread());
   	fa.start();
   	try{
    	fa.join();
   	}catch(InterruptedException e){
     	e.printStackTrace();
   }	
     	System.out.println("主线程:执行完毕");
}
//执行结果:
/**
*主线程:开始执行
*父线程执行
*子线程执行
*子线程:执行完毕。
*父线程:执行完毕。
*主线程:执行完毕
*/
线程的礼让

线程礼让:暂停当前线程,允许其他具有相同优先级的线程获得运行机会,该线程处于就绪状态,不转为阻塞状态
可以使用yield()

public class MyThread implements Runnable {
   public void run() {
      for (int i = 0; i < 5; i++) {
          System.out.println(Thread.currentThread().
                  getName() + "正在运行:" + i);
          if (i == 3) {
              System.out.print("线程礼让:");
              Thread.yield();
          }
      }
  }
}

示例:
①模仿多人爬山,每个线程代表一个人,可设置每人爬山速度,每爬完100米显示信息,爬到终点时给出相应提示

public class Test3 implements Runnable{
    @Override
    public void run() {
        for (int i = 1; i <= 10 ; i++) {
            System.out.println(Thread.currentThread().getName()+"爬完100米!");
        }
        System.out.println(Thread.currentThread().getName()+"到达终点!");
    }
    public static void main(String[] args) {
        Thread t1 = new Thread(new Test3(),"年轻人");
        Thread t2 = new Thread(new Test3(),"老年人");
        t1.setPriority(10);
        t2.setPriority(1);
        t1.start();
        t2.start();
    }
}

②显示主线程、子线程默认优先级,将主线程设置为最高优先级、子线程设置为最低优先级并显示

public class Test4 {
    public static void main(String[] args) {
        Thread t = new Thread();
        System.out.println("***********显示默认优先级***********");
        System.out.println("主线程名:"+Thread.currentThread().getName()+",优先级:"+Thread.currentThread().getPriority());
        System.out.println("子线程名:"+t.getName()+",优先级:"+t.getPriority());
        Thread.currentThread().setPriority(10);
        t.setPriority(1);
        System.out.println("***********修改默认优先级后***********");
        System.out.println("主线程名:"+Thread.currentThread().getName()+",优先级:"+Thread.currentThread().getPriority());
        System.out.println("子线程名:"+t.getName()+",优先级:"+t.getPriority());
    }
}

③某科室一天需看普通号50个,特需号10个,特需号看病时间是普通号的2倍,开始时普通号和特需号并行叫号,叫到特需号的概率比普通号高
当,通号叫完第10号时,要求先看完全部特需号,再看普通号,使用多线程模拟这一过程

public class Test5 implements Runnable {
    @Override
    public void run() {
        for (int i = 1; i <= 10 ; i++) {
            System.out.println(Thread.currentThread().getName()+":"+i+"号病人正在看病!");
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    public static void main(String[] args) {
        Thread.currentThread().setName("普通号");
        Thread t = new Thread(new Test5(),"特需号");
        t.start();
        for (int i = 1; i <= 50 ; i++) {
            if(i==11){
                try {
                    t.join();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println(Thread.currentThread().getName()+":"+i+"号病人正在看病!");
            try {
                Thread.sleep(50);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
多线程共享数据引发

看如下代码

public void run() {
	while (true) {
	     //省略代码:判断是否余票				
	     num++;
	     count--;
	     try {
	         Thread.sleep(500); //模拟网络延时
	     } catch (InterruptedException e) {//…}
	         System.out.println(Thread.currentThread().getName()
	                 + "抢到第" + num + "张票,剩余" + count + "张票!");
     }
}

以上代码会出现如下问题:
①不是从第1张票开始
②存在多人抢到一张票的情况
③有些票号没有被抢到
为了解决以上问题,我们可以使用访问修饰符:synchronized

同步方法 synchronized

synchronized使用可有两种途径:

解决上述问题可将方法改成如下:
①访问修饰符 synchronized 返回类型 方法名(参数列表){……}
示例:
解决上述问题可以加上synchronized

// 同步方法:售票
public synchronized void sale() {	
    if (count <= 0) {
        flag = true;
        return;
     }
       // 省略代码:修改数据
       // 省略代码:显示信息
 }

②使用synchronized关键字修饰代码块
syncObject为需同步的对象,通常为this

synchronized(syncObject){
    //需要同步的代码
}

synchronized作用
①同一时刻只能有一个线程进入synchronized(this)同步代码块
②当一个线程访问一个synchronized(this)同步代码块时,其他synchronized(this)同步代码块同样被锁定
③当一个线程访问一个synchronized(this)同步代码块时,其他线程可以访问该资源的非synchronized(this)同步代码

示例
网络购票:“桃跑跑”、“张票票”、“黄牛党”共同抢10张票,且限“黄牛党”只能抢一张票

public class Test7 implements Runnable {
    int ticket = 10;
    int sold = 0;
    @Override
    public void run() {
        while (ticket>0){
            synchronized (this) {
                ticket--;
                sold++;
                if(ticket<0)return;
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                if(Thread.currentThread().getName().equals("黄牛党")){
                    System.out.println(Thread.currentThread().getName() + "购买了第" + sold + "张票,剩余" + ticket + "张票");
                    break;
                }
                System.out.println(Thread.currentThread().getName() + "购买了第" + sold + "张票,剩余" + ticket + "张票");
            }
        }
    }
    public static void main(String[] args) {
        Test7 t = new Test7();
        Thread th1 = new Thread(t,"桃跑跑");
        Thread th2 = new Thread(t,"张票票");
        Thread th3 = new Thread(t,"黄牛党");
        th1.start();
        th2.start();
        th3.start();
    }
}
  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值