Java线程基础知识
## 补充内容:
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.线程过多,内存溢出,容易宕机。
二.认识Java的线程
1.java线程的3种启动方式,3种有什么区别和实现具体案例。2.怎么样才能让Java里的线程安全停止工作呢。2.1 interrupt(), isInterrupted(), static方法interrupted():主要作用。2.2 线程中断(interrupt())具体案例说明。3.守护线程4.对象锁和类锁5.volatile关键字6.ThreadLocal的使用
三.认识Java里的线程常用方法
1.wait/notify,notifyAll2.join()->交出执行权,让加入线程先执行。3.yield()->持有的锁是不释放的4.sleep()->持有的锁是不释放的5.wait()->调用方法之前,必须持有锁。方法执行会释放锁。当wait方法返回的时候会重新持有锁。6.notify()->调用方法之前,必须持有锁。调用notify不会释放锁。
-------java基础知识详细
一 .基础概念
1.CPU核心数和线程数的关系
核心数:线程数=1:1 ;使用了超线程技术后—> 1:2
2.CPU时间片轮转机制
又称RR调度,会导致上下文切换
3.什么是进程和线程
进程:程序运行资源分配的最小单位,进程内部有多个线程,会共享这个进程的资源
线程:CPU调度的最小单位,必须依赖进程而存在。
4.澄清并行和并发
并行:同一时刻,可以同时处理事情的能力
并发:与单位时间相关,在单位时间内可以处理事情的能力
5.高并发编程的意义、好处和注意事项
好处:充分利用cpu的资源、加快用户响应的时间,程序模块化,异步化
问题:
线程共享资源,存在冲突;
容易导致死锁;
启用太多的线程,就搞垮机器的可能
二.认识Java里的线程
1.java线程的3种启动方式,3种有什么区别和实现具体案例。
1.类Thread,接口Runnable,接口Callable3种方式。
2.Thread类单继承的,不方便扩展。Runnable是多继承的,方便扩展。Callable执行线程有返回值。
3.具体实现方式:
1.创建线程类
/*扩展自Thread类*/
private static class MyThread extends Thread{@Overridepublic void run() {System.out.println("I am implements MyThread");}
}/*实现Runnable接口*/
private static class UseRun implements Runnable{@Overridepublic void run() {System.out.println("I am implements Runnable");}
}/*实现Callable接口,允许返回值*/
private static class UseCall implements Callable<String>{@Overridepublic 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); }@Overridepublic 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{@Overridepublic 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);}@Overridepublic 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 {@Overridepublic 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<Thread,Integer>,线程之间不会影响线程变量。
使用案例:
public class UseThreadLocal { //1. ThreadLocal可以理解为 一个map,类型 Map<Thread,Integer>static ThreadLocal<Integer> threadLaocl = new ThreadLocal<Integer>(){@Overrideprotected 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+1threadLaocl.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();}
}
三.认识Java里的线程常用方法
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{@Overridepublic void run() {express.waitKm();}}/*检查地点变化的线程,不满足条件,线程一直等待*/private static class CheckSite extends Thread{@Overridepublic 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时间内还拿不到数据库连接,返回一个nullpublic 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) { // 空 且 等待时间大于0pool.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; // 分别统计连接获取的数量gotAtomicInteger notGot; // 分别统计未获取到的数量notGotpublic 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和未获取到的数量notGotConnection connection = pool.fetchConn(1000);if (connection != null) {try {connection.createStatement();connection.commit();} finally {pool.releaseConn(connection);got.incrementAndGet(); // 拿到统计+1}} else {notGot.incrementAndGet(); // 拿不到统计+1System.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拿钱.
*/