Java多线程-1

1. 概念

在这里插入图片描述在操作系统中运行的程序是进程,一个进程可以有多个线程。线程是CPU调度和执行的单位。

注意:很多多线程是模拟出来的,真正的多线程是指有多个CPU,即多核。

  1. 线程就是独立执行的路径
  2. 在程序运行时,即使没有自己创建线程,后台也会有多个线程,如主线程、gc线程
  3. main()为主线程,为系统的入口,用于执行整个程序
  4. 在一个进程中开辟多个线程,线程的运行由调度器安排调度,调度器是与操作系统紧密相连的,先后顺序不能人为干预的
  5. 对同一份资源操作时,会存在资源抢夺的问题,需要加入并发控制
  6. 线程会带来额外开销,如CPU调度时间,并发控制开销
  7. 每个线程在自己的工作内存交互,内存控制不当会造成数据不一致

线程的三种创建方式:
继承Thread类、实现Runnable接口、实现Callable接口

2. 继承Thread类

步骤:(线程不一定立即执行,由CPU进行调度)
1. 自定义线程类继承Thread类
2. 重新run()方法,编写线程执行体
3. 创建线程对象,调用start()方法启动线程

public class TestThread extends Thread{
	@Override
	public void run(){
		for(int i = 0; i <200; i++){
			System.out.println("Thread2" + i);
		}
	}
	
	public static void main(String[] args){
		TestThread tt = new TestThread();
		tt.start();
	}
}

3. 实现Runnable接口

步骤:(推荐使用Runnable接口,因为Java是单继承)
1. 定义MyRunnable类实现Runnable接口
2. 实现run()方法,编写线程执行体
3. 创建线程对象,调用start()方法启动线程

public class TestThread implements Runnable{
	@Override
	public void run(){
		for(int i = 0; i <200; i++){
			System.out.println("Thread2" + i);
		}
	}
	
	public static void main(String[] args){
		TestThread tt = new TestThread();
		new Thread(tt).start();
	}
}

4. 实现Callable接口

  1. 实现Callable接口需要返回值类型
  2. 重写call方法,要抛出异常
  3. 创建目标对象
  4. 创建执行服务:ExecutorService ser = Executors.newFixedThreadPool(1);
  5. 提交执行: Futhure<Boolean) result1 = ser.submit(1);
  6. 获取结果:boolean r1 =result.get()
  7. 关闭服务:ser.shutdownNow()

5. 静态代理

代理的原则:

  1. 真实对象和代理对象要实现同一个接口
  2. 代理对象要代理真实角色
    代理的好处:
  3. 代理对象可以做很多真实对象做不了的事情
  4. 真实对象专注做自己的事情

场景:电影院卖电影票,在播放电影的同时电影院播放广告赚取额外的收入

/*
* 先有一个接口
* */
public interface Movie{
	void play();
}
/*
* 真正的电影,带有播放功能
* */
public class RealMovie implements Movie{
	@Override
	public void play(){
		System.out.println("播放电影");
	}
}

/*
* 代理类,电影院
* */
public class Cinema implements Movie{
	//代理对象要代理真实角色
	RealMovie realMovie;
	//构造函数,带真实角色
	public Cinema(RealMovie realMovie){
		this.realMovie = realMovie;
	}
	@Override
	public void play(){
		before();
		realMovie.play();
	}

	public void before(){
		System.out.println("播放广告");
	}
}

/*
* 实现
* */
public class Test{
	public static void main(String[] args){
		RealMovie realMovie = new RealMovie();
		Movie movie = new Cinema(realMovie);
		movie.play();
	}
}

6. Lamda表达式

作用:避免匿名内部类定义过多;实质属于函数式编程的概念

函数式接口:
任何接口,如果只包含唯一一个抽象方法,那么它是一个函数式接口

对于函数式接口,可以通过lamda表达式来创建接口的对象。
(params) -> expression[表达式]
(params) -> statement[语句]
(params) -> {statements}

总结:
1. lamda表达式只有一行代码,可以去掉花括号,如有多行用代码块包裹
2. lamda表达式的前提是函数式接口
3. 多个参数也可以去掉参数类型,要去掉就全去掉,必须加上括号

7. 线程状态

7.1 线程的五种状态

在这里插入图片描述
在这里插入图片描述

7.2 线程方法

在这里插入图片描述
其他方法:
获取当前线程的名字:Thread.currentThread().getName();

7.3 线程停止

【已废弃】不推荐使用JDK提供的stop() destroy()方法
推荐线程自己停止下来
建议使用一个标志位进行终止变量,如flag = false

pubulic class TestStop implements Runnable{
	private boolean flag=true;
	//重写run方法
	@Override
	public void run(){
		while(flag){
			System.out.println("自定义线程---->"+i++);
		}
	}
	//写stop方法
	public void stop(){
		this.flag = false;
	}
	
	public static void main(String[] args){
		TestStop ts = new TestStop();
		new Thread(ts).start();
		for(int i=0; i<1000; i++){
			Sysem.out.println("主线程----->" +i);
			if(i=900){
				//这样主线程会跑1000次,自定义线程跑900次
				ts.stop();
			}
		}
	}
}

7.4 线程休眠sleep

每个对象都有一个锁,sleep不会释放锁
用处:模拟网络延时、模拟倒计时
用法:Thread.sleep(); 抛出异常

7.5 线程礼让yield

比如有两个线程A和B,CPU正在执行A,在礼让后A跳出CPU,A和B同时等待CPU调度,具体调度哪个还是要看CPU。因此礼让不一定成功,看CPU心情。

7.6 线程强制执行join

本质是插队,强制执行的线程执行完了才能执行其他线程,独占CPU。
代码中少用,避免线程阻塞

7.7 观察线程状态

Thread.State state = thread.getState();
线程状态:
NEW:尚未启动的线程
RUNABLE:在Java虚拟机中执行的线程
BLOCKED:被阻塞等待监视器锁定的线程
WAITING:正在等待另一个线程执行特定动作的线程
TIMED WAITING:正在等待另一个线程执行动作达到指定等待时间的线程
TERMINATED:已退出的,同一个线程已退出,不能再次启动

8. 线程的优先级

Java提供一个线程调度器来监控程序中启动后进入就绪状态的所有线程。线程调度器按照优先级决定应该调度哪个线程来执行。注意,设置优先级高不一定真的先执行,只是代表权重比较大,具体要执行哪个,还是CPU决定。
线程优先级用数字表示:1~10
Thread.MIN_PRIORITY=1;
Thread.MAX_PRIORITY=10;
Thread.NORM_PRIORITY=5;

相关方法:
getPriority() setPriority(int XXX)

main的优先级是不能设置的,默认优先级是5,优先级要先设置再启动才能生效!

9. 守护daemon线程

线程分为用户线程守护线程
虚拟机必须保证用户线程执行完毕
虚拟机不用等待守护线程执行完毕
守护线程有如:垃圾回收、内存监控、记录操作日志等

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,您的问题是关于Java多线程的存钱取钱问题。这是一个经典的多线程同步问题,可以通过使用synchronized关键字来实现线程安全。下面是一个简单的示例代码: ```java public class BankAccount { private int balance; public BankAccount(int balance) { this.balance = balance; } public synchronized void deposit(int amount) { balance += amount; System.out.println("Deposit successful. Balance: " + balance); } public synchronized void withdraw(int amount) { if (balance < amount) { System.out.println("Withdraw failed. Insufficient balance."); return; } balance -= amount; System.out.println("Withdraw successful. Balance: " + balance); } } public class Main { public static void main(String[] args) { BankAccount account = new BankAccount(1000); // Create two threads to simulate deposit and withdraw Thread depositThread = new Thread(() -> { for (int i = 0; i < 5; i++) { account.deposit(100); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } }); Thread withdrawThread = new Thread(() -> { for (int i = 0; i < 5; i++) { account.withdraw(200); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } }); depositThread.start(); withdrawThread.start(); } } ``` 在这个示例代码中,我们创建了一个银行账户类BankAccount,并在其中实现了deposit和withdraw方法,并使用synchronized关键字来保证线程安全。 在main方法中,我们创建了两个线程来模拟存款和取款操作,每个线程执行5次操作。我们使用Thread.sleep方法来模拟每个操作之间的间隔,以便更好地观察多线程操作的结果。 当多个线程同时访问BankAccount对象的deposit和withdraw方法时,synchronized关键字可以确保每个方法只能被一个线程访问,从而避免了竞争条件和数据不一致的问题。 希望这个示例代码能够回答您的问题。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值