线程知识点,小归纳(笔记)
首先对一些简单概念进行理解
进程:程序运行资源分配的最小单位,进程内部有多个线程,并共享整个进程的资源。
线程:CPU调度的最小单位
并发和并行的区别:
并行:多个cpu实例或者多台机器同时执行一段处理逻辑,是真正的同时。(在食堂有八个窗口,八个窗口并发)
并发:通过cpu调度算法,让用户看上去同时执行,实际上从cpu操作层面不是真正的同时。并发往往在场景中有公用的资源,那么针对这个公用的资源往往产生瓶颈,我们会用TPS或者QPS来反应这个系统的处理能力。(每个窗口30s能给一个儿打饭,并发度为16)
线程创造三种方式:
1.继承Thread的父类
2.实现Runnable的接口
3.实现Callable的接口
(java类是单继承和多实现)。
package thread.cn; import java.text.SimpleDateFormat; import java.util.Date; import java.util.concurrent.Callable; import java.util.concurrent.FutureTask; public class createThread { private static SimpleDateFormat formater = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss_SSS"); public static class CreateThread1 extends Thread{ public void run(){ try { while (!isInterrupted()) { System.out.println(Thread.currentThread().getName() + "is running"); } } finally { System.out.println("finally"); } } } public static class CreateThread2 implements Runnable{ @Override public void run() { String threadName = Thread.currentThread().getName(); while(!Thread.currentThread().isInterrupted()) { try { System.out.println(Thread.currentThread().getName() + "is running"); System.out.println("UseThread:"+formater.format(new Date())); Thread.currentThread(); Thread.sleep(200); } catch (InterruptedException e) { System.out.println(threadName+" catch interrput flag is " +Thread.currentThread().isInterrupted()+ " at " +(formater.format(new Date()))); Thread.currentThread().interrupt(); e.printStackTrace(); } System.out.println(threadName); } System.out.println(threadName+" interrput flag is " +Thread.currentThread().isInterrupted()); } } public static class CreateThread3 implements Callable<String>{ @Override public String call() throws Exception { System.out.println("this is Callable!"); return "Callable"; } } public static void main(String[] args) throws Exception { //1.继承Thread父类 Thread method1 = new CreateThread1(); method1.start(); method1.interrupt(); //2.实现Runnable接口 CreateThread2 method2 = new CreateThread2(); Thread t = new Thread(method2); t.start(); System.out.println("Main:"+formater.format(new Date())); Thread.sleep(800); t.interrupt(); //3.实现Callable接口 CreateThread3 method3 = new CreateThread3(); FutureTask<String> futureTask = new FutureTask<>(method3); new Thread(futureTask).start(); } }
中断线程
stop过于强硬,对线程安全不友好,所以一般不用。
interrupt()中断一个线程,并不是强行关闭这个线程,而是打个招呼(java线程是协作式),给中断标志位设置为true;
isInterrupted()判断当前线程是否处于中断状态。
static方法interrupted()判断当前,断标志位设置为false。
需要注意到的是在catch到InterruptedException e 后,中断标志位会被取消,需要重新设置中断标志位才会使线层中断
catch (InterruptedException e) { System.out.println(threadName+" catch interrput flag is " +Thread.currentThread().isInterrupted()+ " at " +(formater.format(new Date()))); Thread.currentThread().interrupt(); e.printStackTrace(); }
线程的状态图
join()
当A线程调用调用此方法,使得当前在运行的B线程与该线程合并,即:等待A线程结束,B线程才能继续运行,在这时间段内,B处于等待状态。
package thread.cn; public class joinAndyield { public static class Testjoin implements Runnable{ @Override public void run() { String threadName ="ThreadName:"+ Thread.currentThread().getName(); for(int i = 0 ; i <5 ;i++) { System.out.println(threadName); Thread.currentThread(); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } } public static void main(String[] args) throws Exception { Testjoin testjoin = new Testjoin(); Thread t = new Thread(testjoin); t.setName("testJoin"); t.start(); Thread.sleep(1000); t.join(); for (int i = 0; i <= 5; i++) { System.out.println("I am main Thread"); } } }
在未写t.join之前,测试线程和主线程是随机的运行状态,
ThreadName:testJoin
ThreadName:testJoin
I am main Thread
I am main Thread
I am main Thread
I am main Thread
I am main Thread
I am main Thread
ThreadName:testJoin
ThreadName:testJoin
ThreadName:testJoin
写了之后
ThreadName:testJoin
ThreadName:testJoin
ThreadName:testJoin
ThreadName:testJoin
ThreadName:testJoin
I am main Thread
I am main Thread
I am main Thread
I am main Thread
I am main Thread
I am main Thread
yield
在做了大量实验和分析后,哎,先看下代码吧
package thread.cn; import thread.cn.createThread.CreateThread1; public class joinAndyield { public static class Testyield extends Thread{ @Override public void run() { for(int i = 1 ; i <= 5 ;i++) { System.out.println(getName()+"----"+i+""); if(0==i%2) { try { sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("\\\\\\"+getName()+"\\\\"); Thread.yield(); } } } } public static void main(String[] args) throws Exception { Thread t1 = new Testyield(); Thread t2 = new Testyield(); t1.start(); t2.start(); } }
Thread-1----1 Thread-0----1 Thread-0----2 Thread-1----2 \\\Thread-0\\ Thread-0----3 Thread-0----4 \\\Thread-1\\ Thread-1----3 Thread-1----4 \\\Thread-0\\ Thread-0----5 \\\Thread-1\\ Thread-1----5
分析可得,yield()之后并不会将线程给另外一个线程,虚拟机做的只是释放当前线程的资源。yield()让当前正在运行的线程回到可运行状态,以允许具有相同优先级的其他线程获得运行的机会。因此,使用yield()的目的是让具有相同优先级的线程之间能够适当的轮换执行。但是,实际中无法保证yield()达到让步的目的,因为,让步的线程可能被线程调度程序再次选中。
对象锁
synchronized:
一个线程A获取了该对象的锁之后,其他线程来访问其他synchronized实例方法现象时,要先A把释放掉后其他线程才能拿到对象锁。
package thread.cn; public class ThreadSy extends Thread{ synchronized void show1() { System.out.println("show1 start"); try { Thread.sleep(1000); System.out.println("show1 is run"); } catch (InterruptedException e) { e.printStackTrace(); }finally { System.out.println("show1 is finally"); } } synchronized void show2() { System.out.println("show2 start"); try { Thread.sleep(1000); System.out.println("show2 is run"); } catch (InterruptedException e) { e.printStackTrace(); }finally { System.out.println("show2 is finally"); } } public static void main(String[] args) { ThreadSy becomeBt = new ThreadSy(); Thread t1 = new Thread(becomeBt); Thread t2 = new Thread(becomeBt); new Thread(t1) { public void run(){ becomeBt.show1(); } }.start(); new Thread(t2) { public void run(){ becomeBt.show2(); } }.start(); } }
结果:
show1 start show1 is run show1 is finally show2 start show2 is run show2 is finally
当访问其他非synchronized实例方法时,无需A释放锁。
package thread.cn; public class ThreadSy extends Thread{ synchronized void show1() { System.out.println("show1 start"); try { Thread.sleep(1000); System.out.println("show1 is run"); } catch (InterruptedException e) { e.printStackTrace(); }finally { System.out.println("show1 is finally"); } } void show2() { System.out.println("show2 start"); try { Thread.sleep(1000); System.out.println("show2 is run"); } catch (InterruptedException e) { e.printStackTrace(); }finally { System.out.println("show2 is finally"); } } public static void main(String[] args) { ThreadSy becomeBt = new ThreadSy(); Thread t1 = new Thread(becomeBt); Thread t2 = new Thread(becomeBt); new Thread(t1) { public void run(){ becomeBt.show1(); } }.start(); new Thread(t2) { public void run(){ becomeBt.show2(); } }.start(); } }
结果:
show1 start show2 start show1 is run show1 is finally show2 is run show2 is finally
类锁:
synchronized作用于一个给定的实例对象instance,即当前实例对象就是锁类对象,每次当线程进入synchronized包裹的代码块时就会要求当前线程持有instance实例对象锁,如果当前有其他线程正持有该对象锁,那么新到的线程就必须等待,这样也就保证了每次只有一个线程执行i++;操作、
package thread.cn; public class synchronizedTest1 implements Runnable { static synchronizedTest1 instance=new synchronizedTest1(); static int i=0; @Override public void run() { //省略其他耗时操作.... //使用同步代码块对变量i进行同步操作,锁对象为instance synchronized(instance){ for(int j=0;j<5;j++){ i++; System.out.println(Thread.currentThread().getName()+"-----"+i); } } } public static void main(String[] args) throws InterruptedException { Thread t1=new Thread(instance); Thread t2=new Thread(instance); t1.start(); t2.start(); t1.join(); t2.join(); System.out.println(i); } } Thread-0-----1 Thread-0-----2 Thread-0-----3 Thread-0-----4 Thread-0-----5 Thread-1-----6 Thread-1-----7 Thread-1-----8 Thread-1-----9 Thread-1-----10 10
小结:
在对象锁中,若两个线程锁同一个对象,必是先拿到锁的先运行(并不是运行完成所有,但是是先运行),再是另外线程运行。
若线程锁的是两个毫不相关的对象时,他们是并行关系,可以同时运行。
类锁:类锁和不相干的对象锁同时被线程运行时,他们是并行关系,可以同时运行。