java多线程

前言:

进程:运行中的程序的实例(例如应用程序)。

线程:进程中的执行单元。

关系:一个进程可以包含多个线程,线程是进程的一部分。

区别

(1)进程拥有独立的内存空间和资源,而线程共享进程的内存空间和资源。

(2)进程间的切换开销较大,需要保存和恢复整个进程的上下文,而线程的切换开销较小,只需保存和恢复线程的上下文。

(3)多个线程可以并发地执行,从而提高程序的响应性能,而进程之间的并发性需要通过多进程或进程间通信来实现。

(4)线程之间共享内存,可以方便地访问共享数据,但也需要考虑线程安全问题;而进程之间的访问需要通过进程间通信进行,开销相对较大。

1.多线程实现的三种方式

 1.1 继承Thread类的方式进行实现

import java.util.Scanner;
import java.awt.*;

public class Test5 {
  public static void main(String[] args) {
	  Dage t1=new Dage("hello");
	  Dage t2=new Dage("world");
	  
	  t1.start();
	  t2.start();
  }
}
class Dage extends Thread {
    private String name;
    public Dage(String name) {
    	this.name = name;
    }
	public void run() {
		System.out.print(name+" ");
	}
}

1.2 实现Runnable接口的方式进行实现

import java.util.Scanner;
import java.awt.*;

public class Test1 {
   public static void main(String[] args) {
	  Thread t1 =new Thread(new Dage("hello"));
	  Thread t2 =new Thread(new Dage("world"));
	  
	  t1.start();
	  t2.start();
   }
}
class Dage implements Runnable {
	private String name;
	
	public Dage(String name) {
		this.name = name;
	}
	public void run() {
		System.out.print(name+" ");
	}
}

 1.3 利用Callable接口和Future接口方式实现

import java.util.Scanner;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
import java.awt.*;

public class Test5 {
  public static void main(String[] args) throws Exception {
	  Dage ge=new Dage();
	  FutureTask<String> future=new FutureTask<>(ge);
	  Thread t1=new Thread(future);
	  
	  t1.start();
	  String a=future.get();
	  System.out.println(a);
  }
}
class Dage implements Callable<String> {
    public String call() throws Exception {
    	String s="hello world";
    	return s;
    }
}

1.4 扩展写法

public class Test03 {
    public static void main(String[] args) throws InterruptedException {
        Dage t1=new Dage("hello");
        Dage t2=new Dage("world");
        t1.start();
        t2.start();
        Thread tt=new Thread(){
            @Override
            public void run() {
                System.out.print(" 匿名内部类 ");
            }

        };
        //匿名内部类
        tt.start();
        //lambda写法
        Thread ttt=new Thread(  ()->{
            System.out.println(" 学习 ");
        });
        ttt.start();
        System.out.println(" play game ");
    }
    //内部类
    static class Dage extends Thread {
        private String name;
        public Dage(String name) {
            this.name = name;
        }
        @Override
        public void run() {
            System.out.print(name+" ");
        }
    }
}

问题:

在上面三种方式中,不难发现,创建两线程的话,可能会出现打印出“world hello”的情况!,这与它们抢占CPU的情况有关,如何解决这一现象呢?

1.4 可以考虑线程的优先级,具体代码如下:

public class Test6{
    public static void main(String[] args) {
        Dage t1=new Dage("hello");
        Dage t2=new Dage("world");

        t1.start();
        t2.start();

        t1.setPriority(10);
        t2.setPriority(1);
    }
}
class Dage extends Thread {
    private String name;
    public Dage(String name) {
        this.name = name;
    }
    public void run() {
        System.out.print(name+" ");
    }
}

(说明:这样只是提高打印出“hello world"的概率但不是绝对的,所以适用性不强)

1.5 优先级不是绝对的!,那只能考虑插入线程了,具体代码如下:

public class Test6{
    public static void main(String[] args) throws InterruptedException {
        Dage t1=new Dage("hello");
        Dage t2=new Dage("world");
        t1.start();
        t1.join();
        t2.start();

    }
}
class Dage extends Thread {
    private String name;
    public Dage(String name) {
        this.name = name;
    }
    public void run() {
        System.out.print(name+" ");
    }
}

备注:这样总算解决了,多线程有许多常见的成员方法,解这个问题不限这一个方法。

2.经典多线程案例"卖火车票”

问题描述:某火车站出售100张火车票,有三个窗口在卖,请设计一个程序模拟卖票。

2.1 直接上代码(猜猜有没有问题?),代码如下:

public class Test6{
    public static void main(String[] args){
        Dage t1=new Dage();
        Dage t2=new Dage();
        Dage t3=new Dage();

        t1.setName("窗口1");
        t2.setName("窗口2");
        t3.setName("窗口3");

        t1.start();
        t2.start();
        t3.start();
    }
}
class Dage extends Thread {
    int ticket=0;
    public void run() {
        while(true){
            if(ticket<100){
                ticket++;
                System.out.println(getName()+"正在卖第"+ticket+"张票");
            }else{
                break;
            }
        }
    }
}

问题:运行程序不难发现三个窗口竟然都卖了100张票,加起来有300张,看来这程序需要优化。

2.1 多线程加锁,具体代码如下:

public class Test6{
    public static void main(String[] args){
        Dage t1=new Dage();
        Dage t2=new Dage();
        Dage t3=new Dage();

        t1.setName("窗口1");
        t2.setName("窗口2");
        t3.setName("窗口3");

        t1.start();
        t2.start();
        t3.start();
    }
}
class Dage extends Thread {
    static int ticket=0;
    static Object abc=new Object();
    public void run() {
        while(true) {
            synchronized (abc) {
                if (ticket < 100) {
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                    ticket++;
                    System.out.println(getName() + "正在卖第" + ticket + "张票");
                } else {
                    break;
                }
            }
        }
    }
}

2.在上面打印“hello word”的两线程中,不难发现它们之间竟然没有什么联系!,这可不符合多线程存在的意义,所以优化一下双线程,于是有了一个简单的“猜数字”游戏的程序,一个线程负责输入猜的数字,一个线程负责判断猜的情况,案例具体代码如下:

import java.util.Scanner;

public class Test4{
  public static void main(String[] args) {
  	Num n = new Num();
  	CreateThread create = new CreateThread(n);
  	JudgeThread judge = new JudgeThread(n);
  	judge.start();
  	create.start();
  }
}
// Num类完成线程之间的同步
class Num {
  int number;// 保存猜测的数
  boolean first = true;
  boolean stop = false;
}
//输入猜测的数
class CreateThread extends Thread {
  Num num;
  public CreateThread(Num num) {
  	this.num = num;
  }
  public void run(){
  	Scanner sc = new Scanner(System.in);
  	while (true) {
  		try {
  			synchronized (num) {
  				if (num.first) {// 如果是第一次执行
  					num.wait();
  				} else if (num.stop) {
  					System.out.println("猜数结束");
  					return;
  				} else {
  					System.out.print("请输入猜测的数(1-100):");
  					int guess = sc.nextInt();
  					// 猜数结束就需要等待,判断线程
  					num.number = guess;
  					num.notify();
  					num.wait();
  				}
  			}
  		} catch (Exception e) {
  			e.printStackTrace();
  		}
  	}
  }
}
//判断线程
class JudgeThread extends Thread {
  Num num;
  public JudgeThread(Num num) {
  	this.num = num;
  }
  public void run() {
  	int random = 0;
  	while (true) {
  		try {
  			synchronized (num) {
  				if (num.first) {
  					random = (int) (Math.random() * 100 + 1);
 				//  System.out.println("产生的随机数为:" + random);
  					num.first = false;
  					num.notify();
  					num.wait();
  				} else {
  					// 判断过程
  					if (num.number < random) {
  						System.out.println("猜小了!");
  					} else if (num.number > random) {
  						System.out.println("猜大了!");
  					} else {
  						System.out.println("猜对了!");
  						num.stop = true;
  						num.notify();// 猜对结束之前
  						return;
  					}
  					num.notify();
  					num.wait();
  				}
  			}
  		} catch (Exception e) {
  			e.printStackTrace();
  		}
  	}
  }
}

3.线程池

线程池是一种多线程处理的机制,它包含了一定数量的线程,当有任务需要处理时,就从线程池中取出一个线程来处理任务。在任务处理完成后,该线程并不会立即退出,而是继续留在线程池中等待下一次任务的到来。这样可以避免频繁创建和销毁线程所带来的开销,提高了程序的效率。

线程池通常由两个部分组成:工作线程和任务队列。工作线程是线程池中的线程,它们用于执行具体的任务;任务队列是用来存储任务的队列,当有新的任务到达时,就将任务加入到队列中等待被执行。

线程池的主要优点如下:

  1. 线程复用:减少线程创建和销毁的开销,提高程序的效率;
  2. 控制并发度:通过限制线程池中的线程数量,可以有效地控制系统的并发度,避免资源竞争和阻塞;
  3. 提高响应速度:线程池可以预先创建一定数量的线程,当有任务到达时,可以立即执行,从而提高系统的响应速度。

具体原理见图解:

代码模板:

import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
//线程池:系统的渐状性
public class Test05 {
    public static void main(String[] args) {
        //核心线程池的大小
        int corePoolSize=2;
        //核心线程池的最大数量
        int maxPoolSize=4;
        //线程最大空闲时间
        long keepAliveTime=10;
        //时间单位:秒
        TimeUnit unit=TimeUnit.SECONDS; //enum枚举,常量
        //有界阻塞队列 容量为2    最多允许放入两个空闲任务
        BlockingQueue<Runnable> workQueue=new ArrayBlockingQueue<>(2);
        // 线程创建工厂
        ThreadFactory threadFactory=new NameTreadFactory();
        //线程池拒绝策略
        RejectedExecutionHandler handler=new MyIgnorePolicy();
        ThreadPoolExecutor executor=null;
        try{
            //推荐的创建线程池的方式, 不推荐的使用现成API创创建线程池,
            executor=new ThreadPoolExecutor(corePoolSize,maxPoolSize,keepAliveTime,unit,workQueue,threadFactory,handler);
            //预启动所有核心线程 提升效率
            executor.prestartAllCoreThreads();
            //任务数量
            int count=10;
            for (int i =1; i <=count; i++) {
                RunnableTask task=new RunnableTask(String.valueOf(i));
                executor.submit(task);//提交任务到线程池 (如果任务为10,则有4个任务无法执行)
            }
        }finally {
            assert executor!=null; //断言, 可开关
            executor.shutdown();
        }
    }

    static class NameTreadFactory implements ThreadFactory{
        //线程id AtomicInteger 原子类
         private final AtomicInteger threadId=new AtomicInteger();

         @Override
         public Thread newThread(Runnable runnable){
             Thread t=new Thread(runnable,"线程"+threadId.getAndIncrement());
             System.out.println(t.getName()+"已经被创建");
             return t;
         }
    }

    public static  class MyIgnorePolicy implements RejectedExecutionHandler{
        @Override                       //被拒绝任务             线程池对象
        public  void rejectedExecution(Runnable runnable,ThreadPoolExecutor e){
            doLog(runnable,e);
        }
        private  void doLog(Runnable runnable,ThreadPoolExecutor e){
            System.err.println("线程池:"+e.toString()+runnable.toString()+"被拒绝执行");
        }
    }

    //任务类
    static class  RunnableTask implements Runnable{
        private String name;
        public RunnableTask(String name){
            this.name=name;
        }
        @Override
        public void run(){
            try{
                System.out.println(this.toString()+"is running");
                Thread.sleep(3000);
            }catch (Exception e){
                e.printStackTrace();
            }
        }
        @Override
        public String toString(){
            return "RunnableTask [name="+name+"]";
        }
    }
}

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值