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,notifyAll
   2.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{
	@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<Thread,Integer>,线程之间不会影响线程变量。
使用案例:

public class UseThreadLocal {	
	//1. ThreadLocal可以理解为 一个map,类型 Map<Thread,Integer>
	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();
    }
}

三.认识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{
        @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拿钱.
*/
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

java之书

会持续更新实用好的文章谢谢关注

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值