Java多线程

本文详细介绍了Java中的多线程概念,包括进程与线程的区别,线程的组成部分如CPU时间片、堆和栈空间,以及创建线程的四种方式。讨论了线程安全问题,如同步代码块、死锁、线程通信机制如wait、notify和notifyAll。最后,文章提到了线程池和Callable接口,并简要提及了Future接口和Lock接口在多线程编程中的作用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

进程:程序是静止的,只有真正运行时的程序,才被称为进程。

特点:
单核CPU在任何时间点上。
只能运行一个进程。
宏观并行、微观串行。

线程:又称轻量级进程。程序中的一个顺序控制流程,同时也是CPU的基本调度单位。进程由多个线程组成,彼此间完成不同的工作,交替执行,称为多线程。

线程和进程的区别

进程是操作系统资源分配的基本单位,而线程是CPU的基本调度单位。
一个程序运行后至少有一个进程。
一个进程可以包含多个线程,但是至少需要有一个线程。
进程间不能共享数据段地址,但同进程的线程之间可以。

线程的组成:

  1. CPU时间片:操作系统会为每个线程分配执行时间。
  2. 运行数据:
    (1)堆空间:存储线程要使用的对象。多个线程可以共享堆中的对象。
    (2)栈空间:存储线程需使用的局部变量,每个线程都拥有独立的栈。
  3. 线程的逻辑代码。
    创建线程的方式:
    1、继承Thread类
    2、实现Runnable接口
    3、通过线程池
    4、实现Callable接口

一、继承Thread类

public class MyThread extends Thread {
	
	public MyThread() {
		// TODO Auto-generated constructor stub
	}
	public MyThread(String name) {
		super(name);
	}
	 
	@Override
	public void run() {
		for(int i=0;i<100;i++) {
			System.out.println("子线程:"+i);
		}
	}
}
//this.getId获取线程Id ||  Thread.currentThread().getName()
//this.getName获取线程名称 || Thread.currentThread().getID()
public class TestThread {
	public static void main(String[] args) {
		//1创建线程对象
		MyThread myThread=new MyThread();
		myThread.start();//myThread.run()
		//创建第二个线程对象
		MyThread myThread2=new MyThread();
		myThread2.start();
		//主线程执行
		for(int i=0;i<50;i++) {
			System.out.println("主线程======"+i);
		}
	}
}

二、实现Runnable接口

java
public class MyRunnable implements Runnable{
	@Override
	public void run() {
		for(int i=0;i<100;i++) {	
		System.out.println(
			Thread.currentThread().getName()+" .."+i);
		}
	}
}
1、线程优先级为1-10,默认是5。线程级别越高,获取CPU机会越多。
2、线程对象被创建,即为初始状态,只在堆中开辟内存,与常规对象无异。
3、调用start()方法后,线程进入就绪状态。只有获取到时间片之后,才会进
入运行状态。线程结束,释放时间片。
4、线程状态:新建、就绪、运行、等待、终止。
//当前线程主动休眠 millis 毫秒。
public static void sleep(long millis) 
//当前线程主动放弃时间片,回到就绪状态,竞争下一次时间片。 
public static void yield()
//允许其他线程加入到当前线程中
public final void join() 
//线程优先级为1-10,默认为5,优先级越高,表示获取CPU机会越多。
public void setPriority(int)

线程安全问题:

当多线程并发访问临界资源时,如果破坏原子操作,可能会造成数据不一致。
临界资源:共享资源(同一对象),一次仅允许一个线程使用,才可保证其正确性。
原子操作:不可分割的多步操作,被视作一个整体,其顺序和步骤不可打乱或缺省。

同步代码块:

每个对象都有一个互斥锁标记,用来分配给线程的。
只有拥有对象互斥锁标记的线程,才能进入对该对象加锁的同步代码块。
线程退出同步代码块时,会释放相应的互斥锁标记。
synchronized(临界资源对象){
	//代码原子操作
}


public class Ticket implements Runnable{
	
	private int ticket=100;
	//创建锁
	//private Object obj=new Object();
	
	@Override
	public void run() {
		
		while(true) {
			synchronized (this) {//this ---当前对象
				if(ticket<=0) {
					break;
				}
				System.out.println(
				Thread.currentThread().getName()+"卖了第"+
				ticket+"票");
				ticket--;
			}		
		}
	}
}

死锁:
当第一个线程拥有A对象锁标记,并等待B对象锁标记,同时第二个线程拥有B对象锁标记,并等待A对象锁标记时,产生死锁。

//死锁案例
//Main
public class TestDeadLock {
	public static void main(String[] args) {
		Boy boy=new Boy();
		Girl girl=new Girl();
		girl.start();
		try {
			Thread.sleep(100);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		boy.start();
	}
}

//MyLock
public class MyLock {
	//两个锁(两个筷子)
	public static Object a=new Object();
	public static Object b=new Object();
}

//BoyThread
public class Boy extends Thread{
	@Override
	public void run() {
		synchronized (MyLock.a) {
			System.out.println("男孩拿到了a");
			synchronized (MyLock.b) {
				System.out.println("男孩拿到了b");
				System.out.println("男孩可以吃东西了...");
			}
		}
	}
}

//GirlThred
public class Girl extends Thread {
	@Override
	public void run() {
		synchronized (MyLock.b) {
			System.out.println("女孩拿到了b");
			synchronized (MyLock.a) {
				System.out.println("女孩拿到了a");
				System.out.println("女孩可以吃东西了...");
			}
		}
	}
}

线程通信:
等待:
wait():wait会释放锁。sleep()是不释放锁的。
通知:
notify():随机通知一个在等待队列中的线程。
notifyAll():通知所有进入了等待队列的线程。

生产者消费者问题:

//Bread
public class Bread {
	private int id;
	private String productName;
	public Bread() {
		// TODO Auto-generated constructor stub
	}
	public Bread(int id, String productName) {
		super();
		this.id = id;
		this.productName = productName;
	}
	public int getId() {
		return id;
	}
	public void setId(int id) {
		this.id = id;
	}
	public String getProductName() {
		return productName;
	}
	public void setProductName(String productName) {
		this.productName = productName;
	}
	@Override
	public String toString() {
		return "Bread [id=" + id + ", productName=" + productName + "]";
	}
}
//BreadCon
public class BreadCon {
	//存放面包的数组
	private Bread[] cons=new Bread[6];
	//存放面包的位置
	private int index=0;
	
	//存放面包
	public synchronized void input(Bread b) { //锁this
		//判断容器有没有满
		while(index>=6) {
			try {
				this.wait();
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		
		cons[index]=b;
		System.out.println(Thread.currentThread().getName()+"生产了"+b.getId()+"");
		index++;
		//唤醒
		this.notifyAll();
	}
	//取出面包
	public synchronized void output() {//锁this
		while(index<=0) {
			try {
				this.wait();
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		index--;
		Bread b=cons[index];
		System.out.println(Thread.currentThread().getName()+"消费了"+b.getId()+" 生产者:"+b.getProductName());
		cons[index]=null;
		//唤醒生产者
		this.notifyAll();
	}
}
//Consume
public class Consume implements Runnable{

	private BreadCon con;
	
	public Consume(BreadCon con) {
		super();
		this.con = con;
	}

	@Override
	public void run() {
		for(int i=0;i<30;i++) {
			con.output();
		}
	}

}
//Product
public class Prodcut implements Runnable {

	private BreadCon con;
	
	public Prodcut(BreadCon con) {
		super();
		this.con = con;
	}

	@Override
	public void run() {
		for(int i=0;i<30;i++) {
			con.input(new Bread(i, Thread.currentThread().getName()));
		}
	}
	
}
//Test
public class Test {
	public static void main(String[] args) {
		//容器
		BreadCon con=new BreadCon();
		//生产和消费
		Prodcut prodcut=new Prodcut(con);
		Consume consume=new Consume(con);
		//创建线程对象
		Thread chenchen=new Thread(prodcut, "晨晨");
		Thread bingbing=new Thread(consume, "消费");
		Thread mingming=new Thread(prodcut, "明明");
		Thread lili=new Thread(consume, "莉莉");
		//启动线程
		chenchen.start();
		bingbing.start();
		mingming.start();
		lili.start();
	}
}

三、线程池创建线程:

1、频繁创建和销毁线程会比较耗性能。有了线程池就不要创建更多的线程来完成任务,因为线程可以重用。
2、线程池用维护者一个队列,队列中保存着处于等待(空闲)状态的线程。不用每次都创建新的线程。
public class TestThreadPool {
	public static void main(String[] args) {
		//1.1创建固定线程个数的线程池
		//ExecutorService es=Executors.newFixedThreadPool(4);
		//1.2创建缓存线程池,线程个数由任务个数决定
		ExecutorService es=Executors.newCachedThreadPool();
		//1.3创建单线程线程池
		//Executors.newSingleThreadExecutor();
		//1.4创建调度线程池  调度:周期、定时执行
		//Executors.newScheduledThreadPool(corePoolSize)
		Executors.newScheduledThreadPool(3);
		//2创建任务
		Runnable runnable=new Runnable() {
			private int ticket=100;
			@Override
			public void run() {
				while(true) {
					if(ticket<=0) {
						break;
					}
					System.out.println(Thread.currentThread().getName()+"买了第"+ticket+"张票");
					ticket--;
				}
			}
		};
		//3提交任务
		for(int i=0;i<5;i++) {
			es.submit(runnable);
		}
		//4关闭线程池
		es.shutdown();//等待所有任务执行完毕 然后关闭线程池,不接受新任务。
	}
}

四。Callable接口创建线程

public interface Callable< V >{
	public V call() throws Exception;
}
public class TestCallable {
	public static void main(String[] args) throws Exception{
		//功能需求:使用Callable实现1-100和
		//1创建Callable对象
		Callable<Integer> callable=new Callable<Integer>() {
			@Override
			public Integer call() throws Exception {
				System.out.println(
				Thread.currentThread().getName()+"开始计算");
				int sum=0;
				for(int i=1;i<=100;i++) {
					sum+=i;
					Thread.sleep(100);
				}
				return sum;
			}
		};
		//2把Callable对象 转成可执行任务
		FutureTask<Integer> task=new FutureTask<>(callable);
		
		//3创建线程
		Thread thread=new Thread(task);
		
		//4启动线程
		thread.start();
		
		//5获取结果(等待call执行完毕,才会返回)
		Integer sum=task.get();
		System.out.println("结果是:"+sum);
	}
}

Future接口:

Future接口表示将要执行完任务的结果。
get()以阻塞形式等待Future中的异步处理结果(call()的返回值)。
//案例 计算1-100的值
public class TestFuture {
	public static void main(String[] args) throws Exception{
		//1创建线程池
		ExecutorService es=Executors.newFixedThreadPool(1);
		//2提交任务 Future:表示将要执行完任务的结果
		Future<Integer> future=es.submit(new Callable<Integer>() {

			@Override
			public Integer call() throws Exception {
				System.out.println(Thread.currentThread().getName()+"开始计算");
				int sum=0;
				for(int i=1;i<=100;i++) {
					sum+=i;
					Thread.sleep(10);
				}
				return sum;
			}
		});
		
		//3获取任务结果,等待任务执行完毕才会返回.

		System.out.println(future.get());
		
		//4关闭线程池
		es.shutdown();
	}
}

Lock接口:

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值