Java学习历程---线程

1 基本概念:程序、进程、线程

JDK5.0之后 创建线程有 4 种方式
---->程序:为完成特定任务、用某种语言编写的一组指令的集合。静态对象
---->进程:是程序的一次执行过程,动态的
进程作为资源分配的单位,系统在运行时会为每个进程分配不同的内存区域
---->线程:进程可进一步细化为线程,是一个程序内部的一条执行路径
一个程序可以同时并行多个线程,就是多线程的

线程作为调度和执行的单位,每个线程都拥有独立的运行栈和程序计数器(pc)
多个线程要共享一个进程的方法区和堆

---->并行和并发

并行:多个CPU同时执行多个任务
并发:一个CPU(采用时间片)同时处理多个任务

2多线程创建方式一:继承Thread

*1.创建一个继承Thread类的子类
*2.重写Thread类的run()
*3.创建Thread子类的对象
*4.调用start()方法:作用:启动当前线程 / 调用当前线程的run()方法

遍历100以内的所有偶数
---->例如:下面的语句中有两个线程:一个主线程 一个thread线程

public static void main(String[]args){
	Thread1 thread = new Thread1();
	thread.start();
	System.out.println("hello");
}

---->Thread.currentThread().getName() 查看是什么线程
---->不能用 对象.run()方法启动线程 ,那样只是掉了个方法
---->一个线程只能Start一次,如果再次start 就会出现IllegalThreadStateException的异常

---->创建Thread匿名子类的方式,

new Thread1().start();
new Thread(){
	@Override
	public void run(){
		for(int i = 0; i < 100; i++){
			if((i % 2) == 1){
				System.out.println(Thread.currentThread().getName()+":"+i);
			}
		}
	}
}.start();

3 Thread类的常用方法

测试Thread类的常用方法

*1 start()
*2 run()一般需要重写
*3 currentThread()静态方法返回当前代码的线程
*4 getName()
*5 setName(Stringname)设置当前线程的名字
*6 yield()写在run()中,表示释放当前cpu的执行权,有可能在下一刻又抢回来了
*7 join()在一个线程A中调用另外一个线程B的join()方法,A进入阻塞状态执行另一个线程B,直到B执行完成,A才结束阻塞
*8 sleep(longmillitime)

4 线程优先级的设计

*线程的优先级

1.MAX_PRIORITY:10
*MIN_PRIORITY:1
*NORM_PRIORITY:5

*2.如何获取和设置当前线程的优先级:

*获取:Thread.currentThread().getPriority()
*设置:对象.setPriority(Thread.MAX_PRIORITY)
*说明:高优先级的线程要抢占低优先级的线程cpu的执行权,但是从概率上讲,高优先级线程高概率的情况下被执行,
*并不意味着只有搞优先级的线程执行后,低优先级才执行

5 实现 Runnable接口

*实现Runnable接口

*1创建一个实现了Runnable接口的类
*2实现该接口中的run()抽象方法
*3创建该类的实例对象
*4将此对象作为参数,传递到Thread类的构造器中,创建Thread类的对象
*5调用thread对象的start()方法:启动线程 / 调用当前线程的run()方法

Mthread mThread = new MThread();
Thread thread = newThread(mThread);
//创建第二个 线程
Thread thread2 = newThread(mThread);

说明:这个调用的run()方法 其实就是调用的 Runnable 接口的方法
底层:Thread构造器 public Thread(Runnable target){ }

6 两种方式的对比

实际应用中 实现的方式好一点,
实现的方式没有类的但继承性的局限
实现的方更适合有共享数据的
---->Thread类中也实现了Runnable接口

7 线程的声明周期

阻塞不是一个线程的最终状态!!!!
Thread.State / Thread中的内部类(枚举类) State

8 线程的安全问题

买票中出现重票
---->线程安全问题出现的原因:多个线程操作共享数据
当某个线程操作车票的过程中,还没有操作完,其他线程参与进来
---->如何解决:当一个线程操作共享数据时,,其他线程不能参与进来,即使这个线程进入了阻塞状态
---->通过同步机制。

---->同步的方式,解决了线程的安全问题 ----好处
操作同步代码时,只能有一个线程参与 ,其他线程等待,相当于一个单线程的过程,效率低
*线程同步问题:concurrent
*
方式一、同步代码块

*//实现的方式
*private Object obj=new Object()
*synchronized(同步监视器/obj){
*//需要被同步的代码,即操作共享数据的代码
*}
*说明:1.同步监视器,俗称:锁。任何一个类的对象都可以成为锁
*要求:多个线程必须共用同一把锁!!!!
*注意实现Runnable接口的方式可以用this,但是继承Thread的方式要慎用this
*//继承的方式
*private static Objectobj=newObject()!!!!!!!!!!1
*synchronized(同步监视器/obj/或者用类.class){
**//需要被同步的代码,即操作共享数据的代码
**}
*说明:类.class只会加载一次

方式二、同步方法
*如果操作共享数据的代码完整的声明在一个方法总,可以把方法声明为同步的
实现的方式Runnable

private synchronized void show(){//同步监视器this
	if(tickets>0){
		try{
			Thread.sleep(100);
		}catch(InterruptedExceptione){
			e.printStackTrace();
		}
		System.out.println(Thread.currentThread().getName()+"正在卖第"+tickets+"张票");
		tickets--;
	}
}

继承的方式

private static synchronized void show(){//同步监视器当前的类.class
	if(tickets>0){
		try{
			Thread.sleep(100);
		}catch(InterruptedExceptione){
			e.printStackTrace();
		}
		System.out.println(Thread.currentThread().getName()+"正在卖第"+tickets+"张票");
		tickets--;
	}
}

总结:同步方法仍然涉及到同步监视器,
非静态的锁是this
静态的锁是当前类.class

例题,单例模式,懒汉式(线程安全版)

class Bank(){
	private Bank(){
	}
	private static Bank bank = null;
	public static synchronized Bank getBank(){   // 同步监视器:bank.class // 方式二
		synchronized(Bank.class){    //方式一:效率较差
			if(bank == null){            //这个地方会造成线程安全问题
				bank = new Bank();
			}
		return bank;
		}
		//方式三,效率较高
		if(bank == null){
			synchronized(Bank.class){    
				if(bank == null){            //这个地方会造成线程安全问题
				bank = new Bank();
				}
			return bank;
		}
 
	}
}
 

9 死锁的问题

不同的线程 分别占用对方需要的同步资源不放弃,都在等待对方放弃,这时候,不会出现异常,不出现提示,所有线程都处于阻塞状态
----> 要避免死锁

10 Lock锁方式解决线程安全问题

JDK5.0
解决线程安全问题的方式三:Lock锁方式JDK5.0新增
*1.面试题synchronized/Lock的异同

同:都可以解决线程安全问题
异:synchronized机制在执行完相应的同步代码以后,自动的释放同步监视器
Lock需要手动的启动同步(lock()),同时结束同步也需要手动的实现(unlock())
Lock是显示锁,synchronized是隐式锁
Lock只有代码块锁,synchronized有代码块锁和方法锁
使用Lock锁,JVM将花费较少的时间调度线程,性能更好

2.优先使用顺序:
Lock->同步代码块->同步方法

11 线程的通信

线程的通信:涉及到三个方法

wait():一旦执行此方法,当前线程进入阻塞状态,并释放同步锁
notify():一旦执行此方法,就会唤醒wait的一个线程,如果有多个线程被wait,就唤醒优先级搞得
notifyAll():所有wait的线程都被唤醒

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

12 sleep()和wait()异同?

---->相同点:

一旦执行,都使得当前线程进入阻塞状态

---->不同点:

(1)声明的位置不同,sleep声明在Thread中,wait声明在Object中,
(2)调用要求不同,sleep可以在任何需要的地方调用,wait只能在同步代码块或者同步方法中调用
(3)关于是否释放同步监视器:如果两个方法都用在同步代码块或者同步方法中,sleep不会释放锁,wait会

13 生产者/消费者问题:

public class ProductTest{
	public static void main(String[] args){
		Product produnct = new Product(5);
		Producer pro = new Producer(produnct);
		pro.setName("生产者");
 
		Consumer con = new Consumer(produnct);
		con.setName("消费者");
		pro.start();
		con.start();
	}
}
 
classProduct{
	privateint num;
	publicProduct(int num){
		this.num=num;
	}
	//要考虑线程同步问题用同步方法 同步监视器是this
	public synchronized void produce Product(){//生产者生产产品
	//如果超过20个就停止上产
		if(num<20){
			try{
				Thread.sleep(100);
			}catch(InterruptedExceptione){
				e.printStackTrace();
			}
 
			num++;
			System.out.println(Thread.currentThread().getName()+":开始生产第"+num+"个产品");
			//生产者生产了一个产品就可以唤醒消费者线程了
			notify();
		}else{
			//否则就等待
			try{
				wait();
			}catch(InterruptedExceptione){
				e.printStackTrace();
			}
		}
	}
	//消费者消费产品
	public synchronized void consumeProduct(){
		//如果有就开始消费
		if(num>0){
			try{
				Thread.sleep(1000);
			}catch(InterruptedExceptione){
				e.printStackTrace();
			}
			System.out.println(Thread.currentThread().getName()+":开始消费第"+num+"个产品");
			num--;
			//消费者消费了一个产品就可以唤醒生产者线程了
			notify();
		}else{
			//否则就等待
			try{
				wait();
			}catch(InterruptedExceptione){
				e.printStackTrace();
			}
		}
	}
}
 
//生产者
class Producer extends Thread{
	private Product product;
	public Producer(Product product){
		this.product = product;
	}
	@Override
	public void run(){
		System.out.println("生产者生成中。。。");
		while(true){	
			product.produceProduct();
		}
	}
}
//消费者
class Consumer extends Thread{
	private Product product;
	public Consumer(Product product){
		this.product = product;
	}
	@Override
	public void run(){
		System.out.println("消费者消费中。。。");
		while(true){
			product.consumeProduct();
		}
	}
}

14 创建多线程的方式三:实现Callable接口

重写call()方法
创建多线程的方式三:实现callable接口

//1.实现callable接口重写call方法
class Order implements Callable
//2.实例化
Order order = new Order();
//3.借用FutureTask类
FutureTask futureTask = new FutureTask(order);
futureTask.get();
//get()方法返回值即FutureTask构造器参数Callable实现类重写的call()方法的返回值
//4.
newThread(futureTask).start();

如何理解Callable接口方式创建线程比Runnable接口方式强大?

call()方法有返回值
call()方法可以抛出异常,被外面的操作捕获
Callable是支持泛型的

15 方式四:使用线程池

创建线程池的方法
1.提供指定线程数量的线程池
ExecutorService executorService = Executors.newFixedThreadPool(10);
2.执行指定的线程的操作,提供一个实现Runnable接口或者实现Callable接口的对象
executorService.execute(newNumberThread());//适用于Runnable
//executorService.submit(newCallable());//适合使用Callable
3.关闭
executorService.shutdown();
好处:

1.提高响应速度(减少了创建新线程的时间)
2.降低资源消耗
3.便于线程管理
corePoolSize:核心池的大小
maximumPoolSize:最大线程数
keepAliveTime:线程没有任务时最多保持多长时间会终止

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值