JAVA_多线程

线程的创建和使用

Thread类

  1. 自定义线程类继承Thread类
  2. 重写run()方法,编写线程执行体
  3. 创建线程对象,调用start()方法启用线程

常用方法:start\run(需重写)\currentThread\getName\setName\yield(争球)\join(球权转换)\stop(不建议使用)\sleep(long milltime)\isAlive\getPriority\setPriority(优先级是概率上的)

Runnable接口

  1. 创建一个实现了Runnable接口的类
  2. 实现类区实现Runnable中的抽象方法:run()
  3. 创建实现类的对象
  4. 将此对象作为参数传递到Thread类的构造器中,创建Thread类的对象
  5. 通过Thread类的对象调用start()

开发中优先选择实现Runnable接口方式
原因:1.实现的方式没有类的单继承的局限性 2.实现的方式更适合来处理多个线程有共享数据的情况

线程的声明周期、同步、通信

周期:新建、就绪、运行、阻塞、死亡

通过同步机制解决线程的安全问题:

方式一:同步代码块

synchronized(同步监视器){
	//需要被同步的代码
}
//说明:1.操作共享数据的代码即为需要被同步的代码 
//2.同步监视器(锁),任何一个对象都可以充当为锁,要求多个线程必须使用同一把锁
//3.在继承Thread方法中,同步监视器可以使用XXXXX.class(类也是对象),因为类只会加载一次所以是同一把锁
//4.在实现Runnable接口方法中,同步监视器可以使用this

方式二:同步方法
如果操作共享数据的代码完整的声明在一个方法中,我们不妨将此方法声明为同步的

public void run(){
	while(true){
		show();
	}
}
private static synchronized void show(){
	//需要被同步的代码
}
//1.同步方法仍然涉及到同步监视器,只是不需要显示声明
//2.非静态的同步方法,同步监视器是:this
//  静态的同步方法,同步监视器是:当前类本身

解决了安全问题,但是操作同步代码时只能有一个线程参与,其他线程等待相当于是一个单线程的过程,效率低

方式三:Lock锁(jdk5.0新增)

private ReentrantLock lock = new ReentranLock();//参数为true则为FIFO形式
@override
public void run(){
	while(true){
		//调用lock
		try{
			lock.lock();
			//需要被同步的代码
		}finally{
			lock.unlock();
		}
	}
}

synchronized与lock的不同:
synchronized执行完相应的代码逻辑后自动的释放同步监视器,而lock需要手动的启动同步,同时结束同步也需要手动的实现

线程通信方式

涉及到的三个方法:

  1. wait():当前进程进入阻塞状态,并释放同步监视器
  2. notify():唤醒wait优先级最高的一个进程
  3. notifyAll():唤醒所有进程

这三个方法必须使用在同步代码块或同步方法中
这三个方法的调用者必须是同步代码块/同步方法的同步监视器
这三个方法是定义在java.lang.Object方法中

应用一:交替输出

public void run(){
	//创建变量
	while(true){
		synchronized(this){
			//唤醒操作
			notify()/notifyAll();
			//判断
			if(xxx > xxx){
				//需要执行的代码
				try{
					wait();
				}catch(InterruptedException e){
					e.printStackTrace();
				}
			}else{
				break;
			}	
		}
	}
}

sleep()和wait()的异同
相同点:一旦执行方法,就可以让当前进程进入阻塞态
不同点:
1)声明的位置不同,sleep声明在Thread类中,wait声明在Object类中
2)调用的范围不同,sleep随时调用,wait必须在同步代码块/同步方法中
3)关于释放释放同步监视器:若两个方法都使用在同步代码块或同步方法中,sleep()不会释放锁,wait()会释放锁

JDK5.0新增线程创建方式

方式三:实现Callable接口

//1.创建一个实现Callable的实现类
class XxxThread implements Callable<Xxx>{
	//2.实现call方法,将此线程需要执行的程序声明在call()中
	@override
	public Xxx call() thorws Exception{
	//代码
	}
	return xxx;//可返回也可不返回
}
public class Xxx{
	//3.创建Callable接口实现类的对象
	XxxThread xxxthread = new XxxThread();
	//4.将此Callable接口实现类的对象作为参数传递到FutureTask构造器中,创建FutureTask对象
	FutureTask<Xxx> futuretask = new FutureTask<Xxx>(xxxthread);
	//5.将FutureTask的对象作为参数传递到Thread的构造器中,创建Thread对象,并调用start()
	Thread thread = new Thread(futuretask);
	thread.start();
	//6.获取Callable中call方法的返回值则使用get(),这是实现类重写的call()的返回值
	try {
            Object sum = futureTask.get();
            System.out.println("call中返回的参数");
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
}

使用Callable接口比Runnable接口强大的原因:
1.call()是可以有返回值的
2.call()可以抛出异常
3.callable支持范型

方式四:实现线程池

class Thread1 implements Runnable{}
class Thread2 implements Runnable{}

public class ThreadPool{
	public static void main(String[] args){
		//1.提供指定线程数量的线程池
		ExecutorService service = Executors.newFixedThreadPool(10);		
		//可以设置线程属性
		ThreadPoolExecutor service1 = (ThreadPoolExecutor) service;
		System.out.println(service.getClass());
        service1.setCorePoolSize(15);
        service1.setKeepAliveTime();
		//2.执行指定的线程的操作。需要提供实现Runnable接口或Callable接口实现类的对象
		service.execute(new Thread1());//适用于Runnable
		service.execute(new Thread2());
		service.submit(Callable callable);//适用于Callable
		//3.关闭线程池
		service.shutdown();
	}
}

好处:
1.提高响应速度(减少了创建新线程的时间)
2.降低资源消耗(重复利用线程池中线程,不需要每次都创建)
3.便于线程管理
corePoolSize:核心池的大小
maximumPoolSize:最大线程数

面试题:创建多线程有几种方式?四种!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值