java多线程实力讲解
Posted meililiuwei
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了java多线程实力讲解相关的知识,希望对你有一定的参考价值。
昨天,由于工作比较繁忙,只是简单整理了一下java的线程的生命周期的流程图,今天就根据这个流程图来一步一步的讲解java多线程的知识。
图再来一遍:
第一点、java线程新生态的生成
也就是线程新建成功
1、继承Thread类(为了方便添加线程名字,可以自定义构造方法),代码如下:
public class MyThread extends Thread{ // 自己书写构造方法,两种方法都可 public MyThread(String name){ // super.setName(name); super(name); } @Override public void run() { System.out.println("This is a Thread!"); } }
二、实现Runnable接口(现在大多都用函数式编程来实现,为了看的明白,咱两种都用)
// 一般方法 Thread thread1 = new Thread(new Runnable() { @Override public void run() { System.out.println(Thread.currentThread().getName()); } },"thread1"); // 函数式编程 Thread thread2 = new Thread(() ->System.out.println(Thread.currentThread().getName()),"thread2");
第二点、java由新生态转化为就绪态
实际就是start方法,start方法是开启一个新的线程,如果只调用run方法,还是在主线程执行run方法,不会开启新的线程,线程不会进入到就绪状态,所以start方法才是开启线程的方法,
run方法中存入的只是任务内容。代码如下:
Thread thread = new Thread(() ->System.out.println(Thread.currentThread().getName()),"thead"); // 线程的启动 thread.start();
第三点、就绪态到运行态
这一步的话,没有办法使用代码实现,因为呢,当线程执行start方法后,许多线程会去抢占cpu时间片,哪个线程抢占到了cpu时间片,哪个线程就进入到运行状态。
第四点、由运行态回到就绪态
回到就绪态,就需要线程主动放弃抢到的cpu时间片,也就是大家常说的线程的礼让
但是需要注意的是,礼让不代表将cpu时间片彻底让给其他线程,还可能在次抢占时又获取到cpu时间片(没办法,有时候都不知道自己折磨厉害)
代码如下:
public static void main(String[] args) { Runnable r = () -> { for (int i = 0; i < 10; i++){ System.out.println(Thread.currentThread().getName() + " : " + i); if(i == 3){ System.out.println(Thread.currentThread().getName() + " : " + i + "start use yield"); // 线程开始礼让 Thread.yield(); } } }; Thread t1 = new Thread(r,"thread1"); Thread t2 = new Thread(r,"thread2"); // 启动线程 t1.start(); t2.start(); }
第五点、由运行态到阻塞态
故名思意就是阻塞了,线程暂停了,主要导致线程暂停的方法由:等待用户输入、使用sleep方法、使用jion方法
一、用户输入就不多说了无非是:
Thread thread = new Thread(() -> {
Scanner scanner = new Scanner(System.in);
int number = scanner.nextInt();
}, "thread1");
thread.start();
二、使用sleep方法(睡眠)代码如下:
public static void main(String[] args) { Thread thread = new Thread(() -> { for (int i = 0;i < 10;i++){ System.out.println(Thread.currentThread().getName()+" : "+i); try { // 没执行一次睡眠1s Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } },"thread1"); thread.start(); }
三、使用join方法
Thread类中的join方法的主要作用就是同步,使得线程之间的并行执行变为串行执行,切记join在start前面执行无效,不多说
public static void main(String[] args) { // 创建两个线程 Thread thread1 = new Thread(() -> { for(int i = 0;i < 10;i++){ System.out.println(Thread.currentThread().getName()+" : "+i); } },"thread1"); Thread thread2 = new Thread(() -> { for(int i = 0;i < 10;i++){ System.out.println(Thread.currentThread().getName()+" : "+i); } },"thread2"); thread1.start(); // 使用线程同步方法 try { thread1.join(); } catch (InterruptedException e) { e.printStackTrace(); } thread2.start(); System.out.println("This is main Thread!"); }
执行多次后结果如下:
thread1 : 0
thread1 : 1
thread1 : 2
thread1 : 3
thread1 : 4
thread1 : 5
thread1 : 6
thread1 : 7
thread1 : 8
thread1 : 9
This is main Thread!
thread2 : 0
thread2 : 1
thread2 : 2
thread2 : 3
thread2 : 4
thread2 : 5
thread2 : 6
thread2 : 7
thread2 : 8
thread2 : 9
呦呵,thread1厉害了,比主线程优先执行完,还没有thread2与其抢占线程。
揪其原因如下: 程序在main线程中调用thread1线程的join方法,则main线程放弃cpu控制权,并返回thread线程继续执行直到线程t1执行完毕,
所以是thread1执行完后,主线程才执行,其实就是主线程中同步了thread1线程,执行join方法后,主线程阻塞,等thread1执行完必后,主线程继续执行
第六点、阻塞态回到就绪态
话不多说了,就是用户输入完必,sleep方法执行完毕、join方法执行完毕
第七点、由运行态到锁池
首先,涉及到的内容为锁,那么什么条件可以用到锁呢,都有哪些锁呢
一、为什么用到了锁
来,先看一个小问题,实现一个购票系统,4个购票员进行购票,代码如下:
// 定义售票箱的类 static class Ticket{ public static int number = 10; } // 定义四个售票员 // 使用四个线程进行模拟 public static void main(String[] args) { Runnable r = () -> { while(Ticket.number > 0){ System.out.println(Thread.currentThread().getName()+"start sell one ticket......"+"The remaining quantity of tickets is:"+ --Ticket.number); } }; Thread t1 = new Thread(r,"thread - 1"); Thread t2 = new Thread(r,"thread - 2"); Thread t3 = new Thread(r,"thread - 3"); Thread t4 = new Thread(r,"thread - 4"); t1.start(); t2.start(); t3.start(); t4.start();
来,查看部分结果:
thread - 1 start sell one ticket......The remaining quantity of tickets is:3
thread - 1 start sell one ticket......The remaining quantity of tickets is:2
thread - 1 start sell one ticket......The remaining quantity of tickets is:1
thread - 1 start sell one ticket......The remaining quantity of tickets is:0
thread - 4 start sell one ticket......The remaining quantity of tickets is:40
thread - 3 start sell one ticket......The remaining quantity of tickets is:41
??神魔鬼
原来都是资源共享惹的祸,这几个线程共享这个资源,造成了线程不安全
怎么整?
让他们排队用这个资源就好了呀,一个线程操作这个资源时,锁住资源,不让其他线程动即可,这也就有了锁
二、有哪些锁
1、同步代码段
话不多说直接上代码:
// 定义售票箱的类 static class Ticket{ public static int number = 10; } // 定义四个售票员 // 使用四个线程进行模拟 public static void main(String[] args) { Runnable r = () -> { while(Ticket.number > 0){ // 对象锁 "" 任意写都行 // 类锁 // 此锁对于所有人需要都相同 synchronized (""){ if (Ticket.number <= 0){ return; } System.out.println(Thread.currentThread().getName()+"start sell one ticket...... "+"The remaining quantity of tickets is:"+ --Ticket.number); } } }; Thread t1 = new Thread(r,"thread - 1"); Thread t2 = new Thread(r,"thread - 2"); Thread t3 = new Thread(r,"thread - 3"); Thread t4 = new Thread(r,"thread - 4"); t1.start(); t2.start(); t3.start(); t4.start(); }
2、同步方法
话不多说直接上代码:
// 定义售票箱的类 static class Ticket{ public static int number = 10; } // 定义四个售票员 // 使用四个线程进行模拟 public static void main(String[] args) { Runnable r = () -> { while(SourceConflict.Ticket.number > 0){ sellTicket(); } }; Thread t1 = new Thread(r,"thread - 1"); Thread t2 = new Thread(r,"thread - 2"); Thread t3 = new Thread(r,"thread - 3"); Thread t4 = new Thread(r,"thread - 4"); t1.start(); t2.start(); t3.start(); t4.start(); } /** * 同步方法如果为静态方法,锁位类锁,如果不是静态方法,则对应的是this */ public synchronized static void sellTicket(){ if(Ticket.number <= 0){ return; } System.out.println(Thread.currentThread().getName()+"start sell one ticket......"+"The remaining quantity of tickets is:"+ --SourceConflict.Ticket.number); }
3、显示锁
话不多说直接上代码:
// 定义售票箱的类 static class Ticket{ public static int number = 10; } // 定义四个售票员 // 使用四个线程进行模拟 public static void main(String[] args) { ReentrantLock lock = new ReentrantLock(); Runnable r = () -> { while(Ticket.number > 0){ lock.lock(); if(Ticket.number <= 0){ return; } System.out.println(Thread.currentThread().getName()+"start sell one ticket......"+"The remaining quantity of tickets is:"+ --Ticket.number); lock.unlock(); } }; Thread t1 = new Thread(r,"thread - 1"); Thread t2 = new Thread(r,"thread - 2"); Thread t3 = new Thread(r,"thread - 3"); Thread t4 = new Thread(r,"thread - 4"); t1.start(); t2.start(); t3.start(); t4.start(); }
三、所以由运行态怎样到锁池呢
当某一代码段或者方法被锁住后,其他线程等待锁标记,无法访问就会进入锁池
四、补充
提到锁,不得不说的就是死锁
来先来个代码,瞅一瞅:
public static void main(String[] args) { Runnable runnable1 = () -> { synchronized ("a"){ System.out.println("Thread1 gets lock named a"); synchronized ("b"){ System.out.println("Thread1 gets locks named a and b"); } } }; Runnable runnable2 = () -> { synchronized ("b"){ System.out.println("Thread2 gets lock named b"); synchronized ("a"){ System.out.println("Thread2 gets lock named a and b"); } } }; Thread t1 = new Thread(runnable1); Thread t2 = new Thread(runnable2); t1.start(); t2.start(); }
这个就会锁到死,完全符合死锁的定义:死锁:多个线程彼此持有对方所需要的锁对象,而不释放自己的锁
runnable1占着a锁标记等b,rannable2占着b锁标记等a,等到死,也不释放,就产生了死锁
怎么解决呢,这时候就引出下一个内容,使用wait方法进入等待对列,之后通过唤醒进入锁池
第八点、使用wait方法进入等待对列,之后通过唤醒进入锁池
一、通过wait方法进入锁池以及唤醒
为了解决上个死锁问题,我们采用让其中一个线程,拿到一个锁标记,执行完必后,释放掉锁标记,进入等待对列,等其他线程执行完必后,唤醒此线程即可。
代码如下:
public static void main(String[] args) { Runnable runnable1 = () -> { synchronized ("a"){ System.out.println("Thread1 gets lock named a"); try { "a".wait(); } catch (InterruptedException e) { e.printStackTrace(); } synchronized ("b"){ System.out.println("Thread1 gets locks named a and b"); } } }; Runnable runnable2 = () -> { synchronized ("b"){ System.out.println("Thread2 gets lock named b"); synchronized ("a"){ System.out.println("Thread2 gets lock named a and b"); "a".notifyAll(); } } }; Thread t1 = new Thread(runnable1); Thread t2 = new Thread(runnable2); t1.start(); t2.start(); }
二、唤醒方法notifyAll和notify区别
notify:通知,是Object中的一个方法,唤醒等待队列中的一个线程,使这个线程进入锁池
notifyAll: 通知,是Object中得一个方法,唤醒等待队列中得所有线程,使这些线程全部进入锁池
第九点、锁池到就绪态
锁池中的线程,拿到锁标记回到就绪态,也就是占有锁标记的线程将锁标记释放
以上是关于java多线程实力讲解的主要内容,如果未能解决你的问题,请参考以下文章