一、实现多线程
多线程是为了同步完成多项任务,提高资源使用率提高系统使用率。
1、继承Thread类
void run() |
在线程开启后,此方法将被调用执行,run()封装了被线程执行的代码 |
void start() |
启动线程,Java虚拟机会调用run方法()、即由JVM调用此线程的run()方法 |
public class MultiThreaded extends Thread{
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println("继承thread " + i);
}
}
public static void main(String[] args) {
MultiThreaded multiThreaded = new MultiThreaded();
MultiThreaded multiThreaded1 = new MultiThreaded();
multiThreaded.start();
multiThreaded1.start();
}
}
2、实现Runnable接口
Thread(Runnable target) |
分配一个新的Thread对象 |
Thread(Runnable target, String name) |
分配一个新的Thread对象 |
public class RunnableThread implements Runnable {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName() + i);
}
}
public static void main(String[] args) {
RunnableThread run = new RunnableThread();
//创建Thread类的对象,把RunnableThread对象作为构造方法的参数
Thread is = new Thread(run, "is");
Thread lunatic = new Thread(run, "lunatic");
//启动线程
is.start();
lunatic.start();
}
}
3、实现Callable接口
V call() |
计算结果,如果无法计算结果,则抛出一个异常 |
FutureTask(Callable callable) |
创建一个 FutureTask,一旦运行就执行给定的 Callable |
V get() |
如有必要,等待计算完成,然后获取其结果 |
public class CallableThread implements Callable {
@Override
public Integer call() throws Exception {
int sum = 0;
for (int i = 0; i < 100; i++) {
sum += i;
}
//返回值就表示线程运行完毕之后的结果
return sum;
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
//线程开启之后需要执行里面的call方法
CallableThread call = new CallableThread();
//可以获取线程执行完毕之后的结果.也可以作为参数传递给Thread对象
FutureTask task = new FutureTask<>(call);
//创建线程对象
Thread thread = new Thread(task);
//开启线程
thread.start();
Integer str = task.get();
System.out.println("str = " + str);
}
}
4、对比
实现 |
好处 |
坏处 |
实现Runnable、Callable接口 |
扩展性强,实现该接口的同时还可以继承其他的类 |
编程相对复杂,不能直接使用Thread类中的方法 |
继承Thread |
编程比较简单,可以直接使用Thread类中的方法 |
可以扩展性较差,不能再继承其他的类 |
5、线程调度
分时调度模型 |
所有线程轮流使用 CPU 的使用权,平均分配每个线程占用 CPU 的时间片 |
抢占式调度模型 |
优先让优先级高的线程使用 CPU,如果线程的优先级相同,那么会随机选择一个,优先级高的线程获取的 CPU 时间片相对多一些 |
线程优先级:默认优先级是5;设置优先级在1~10之间。
6、线程同步
同步代码块
public class RunnableThread implements Runnable {
private int tickets = 100;
private Object obj = new Object();
@Override
public void run() {
while (true) {
synchronized (obj) { // 对可能有安全问题的代码加锁,多个线程必须使用同一把锁
//t1进来后,就会把这段代码给锁起来
if (tickets > 0) {
try {
Thread.sleep(100);
//t1休息100毫秒
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "正在出售第" + tickets + "张票");
tickets--; //tickets = 99;
}
}
//t1出来了,这段代码的锁就被释放了
}
}
public static void main(String[] args) {
RunnableThread run = new RunnableThread();
//创建Thread类的对象,把RunnableThread对象作为构造方法的参数
Thread is = new Thread(run, "is");
Thread lunatic = new Thread(run, "lunatic");
//启动线程
is.start();
lunatic.start();
}
}
好处 |
解决了多线程数据安全问题 |
坏处 |
当线程有多个,每个线程都会去判断同步上的锁,造成资源耗费严重,降低程序运行效率 |
同步方法
public class TbRunnable implements Runnable{
private static int ticketCount = 100;
@Override
public void run() {
while(true){
if("is".equals(Thread.currentThread().getName())){
//同步方法
boolean result = synchronizedMethod();
if(result){
break;
}
}
if("lunatic".equals(Thread.currentThread().getName())){
//同步代码块
synchronized (TbRunnable.class){
if(ticketCount == 0){
break;
}else{
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
ticketCount--;
System.out.println(Thread.currentThread().getName() + "在卖票,还剩下" + ticketCount + "张票");
}
}
}
}
}
private static synchronized boolean synchronizedMethod() {
if(ticketCount == 0){
return true;
}else{
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
ticketCount--;
System.out.println(Thread.currentThread().getName() + "在卖票,还剩下" + ticketCount + "张票");
return false;
}
}
public static void main(String[] args) {
TbRunnable tb = new TbRunnable();
Thread t1 = new Thread(tb);
Thread t2 = new Thread(tb);
t1.setName("is");
t1.setName("lunatic");
t1.start();
t2.start();
}
}
Lock锁:unlock(释放锁)和lock(获得锁)
public class Ticket implements Runnable {
//票的数量
private int ticket = 100;
private Object obj = new Object();
private ReentrantLock lock = new ReentrantLock();
@Override
public void run() {
while (true) {
//synchronized (obj){//多个线程必须使用同一把锁.
try {
lock.lock();
if (ticket <= 0) {
//卖完了
break;
} else {
Thread.sleep(100);
ticket--;
System.out.println(Thread.currentThread().getName() + "在卖票,还剩下" + ticket + "张票");
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
// }
}
}
public static void main(String[] args) {
Ticket tk = new Ticket();
Thread t1 = new Thread(tk);
Thread t2 = new Thread(tk);
Thread t3 = new Thread(tk);
t1.setName("ttt1");
t2.setName("ttt2");
t3.setName("ttt3");
t1.start();
t2.start();
t3.start();
}
}
8、线程池
存放线程的容器
Executors线程池
public class MyThreadPoolDemo {
public static void main(String[] args) throws InterruptedException {
//1,创建一个默认的线程池对象.池子中默认是空的.默认最多可以容纳int类型的最大值.
ExecutorService executorService = Executors.newCachedThreadPool();
//Executors --- 可以帮助我们创建线程池对象
//ExecutorService --- 可以帮助我们控制线程池
executorService.submit(()->{
System.out.println(Thread.currentThread().getName() + "在执行了");
});
Thread.sleep(2000);
executorService.submit(()->{
System.out.println(Thread.currentThread().getName() + "在执行了");
});
executorService.shutdown();
}
}
ThreadPoolExecutor线程池
corePoolSize |
核心线程的最大值,不能小于0 |
maximumPoolSize |
最大线程数,不能小于等于0,maximumPoolSize >= corePoolSize |
keepAliveTime |
空闲线程最大存活时间,不能小于0 |
unit |
时间单位 |
workQueue |
任务队列,不能为null |
threadFactory |
创建线程工厂,不能为null |
handler |
任务的拒绝策略,不能为null |
public class MyThreadPoolDemo3 {
// 参数一:核心线程数量
// 参数二:最大线程数
// 参数三:空闲线程最大存活时间
// 参数四:时间单位
// 参数五:任务队列
// 参数六:创建线程工厂
// 参数七:任务的拒绝策略
public static void main(String[] args) {
ThreadPoolExecutor pool = new ThreadPoolExecutor(2,5,2, TimeUnit.SECONDS,new ArrayBlockingQueue<>(10), Executors.defaultThreadFactory(),new ThreadPoolExecutor.AbortPolicy());
pool.submit(new RunnableThread());
pool.submit(new RunnableThread());
pool.shutdown();
}
}