如果我们用前面学习的知识,要创建100个线程那么就得new
100次
线程的创建是非常耗费资源和耗时的
解决了系统频繁的创建线程的资源消耗
达到了线程对象的重用
线程池可以保障系统的安全
//创建单线程的线程池(借助jdk中给我们提供的工具类Executors)
ExecutorService executorService1 = Executors.newSingleThreadExecutor();
//创建固定大小的线程池
ExecutorService executorService2 = Executors.newFixedThreadPool(10);
//创建弹性伸缩的线程池
ExecutorService executorService3 = Executors.newCachedThreadPool();
package com.uplooking.demo02;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Main {
public static void main(String[] args) {
//创建线程池
ExecutorService executorService = Executors.newFixedThreadPool(8);
//使用线程池对象执行线程
executorService.execute(new Runnable() {
@Override
public void run() {
while (true) {
System.out.println("线程1");
}
}
});
//执行任务(线程池会帮我们创建线程)
executorService.execute(new Runnable() {
@Override
public void run() {
while (true) {
System.out.println("线程2");
}
}
});
//关闭线程池(等待线程池中的线程执行完毕之后才会关闭线程池)
//executorService.shutdown();
//立刻关闭线程池(关闭的是等待状态线程池中的线程)
//executorService.shutdownNow();
}
}
package com.uplooking.demo03;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
public class Main {
public static void main(String[] args) {
//创建延时任务的线程池对象
ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(8);
scheduledExecutorService.schedule(new Runnable() {
@Override
public void run() {
System.out.println("hello up");
}
}, 3000, TimeUnit.MILLISECONDS);
//关闭线程池
scheduledExecutorService.shutdown();
}
}
FixedThreadPool(n)
n到底为多少性能才能发挥到最好?
n为cpu的核数性能最优
不是
对单核的cpu而言,只能并行的执行一个线程,
对于8核的cpu而言,只能最多并行执行8个线程
最多为cpu的核数就可以了
好处:
弊端:
线程安全问题: 多个线程同时(并行)操作同一个数据的时候出现的数据的安全性问题
核心思想: 对多个线程操作(全局)共享变量的代码,只能同时让一个线程访问
给操作全局变量的代码加锁
凡是加锁,效率必然会受影响,我们问题是一定要解决的,我们能做的就是尽量提高效率;
锁对象: 调用当前(run)方法的对象
上面我们加的锁是属于: 悲观锁,而我们在实际的生产环境中,绝对要避免悲观锁的存在
同步方法的锁对象不能改变,只能是调用当前方法的对象
同步代码快的锁对象可以自己任意定义,但是要保证有意义;
上面这个代码是可以正常执行的,;那就说明同一个锁对象可能被同一个线程对象获取了两次;
同一个锁对象被获取两次,那么加锁毫无意义了;
上面的描述经过我刚才的分析发现是错误的;
锁是可以被传递的
在一个锁中调用另外一个锁的代码**,这两个锁是同一个锁**,那么锁是可以被传递的;
一般我们设计的优秀的锁,都要具有传递性
一般我们设计的优秀的锁,都要是可重入锁
相当于手动挡的车:锁的创建,获取和解锁都要自己来干;
好处: 拥有极高的自定义性
弊端: 对编程能力要求高
你中有我,我中有你
锁A中调用锁B,
同时
锁B调用锁A
互相处于永久的等待释放状态
package com.uplooking.demo07;
public class Main {
public static Object lockObjA = new Object();
public static Object lockObjB = new Object();
public static void main(String[] args) {
RunnableA runnableA = new RunnableA();
RunnableB runnableB = new RunnableB();
new Thread(runnableA, "窗口A").start();
new Thread(runnableB, "窗口B").start();
}
}
class RunnableA implements Runnable {
@Override
public void run() {
synchronized (Main.lockObjA) {
System.out.println("lockObjA " + Thread.currentThread().getName());
try {
//避免直接获取到lockB这个锁
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (Main.lockObjB) {
System.out.println("lockObjB ");
}
}
}
}
class RunnableB implements Runnable {
@Override
public void run() {
synchronized (Main.lockObjB) {
System.out.println("lockObjB " + Thread.currentThread().getName());
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (Main.lockObjA) {
System.out.println("lockObjA ");
}
}
}
}
通过sun公司提供的性能分析工具 jconsole
来监测死锁,监测出来之后就可以修改死锁代码;