## 补充内容:
1.虚拟机线程管理的接口,获取所有线程id和线程名
//虚拟机线程管理的接口
ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean();
ThreadInfo[] threadInfos =
threadMXBean.dumpAllThreads(false, false);
for(ThreadInfo threadInfo:threadInfos) {
System.out.println("["+threadInfo.getThreadId()+"]"+" "+threadInfo.getThreadName());
}
1.CPU核心数和线程数的关系
核数和线程正常比例是:1:1关系。
cup超线程技术->核数和线程数比例:是1:2关系
2.CPU时间片轮转机制
线程和线程之间cpu需要切换执行一种机制
3.什么是进程和线程
进程:是程序的最小单位,一个进程有多个线程,进程和进程之间的独立的。
线程:是CPU调度的最小单位,多个线程是共享进程的资源的。
4.澄清并行和并发(案例理解:8个窗口同时打饭,每人打饭需要化30秒,一分钟,并发为16,并行为8)
并行:同一个时间点,可以执行多少线程。
并发:在一定的时间单位内,可以执行多少个线程。
5.高并发编程的意义、好处和注意事项
1.并发好处:
在一段时间内,充分内用cpu资源,提供效率。
2.坏处:
1.线程过多处理不当,导致线程死锁。
2.线程过多,内存溢出,容易宕机。
1.java线程的3种启动方式,3种有什么区别和实现具体案例。
2.怎么样才能让Java里的线程安全停止工作呢。
2.1 interrupt(), isInterrupted(), static方法interrupted():主要作用。
2.2 线程中断(interrupt())具体案例说明。
3.守护线程
4.对象锁和类锁
5.volatile关键字
6.ThreadLocal的使用
1.wait/notify,notifyAll
2.join()->交出执行权,让加入线程先执行。
3.yield()->持有的锁是不释放的
4.sleep()->持有的锁是不释放的
5.wait()->调用方法之前,必须持有锁。方法执行会释放锁。当wait方法返回的时候会重新持有锁。
6.notify()->调用方法之前,必须持有锁。调用notify不会释放锁。
1.CPU核心数和线程数的关系
核心数:线程数=1:1 ;使用了超线程技术后—> 1:2
2.CPU时间片轮转机制
又称RR调度,会导致上下文切换
3.什么是进程和线程
进程:程序运行资源分配的最小单位,进程内部有多个线程,会共享这个进程的资源
线程:CPU调度的最小单位,必须依赖进程而存在。
4.澄清并行和并发
并行:同一时刻,可以同时处理事情的能力
并发:与单位时间相关,在单位时间内可以处理事情的能力
5.高并发编程的意义、好处和注意事项
好处:充分利用cpu的资源、加快用户响应的时间,程序模块化,异步化
问题:
线程共享资源,存在冲突;
容易导致死锁;
启用太多的线程,就搞垮机器的可能
1.java线程的3种启动方式,3种有什么区别和实现具体案例。
1.类Thread,接口Runnable,接口Callable3种方式。
2.Thread类单继承的,不方便扩展。Runnable是多继承的,方便扩展。Callable执行线程有返回值。
3.具体实现方式:
1.创建线程类
/*扩展自Thread类*/
private static class MyThread extends Thread{
@Override
public void run() {
System.out.println("I am implements MyThread");
}
}
/*实现Runnable接口*/
private static class UseRun implements Runnable{
@Override
public void run() {
System.out.println("I am implements Runnable");
}
}
/*实现Callable接口,允许返回值*/
private static class UseCall implements Callable<String>{
@Override
public String call() throws Exception {
System.out.println("I am implements Callable");
return "CallResult";
}
}
2.调用创建线程类
MyThread mt = new MyThread();
mt.start();
UseRun useRun = new UseRun();
new Thread(useRun).start();
Thread t = new Thread(useRun);
t.interrupt();
UseCall useCall = new UseCall();
FutureTask<String> futureTask = new FutureTask<>(useCall);
new Thread(futureTask).start();
System.out.println(futureTask.get()); //get方法是阻塞的
2.怎么样才能让Java里的线程安全停止工作呢。
//1. 线程自然终止。自然执行完或抛出未处理异常。
//2. stop(),resume(),suspend()已不建议使用,
stop():会导致线程不会正确释放资源。
resume():
suspend():拿到资源后不释放,容易导致死锁。
//3. interrupt(), isInterrupted(),static方法interrupted(),建议使用的方法
interrupt():
调用一个线程的interrupt() 方法中断一个线程,并不是强行关闭这个线程,只是跟这个线程打个招呼,将线程的中断标志位置为true,线程是否中断,由线程本身决定。
isInterrupted():
判定当前线程是否处于中断状态。
static方法interrupted():
判定当前线程是否处于中断状态,同时中断标志位改为false。
//4. 线程中断(interrupt())具体案例说明
// 4.1 Thread中断
public class EndThread {
private static class UseThread extends Thread{
public UseThread(String name) { super(name); }
@Override
public void run() {
String threadName = Thread.currentThread().getName();
while(!isInterrupted()) { //2.判断是否已经中断
System.out.println(threadName+" is run!");
}
//3.打印中断表示,false:没设置中断 true:已设置中断
System.out.println(threadName+" interrput flag is "+isInterrupted());
}
}
public static void main(String[] args) throws InterruptedException {
Thread endThread = new UseThread("endThread");
endThread.start();
Thread.sleep(1);
endThread.interrupt(); //1.设置中断标准为true
}
}
//4.2 Ruabble中断
public class EndRunnable {
private static class UseRunnable implements Runnable{
@Override
public void run() {
String threadName = Thread.currentThread().getName();
while(Thread.currentThread().isInterrupted()) { //Thread.currentThread().isInterrupted()来判断是否设置的中断
System.out.println(threadName+" is run!");
}
System.out.println(threadName+" interrput flag is "+Thread.currentThread().isInterrupted());
}
}
public static void main(String[] args) throws InterruptedException {
UseRunnable useRunnable = new UseRunnable();
Thread endThread = new Thread(useRunnable,"endThread");
endThread.start();
Thread.sleep(20);
endThread.interrupt();
}
}
// 4.3 抛出InterruptedException异常中断处理
public class HasInterrputException {
private static class UseThread extends Thread{
public UseThread(String name) {super(name);}
@Override
public void run() {
while(!isInterrupted()) {
try {
Thread.sleep(3000); //2.抛出InterruptedException异常
} catch (InterruptedException e) {
interrupt(); //3.catch语句块里再次调用interrupt()设置中断,这样才能中断成功。
}
}
}
}
public static void main(String[] args) {
Thread endThread = new UseThread("HasInterrputEx");
endThread.start();
endThread.interrupt(); //1.中断线程,如果抛出InterruptedException异常,线程的中断标志位会被复位成false(没中断设置)
}
}
3.守护线程
//3.1守护线程概率和使用注意
概念:和主线程共死,finally不能保证一定执行
使用注意案例:
public class DaemonThread {
private static class UseThread extends Thread {
@Override
public void run() {
try {
while (!isInterrupted()) {
System.out.println(Thread.currentThread().getName()+ " I am extends Thread.");
}
System.out.println(Thread.currentThread().getName() + " interrupt flag is " + isInterrupted());
} finally {
System.out.println("...........finally");
}
}
}
public static void main(String[] args) throws InterruptedException, ExecutionException {
UseThread useThread = new UseThread();
useThread.setDaemon(true); //守护线程,与主线程共死,finally语句块不能保证一定执行
useThread.start();
Thread.sleep(5);
}
}
4.对象锁和类锁
对象锁,锁的是类的对象实例。
类锁 ,锁的是每个类的的Class对象,每个类的的Class对象在一个虚拟机中只有一个,所以类锁也只有一个。
5.volatile关键字
保证线程的可见性,但破坏的线程的原子性,是线程不安全的。
适合于只一个线程写,多个线程读的场景,因为它只能确保可见性。
6.ThreadLocal的使用
线程变量。 可以理解为是个map,类型 Map
使用案例:
public class UseThreadLocal {
//1. ThreadLocal可以理解为 一个map,类型 Map
static ThreadLocal<Integer> threadLaocl = new ThreadLocal<Integer>(){
@Override
protected Integer initialValue() { return 1;}
};
public void StartThreadArray(){
Thread[] runs = new Thread[3];
for(int i=0;i<runs.length;i++){ runs[i]=new Thread(new TestThread(i)); }
for(int i=0;i<runs.length;i++){ runs[i].start(); }
}
/**
*类说明:测试线程,线程的工作是将ThreadLocal变量的值变化,并写回,看看线程之间是否会互相影响
*/
public static class TestThread implements Runnable{
int id;
public TestThread(int id){ this.id = id; }
public void run() {
System.out.println(Thread.currentThread().getName()+":start");
Integer s = threadLaocl.get(); //2.获得变量的值
s = s+id; //0+1,1+1,2+1
threadLaocl.set(s); //3.设置变量的值
System.out.println(Thread.currentThread().getName()+":"+threadLaocl.get());
//threadLaocl.remove(); 这里只是加快的gc的垃圾回收
}
}
public static void main(String[] args){
UseThreadLocal test = new UseThreadLocal();
test.StartThreadArray();
}
}
1.wait/notify,notifyAll
(1)注意:应该尽量使用notifyAll,使用notify因为有可能发生信号丢失的的情况
(2)案例1:快递公里和实时地点等待通知
//1.定义快递处理公里和地点等待通知处理类
public class Express {
public final static String CITY = "ShangHai";
private int km;/*快递运输里程数*/
private String site;/*快递到达地点*/
public Express() { }
public Express(int km, String site) {
this.km = km;
this.site = site;
}
/****************************************************************************/
/* 变化公里数,然后通知处于wait状态并需要处理公里数的线程进行业务处理*/
public synchronized void changeKm(){
this.km = 101;
notifyAll();
}
//公里数等待
public synchronized void waitKm(){
while(this.km<=100) {
try {
wait();
System.out.println(Thread.currentThread().getId()+":快递公里没变化,在等待中。。。。");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getId()+":快递已到"+this.km+",打电话通知用户拿取");
}
/****************************************************************************/
/****************************************************************************/
/* 变化地点,然后通知处于wait状态并需要处理地点的线程进行业务处理*/
public synchronized void changeSite(){
this.site = "BeiJing";
notifyAll();
}
//地点数等待
public synchronized void waitSite(){
while(CITY.equals(this.site)) {
try {
wait();
System.out.println(Thread.currentThread().getId()+":快递地点没变化,在等待中。。。。");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getId()+":快递已到"+this.site+",打电话通知用户拿取");
}
/****************************************************************************/
}
//2.模拟快递公里数据和地点放生变化,等待或通知业务调用
public class TestWN {
private static Express express = new Express(0,Express.CITY);
/*检查里程数变化的线程,不满足条件,线程一直等待*/
private static class CheckKm extends Thread{
@Override
public void run() {
express.waitKm();
}
}
/*检查地点变化的线程,不满足条件,线程一直等待*/
private static class CheckSite extends Thread{
@Override
public void run() {
express.waitSite();
}
}
public static void main(String[] args) throws InterruptedException {
//地点数个线程
for(int i=0;i<3;i++){ new CheckSite().start();}
//里程数的变化个线程
for(int i=0;i<3;i++){ new CheckKm().start();}
Thread.sleep(1000);
express.changeKm();
}
}
(3)案例2:模拟数据库线程池获取和释放
//1.实现一个数据库的连接池
public class DBPool {
//数据库池的容器
private static LinkedList<Connection> pool = new LinkedList<>();
public DBPool(int initalSize) {
if(initalSize>0) {
for(int i=0;i<initalSize;i++) {pool.addLast(SqlConnectImpl.fetchConnection());}
}
}
//在mills时间内还拿不到数据库连接,返回一个null
public Connection fetchConn(long mills) throws InterruptedException {
synchronized (pool) {
if (mills<0) {
while(pool.isEmpty()) {
pool.wait();
}
return pool.removeFirst();
}else {
long overtime = System.currentTimeMillis()+mills;
long remain = mills;
while(pool.isEmpty()&&remain>0) { // 空 且 等待时间大于0
pool.wait(remain);
remain = overtime - System.currentTimeMillis();
}
Connection result = null;
if(!pool.isEmpty()) {
result = pool.removeFirst();
}
return result;
}
}
}
//放回数据库连接
public void releaseConn(Connection conn) {
if(conn!=null) {
synchronized (pool) {
pool.addLast(conn);
pool.notifyAll();
}
}
}
}
//2.测试线程获取和释放
public class DBPoolTest {
static DBPool pool = new DBPool(10);
// 控制器:控制main线程将会等待所Woker结束后才能继续执行
static CountDownLatch end;
public static void main(String[] args) throws Exception {
// 线程数量
int threadCount = 50;
end = new CountDownLatch(threadCount);
int count = 20;//每个线程的操作次数
AtomicInteger got = new AtomicInteger();//计数器:统计可以拿到连接的线程
AtomicInteger notGot = new AtomicInteger();//计数器:统计没拿到连接的线程
for (int i = 0; i < threadCount; i++) {
Thread thread = new Thread(new Worker(count, got, notGot), "worker_"+i);
thread.start();
}
end.await();// main线程在此处等待
System.out.println("总共尝试了: " + (threadCount * count));
System.out.println("拿到连接的次数: " + got);
System.out.println("没能连接的次数: " + notGot);
}
static class Worker implements Runnable {
int count;
AtomicInteger got; // 分别统计连接获取的数量got
AtomicInteger notGot; // 分别统计未获取到的数量notGot
public Worker(int count, AtomicInteger got, AtomicInteger notGot) {
this.count = count;
this.got = got;
this.notGot = notGot;
}
public void run() {
while (count > 0) {
try {
// 从线程池中获取连接,如果1000ms内无法获取到,将会返回null
// 分别统计连接获取的数量got和未获取到的数量notGot
Connection connection = pool.fetchConn(1000);
if (connection != null) {
try {
connection.createStatement();
connection.commit();
} finally {
pool.releaseConn(connection);
got.incrementAndGet(); // 拿到统计+1
}
} else {
notGot.incrementAndGet(); // 拿不到统计+1
System.out.println(Thread.currentThread().getName()+"等待超时!");
}
} catch (Exception ex) {
} finally {
count--;
}
}
end.countDown();// 做一次总统计
}
}
}
2.join()方法
public class UseJoin {
static class JumpQueue implements Runnable {
private Thread thread;
public JumpQueue(Thread thread) { this.thread = thread; }
public void run() {
try {
System.out.println(thread.getName()+"加入到"+Thread.currentThread().getName()+"前面"); //先加入先执行
thread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"拿钱.");
}
}
public static void main(String[] args) throws Exception {
Thread previous = Thread.currentThread();
for (int i = 0; i < 10; i++) {
Thread thread = new Thread(new JumpQueue(previous), String.valueOf(i));
thread.start();
previous = thread;
SleepTools.ms(50);
}
//模拟等待时间,让线程加入执行
SleepTools.second(2);
}
}
/*
程序执行结果:
main加入到0前面
0加入到1前面
1加入到2前面
2加入到3前面
3加入到4前面
4加入到5前面
5加入到6前面
6加入到7前面
7加入到8前面
8加入到9前面
0拿钱.
1拿钱.
2拿钱.
3拿钱.
4拿钱.
5拿钱.
6拿钱.
7拿钱.
8拿钱.
9拿钱.
*/