Java基础练习 — 多线程2和线程池

一、回顾多线程内容

1.1 线程的实现方式(3种):继承Thread、实现Runnable、匿名内部类

  1. 创建自定义类new 自定义类(),继承Thread类,重写run()方法
package day0709;

public class MyEx {
	public static void main(String[] args) {
		TestThread tt = new TestThread() ;
		tt.start();
	}
}

class TestThread extends Thread{//自定义类
	public void run() {
		System.out.println("方式一:自定义线程类继承Thread类,并重写run()方法");
	}
}
  1. 创建自定义类new 自定义类(),实现Runnable接口,重写run(), new Thread(Runnable对象)
package day0709;

public class MyEx2 {
	public static void main(String[] args) {
		Thread t = new Thread(new TestThread2());
		t.start();
	}
}

class TestThread2 implements Runnable{

	@Override
	public void run() {
		System.out.println("方式二:自定义类实现Runnable接口,重写run(),在Thread中传入Runnable对象");
	}
	
}
  1. 结合方式一和二,创建匿名内部类
package day0709;

public class MyEx3 {
	public static void main(String[] args) {
		//方式一的匿名内部类
		Thread t1 = new Thread() {
			public void run() {
				System.out.println("方式一的匿名内部类");
			}
		};
		
		//方式二的匿名内部类
		Thread t2 = new Thread(new Runnable() {

			@Override
			public void run() {
				System.out.print("方式二的匿名内部类");
			}
			
		});
		
		t1.start();
		t2.start();
	}
}

1.2 常用方法

//开启线程
start()
//获得当前运行的线程
Thread.currentThread()
//设置线程名字
setName()
//获得线程名字
getName()
//获得线程ID
getId()
//获得线程优先级
getPriority()
//设置线程优先级
setPriority(1~10)
//判断线程是否为守护线程
isDaemon()
//设置线程为守护线程
setDaemon(true)
//归还CPU时间片
Thread.yield()

二、多线程2

2.1 线程同步、异步概念

  1. 线程同步:排队执行
  2. 线程异步:多个线程使用同一个资源,抢占资源

2.2 常用方法(join())

  1. void join():等待线程死亡
package day0709;

public class ThreadDemo02 {
	public static void main(String[] args) {
		Thread t1 = new Thread(new Runnable() {
			@Override
			public void run() {
				for(int i = 0; i <= 100; i++) {
					System.out.println(i+"%");
					try {
						Thread.sleep(50);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
				System.out.println("加载完成!");
			}
		});
		
		Thread t2 = new Thread(new Runnable() {

			@Override
			public void run() {
				System.out.println("等待加载");
				
				//等待第一个线程执行完,再继续当前t2线程
				/*while(t1.isAlive()) {
					Thread.yield();
				}*/
				//效率太低
				
				try {
					t1.join();//等待第一个线程死亡(执行完)
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				System.out.println("显示完成!");
				
			}
			
		});
		
		t1.start();
		//join() 不能放这里,这里表示必须等待t1全部结束
		/*try {
			t1.join();
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}*/
		
		t2.start();
	}
}

2.3 多线程可能会出现的问题——线程异步

  1. 案例:两个人,共用同一个桌子 Table,桌上有20个豆子 bean = 20,2个人从桌子上拿豆子。用多线程实现。
package day0709;
/*
 *  两个人,共用同一个桌子 Table
 *  桌上有20个豆子 bean = 20
 *  2个人从桌子上拿豆子
 */
public class ThreadDemo03 {
	public static void main(String[] args) {
		Table ta = new Table(20);
		
		
		
		Thread t1 = new Thread(new Runnable() {
			int num = 0;
			@Override
			public void run() {
				
				while(ta.bean != 0) {//可能会出现直接越过临界点的现象,导致程序出现问题
					ta.getBean();
					num ++;
					System.out.println(Thread.currentThread().getName()+ ":" + num);
				}
				
			}
			
		});
		
		
		Thread t2 = new Thread(new Runnable() {
			int num2 = 0;
			@Override
			public void run() {
				while(ta.bean != 0) {
					ta.getBean();
					num2 ++;
					System.out.println(Thread.currentThread().getName() + ":" + num2);
				}
				
			}
			
		});
		
		t1.start();
		t2.start();
		try {
			t1.join();
			t2.join();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		System.out.println(ta.bean);
	}
}

2.4 synchronized——锁 (wait()、notify()、notifyAll())

2.4.1 锁的特点

  1. 锁的范围越小越好,尽量不要锁整个方法,
  2. 锁在静态方法上,锁类(class) - 千万别用
  3. 可以锁方法,锁代码块 -> 一个对象有一个锁,并且锁只能被一个线程获得
  4. 锁代码块是,通常使用的对象是this
  5. 锁的对象:共享的对象

2.4.2 锁的使用

  1. 线程的等待与唤醒,必须得使用线程所共享的锁,即每个线程都要有相同的锁对象
  2. 给方法上锁,是为保证线程运行该方法时,不被其他线程打断,线程中未上锁的其他代码方法仍会被打断
package day0709;

public class ThreadDemo05 {
	public static void main(String[] args) {
		
		Object o = new Object();
		Thread t1 = new Thread(new Runnable() {

			@Override
			public void run() {
//				synchronized(o) { //锁住方法,使线程运行该方法时,不被其他线程打断
					System.out.println("1——开始加载图片...");
					for(int i = 0; i <= 100; i++) {
						System.out.println(i+"%");
					}
					System.out.println("2——加载图片完成...");
//				}
				
				synchronized(o) { //使用线程等待和唤醒方法,必须得使用锁对象,保证等待和唤醒之间的关系
					o.notify();
//					o.notifyAll();
				}
				
				System.out.println("5——开始下载图片...");
				for(int i = 0; i <= 100; i++) {
					System.out.println(i + "%");
				}
				System.out.println("6——图片下载完成...");
			}
		
		});
		
		Thread t2 = new Thread(new Runnable() {

			@Override
			public void run() {
				System.out.println("3——开始显示图片...");
				synchronized(o) {//锁必须共享,每个线程都要有锁才行
					try {
						o.wait();
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
					
				}
				System.out.println("4——显示图片完成...");
			}
		});
		
		t1.start();
		t2.start();
	}
}

2.4.3 给集合上锁——Collections.synchronizedList(list);

package day0709;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class ThreadDemo06 {
	public static void main(String[] args) {
		List<Integer> list = new ArrayList<Integer>();
		List<Integer> sList = Collections.synchronizedList(list);//给集合上锁
		
		Thread t1 = new Thread(new Runnable() {

			@Override
			public void run() {
				for(int i = 0; i < 10; i++) {
					sList.add(i);
				}
			}
			
		});
		
		Thread t2 = new Thread(new Runnable() {

			@Override
			public void run() {
				for(int i = 0; i < 10; i++) {
					sList.add(i);
				}
			}
			
		});
		
		t1.start();
		t2.start();
		
		try {
			t1.join();
			t2.join();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		System.out.println(list.size());
	}
}

三、线程池

3.1 线程池特点

  1. 线程对象 ExecutorService
  2. 不再手动创建单线程,即new Thread()
  3. 好处:实现线程对象的重复使用,提高效率

3.2 创建线程池的方式(工具类:Executors)

  1. 创建一个可重用固定线程数的线程池:static ExecutorService newFixedThreadPool(int nThreads)
  2. 创建一个可根据需要创建新线程的线程池:static ExecutorService newCachedThreadPool()
  3. 创建一个使用单个 worker 线程的 Executorstatic ExecutorService newSingleThreadExecutor()
  4. 创建一个线程池,它可安排在给定延迟后运行命令或者定期地执行:static ScheduledExecutorService newScheduledThreadPool(int corePoolSize)

代码实现:

package day0709;

import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;

public class ThreadDemo07 {
	public static void main(String[] args) {
		//创建出来的线程池中有3个线程对象,都标记为空闲状态
		ExecutorService pool = Executors .newFixedThreadPool(3);//线程中指定有3个线程执行10个任务
		ExecutorService pool2 = Executors.newCachedThreadPool();//线程根据任务数量生成10个线程
		ExecutorService pool3 = Executors.newSingleThreadExecutor();//线程池中只有1个线程,去执行10个任务
		ScheduledExecutorService pool4 = Executors.newScheduledThreadPool(3);//线程中指定有3个线程,可定时执行
		//多任务只有1个,而线程池中有3个空闲线程,则将任务交给线程池,随机指派一个线程对象来执行
		
		
		for(int i = 0; i < 10; i++) {
			pool.execute(new Runnable() {

				@Override
				public void run() {
					for(int i = 0; i < 10; i++) {
						System.out.println(Thread.currentThread().getName() + " : " + i);
					}
				}
				
			});
		}
		
		//任务执行结束,将线程对象规划给线程池,并且重新标记为空闲状态
		//shutdown() 不执行,线程池还在等待任务,控制台一直显示运行状态
		pool.shutdown();
	}
}

3.3 ExecutorService(接口)——Executors工具类的返回类型

3.3.1 常用方法

  1. 运行线程池:execute()
  2. 关闭线程池:shutdown()
  3. 提交一个返回值的任务用于执行,返回一个表示任务的未决结果的 Future:<T> Future<T> submit(Callable<T> task)
  4. 提交一个 Runnable 任务用于执行,并返回一个表示该任务的 Future:Future<?> submit(Runnable task)

四、线程的生命周期(完整)

在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值