Java多线程知识,多线程的知识点
00-1010 Java多线程知识点总结(1)什么是进程?什么是线程?(2)多线程的运行状态;(3)线程的创建和使用;(4)实现多线程的可运行接口;(5)实现多线程和通用操作方法的可调用接口;(1)线程的命名和获取;(2)线程休眠;(3)线程中断;(4)线程执行;(5)线程礼貌;(6)线程优先级;(7)如何停止线程;(8)
目录
00-1010流程:
它是并发执行程序在执行过程中分配和管理资源的基本单位。一个程序进入内存圈,就是一个线程。
进程三大特点:
独立性:进程是系统中的一个独立实体。它可以独立拥有资源,每个进程都有自己独立的空间。动态:进程和程序的区别在于,进程是动态的,进程有自己的生命周期并发性。超过3360个进程可以在一个处理器上同时执行,而不会影响彼此的并行和并发:.
并行是指同时执行多个命令;并发是指在同一时间,处理器上只执行一个命令,但是多个进程命令快速轮换执行,这样宏观上就是多个进程同时执行。线程:
线程被称为轻量级进程。线程是进程的执行单元和组件。一个进程可以有多个线程,一个线程必须有一个父进程。一个线程可以拥有栈,局部变量…但是它不能拥有系统资源。它与父进程的其他线程共享父进程的所有资源。一个线程可以和父进程的其他线程共享父进程的变量和部分环境,不需要协作就可以一起完成任务。很容易理解,一个类类执行的时候是一个JVM进程,其中成员属性是子线程共享资源,主方法是主线程,其他方法是子线程。子线程(方法)相互配合完成类任务。线程独立运行,线程的执行是抢占式的,这意味着正在执行的线程可能会被挂起,以便运行另一个线程。一个进程可以创建或取消另一个线程,并且一个进程中的线程是并发执行的。
Java多线程知识点总结
(1)线程一般具有5种基本状态 :
创建、就绪、运行、阻塞、终止
(2)
创建状态:在程序中
00-1010 (1)线程的创建
如果要在Java中实现多线程,就必须依赖thread主题类。然而,当定义这个线程主题类时,你还需要一种特殊的需求。这个类可以继承Thread并实现Runnable接口或Callable接口来完成定义。任何类只要继承了thread类都可以成为一个类的主类,而Thread需要在父类的末尾重写run()方法。
(2)螺纹的使用
class javademoa { public static void main(string[]args){ new my thread( thread a : )。start();我的线程(线程B: )。start();MyThread (Thread C: )。start();} } Class My Thread Extensions Thread {//线程私有字符串标题的主体类;public myth read(String title){ this . title=title;} @覆盖public void run(){ for(int I=1;I=4;I){ system . out . println(this . title I);} }}
00-1010Thread是Runnable的子类,继承了Runnable的接口。Thread类确实可以方便多线程,但是这种方法最大的缺点就是单一继承的限制。
Runnable接口从JDK1.8开始就是函数接口,可以直接使用lambda表达式实现线程体代码。同时,这个接口中提供了run()方法来定义线程函数。
线程构造方法3360公共线程(runnable目标)。
在Thread类中,将保存目标属性,该属性保存Runnable的核心业务实体对象。
当Thread.start()方法启动多线程时
也会调用Thread.run() 方法,而在 Thread.run() 会判断是否提供有target实例,如果提供则调用真实主体
public class JavaDemoB { public static void main(String[] args) { new Thread(new MyThreadB("线程A:")).start(); new Thread(new MyThreadB("线程B:")).start(); new Thread(new MyThreadB("线程C:")).start(); }}class MyThreadB implements Runnable{ private String title; public MyThreadB(String title){ this.title=title; } @Override public void run() { for(int i=1;i<=5;i++){ System.out.println(title+i); 加粗样式 } }}
(5)Callable接口实现多线程
Runnable 接口实例化多线程可以避免单继承问题,但 Runnable 中的run() 方法不能返回操作结果,为啦解决这样的问题,从JDK1.5开始对于多线程的实现提供了一个新的接口 java.util.concurrent.Callable
Callable接口在定义时需要定义泛型。
通过FutureTask 实现Callable接口和Thread类的联系,并且通过FutureTask类获取Callable接口中call()方法的返回值。
import java.util.concurrent.Callable;import java.util.concurrent.FutureTask;public class JavaDemoC { public static void main(String[] args) throws Exception{ FutureTask<String> task=new FutureTask<>(new MyThreadC()); new Thread(task).start(); System.out.println(task.get()); }}class MyThreadC implements Callable<String> { @Override public String call() throws Exception { for(int i=1;i<=5;i++){ System.out.println("线程执行:i="+i); } return "Callable实现多线程"; }}
多线程常用操作方法
(1)线程的命名和取得
public Thread (Runnable target,String name) 构造实例化线程对象,接受 Runnable 接口子类对象,同时设置线程名称;
public final void setName(String name) 普通 设置线程名称
public final String getName() 取得线程名字
每当实例化Thread类对象时,都会调用init()方法,并且在没有为线程命名时,自动命名。
package ThreadTest;public class JavaDemoD { public static void main(String[] args) { MyThreadD myThread=new MyThreadD(); new Thread(myThread,"线程A").start();//线程手动命名 new Thread(myThread).start();//线程自动命名 new Thread(myThread,"线程B").start(); }}class MyThreadD implements Runnable{ @Override public void run() { //获取当前线程名称 System.out.println(Thread.currentThread().getName()); }}
(2)线程休眠
线程有时候需要减缓执行速度,所以Thread 类提供啦sleep()方法
public static void sleep(long millis) throws InterruptedException设置线程毫秒数,时间一到自动唤醒
public static void sleep(long millis,int nanos) throws InterruptedException设置线程毫秒数,纳秒数,时间一到自动唤醒
public class JavaDemoE { public static void main(String[] args){ Runnable runnable=(()->{//Runnable接口实例 for(int i=1;i<=5;i++){ System.out.println(Thread.currentThread().getName()+i); try { Thread.sleep(2000);//强制让线程休眠2秒 } catch (InterruptedException e) { e.printStackTrace(); } } }); new Thread(runnable,"线程A:").start(); new Thread(runnable,"线程B:").start(); } //本程序两个线程对象,每一个线程对象执行时都要休眠一秒,因为多线程的启动和执行都是有操作系统, //随机分配,虽然看起来A,B线程同时休眠,但也有先后顺序}
(3)线程中断
Thread 中提供的线程方法很多都会抛出InterruptedException中断异常,所以线程在执行过程中可以被另一个线程中断。
public boolean isInterrupted( ) 普通方法 判断线程是否被中断
public void interrupt( ) 普通 中断线程执行
public class JavaDemoF { public static void main(String[] args) throws InterruptedException { Thread thread=new Thread(()->{ System.out.println("线程累了想休眠10S"); try { Thread.sleep(10000); System.out.println("时间到,自然醒来"); } catch (InterruptedException e) { System.out.println("被强制唤醒,继续工作"); } }); thread.start();//线程执行启动 Thread.sleep(2000);//先让子线程运行2秒 if(!thread.isInterrupted()){//如果线程没有中断 System.out.println("让线程终止休眠"); thread.interrupt();//线程中断 } }}
(4)线程强制执行
在多线程并发执行中每个线程对象都会交替执行,如果某个线程对象需要优先完成执行,可以使用 join ()方法强制执行,待其执行完毕后其他线程才会继续执行
public final void join() throws InterruptedException
public class JavaDemoG { public static void main(String[] args) { Thread threadB=new Thread(new MyThreadH(),"线程B"); Runnable runnable=(()->{ for(int i=1;i<=9;i++){ System.out.println(Thread.currentThread().getName()+i); if(i==5){ try { threadB.join();//如果i=5,强制执行线程B } catch (InterruptedException e) { e.printStackTrace(); } } } }); new Thread(runnable,"线程A").start(); threadB.start(); }}class MyThreadH implements Runnable{ @Override public void run() { for(int i=1;i<=5;i++){ System.out.println(Thread.currentThread().getName()+i); } }}
(5)线程礼让
多线程在彼此交替执行时往往需要进行资源轮流占用,如果某些不是很重要的线程抢占到资源但又不着急执行时就可以暂时礼让出去,让其他线程先执行。
public static void yield( ) 线程礼让
public class JavaDemoH { public static void main(String[] args) { Runnable runnable=(()->{//Lambda实例化线程类对象,方便演示 for(int j=1;j<=15;j++){ System.out.println(Thread.currentThread().getName()+j); } }); new Thread(new MyThreadI(),"礼让线程A").start(); new Thread(runnable,"非礼让线程B:").start(); new Thread(runnable,"非礼让线程C:").start(); new Thread(runnable,"非礼让线程D:").start(); }}class MyThreadI implements Runnable{ @Override public void run() { for(int i=1;i<=10;i++){ System.out.println(Thread.currentThread().getName()+i); if(i%3==0){ Thread.yield();//线程礼让 } } }}
(6)线程优先级
在java线程操作时,所有线程运行前都保持就绪状态,此时那个线程的优先级高就有可能优先被执行
所有创建的线程都是子线程,启动时都是同样的优先级。
主线程是中等优先级 5
public final void setPriority ( int newPriority ) 设置线程优先级
MAX_PRIORITY 最高优先级 10
NORM_PRIORITY 中等优先级 5
MIN_PRIORITY 最低优先级 1
public final int getPriority ( ) 获取线程优先级
public class JavaDemoI { public static void main(String[] args) { Runnable runnable=(()->{ for (int i=1;i<=5;i++){ System.out.println(Thread.currentThread().getName()+i); } }); Thread threadA=new Thread(runnable,"线程A"); Thread threadB=new Thread(runnable,"线程B"); Thread threadC=new Thread(runnable,"线程C"); threadA.setPriority(Thread.MIN_PRIORITY); threadB.setPriority(Thread.NORM_PRIORITY); threadC.setPriority(Thread.MAX_PRIORITY); threadA.start(); threadB.start(); threadC.start(); }}
(7)如何停止线程
(1)停止线程的3个方法(了解)
停止多线:public void stop ()挂起多线程::public final void suspend()恢复挂起的多线程:public final void resume()(2)对多线程中 stop (), suspend(), resume() 方法在jdk1.2开始不建议使用,主要是因为这三个方法在操作时会产生死锁的问题。
(3)优雅的停止一个线程
代码演示:
package ThreadTest;public class JavaDemoO { private static boolean flag=true; public static void main(String[] args) throws InterruptedException { new Thread(()->{ long num=0; while (flag){ try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"正在运行"+num++); } },"执行线程").start(); Thread.sleep(200); flag=false; }}
(8)后台守护线程
(1)java中的线程分为两类:
用户线程和守护线程,守护线程:是一种运行在后台的线程服务线程,当用户线程存在时,守护线程也可以用时存在;当用户线程消失时,守护线程也会消失。public final void setDaemon(boolean on) 设置为守护线程public final boolean idDaemon() 判断是否是守护线程代码:
package ThreadTest;public class JavaDemoP { public static void main(String[] args) { Thread threadA=new Thread(()->{ for(int i=1;i<=100;i++){ try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+i); } },"用户线程"); Thread threadB=new Thread(()->{ for(int i=1;i<=200;i++){ try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+i); } },"守护线程"); threadB.setDaemon(true);//设置为守护线程 threadA.start(); threadB.start(); }}
结果:用户线程结束时,守护线程也结束
(9)volatile 关键字
在多线程编程中,若干个线程为了可以实现公共资源的操作,往往是复制相应变量的副本,待完成操作后再将副本变量的数据与原始数据进行同步处理,如果开发者不希望通过副本数据进行操作,而是希望可以直接通过原始变量的操作(节省了复制变量副本与同步的时间),则可以在变量声明时使用volatile关键字.
使用volatile关键字
代码:
package ThreadTest;public class JavaDemoQ { public static void main(String[] args) { new Thread(new MyThreadQ(),"线程A售票成功_剩余票数:").start(); new Thread(new MyThreadQ(),"线程B售票成功_剩余票数:").start(); new Thread(new MyThreadQ(),"线程C售票成功_剩余票数:").start(); new Thread(new MyThreadQ(),"线程D售票成功_剩余票数:").start(); }}class MyThreadQ implements Runnable{ private volatile int ticket=50;//直接内存操作 @Override public void run() { while (ticket>0){ try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } ticket--; System.out.println(Thread.currentThread().getName()+ticket); } }}
结果:
volatile与synchronized的区别:
volatile无法进行同步处理操作,它只是一种直接内存的处理,避免副本操作volatile主要是在属性上使用,而synchronize是在方法和代码块上使用。
线程的同步和死锁
(1)线程同步问题
(1) 同步问题的引出
public class JavaDemoJ { private static int ticket=5;//总票数5张 public static void main(String[] args) { Runnable runnable=(()->{ while (true){//持续卖票 if(ticket>0){ try { Thread.sleep(100);//模拟网络延迟 } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"卖票成功t剩余票数:"+ticket--); } if(ticket<=0){ System.out.println(Thread.currentThread().getName()+"卖票失败t剩余票数:"+ticket); break; } } }) ; new Thread(runnable,"售票员A:").start(); new Thread(runnable,"售票员B:").start(); new Thread(runnable,"售票员C:").start(); //假设此时还有一张票,当第一个线程满足售票条件时(还未减少票数),其他线程也可能满足售票条件, //就有可能造成同一张票卖出去两次。 }}
问题所在:
假设此时还有一张票,当第一个线程满足售票条件时(还未减少票数),其他线程也可能满足售票条件就有可能造成同一张票卖出去两次。
(2) 解决同步问题
Java使用Synchronized关键字实现同步操作,同步的关键是要给代码上锁,而对于锁的操作有两种:同步代码块,同步方法。
(1)同步代码块
同步代码块是指使用synchronized关键字定义的代码块,在代码执行时,往往需要设置一个同步对象,由于线程操作的不确定性所以这时候的同步对象可以选择 this。
将票数判断和票数自减放在同一代码块中,当多线程并发执行时,只允许一个线程执行该部分代码块,就实现啦同步处理操作、
public class JavaDemoK { public static void main(String[] args) { MyThreadK myThreadK=new MyThreadK(); new Thread(myThreadK,"线程A售票成功,剩余票数:").start(); new Thread(myThreadK,"线程B售票成功,剩余票数:").start(); new Thread(myThreadK,"线程C售票成功,剩余票数:").start(); new Thread(myThreadK,"线程D售票成功,剩余票数:").start(); }}class MyThreadK implements Runnable{ private static int ticket=10;//总票数 @Override public void run() { while (true){ synchronized (this){//同步代码块 if(ticket>0){ try { Thread.sleep(100);//模拟网络延迟 } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+(--ticket)); }else { System.out.println("全部售空"); break; } } } }}
结果:
(2)同步方法解决
public class JavaDemoL { public static void main(String[] args) { MyThreadL myThreadL=new MyThreadL(); new Thread(myThreadL,"售票员A售票成功,剩余票数:").start(); new Thread(myThreadL,"售票员B售票成功,剩余票数:").start(); new Thread(myThreadL,"售票员C售票成功,剩余票数:").start(); new Thread(myThreadL,"售票员D售票成功,剩余票数:").start(); }}class MyThreadL implements Runnable{ private static int ticket=10; @Override public void run() { sale(); } public synchronized void sale() {//同步方法 while (true){ if(this.ticket>0){ try { Thread.sleep(100);//网络延迟 } catch (InterruptedException e) { e.printStackTrace(); } this.ticket--; System.out.println(Thread.currentThread().getName()+this.ticket); } else { System.out.println("票已售空"); break; } } }}
结果:
(2)线程死锁问题
(1)死锁问题的引出
代码:
package ThreadTest;public class JavaDemoJ implements Runnable{ private book book=new book(); private money money=new money(); public JavaDemoJ(){ new Thread(this).start(); book.tell(money); } public void run(){ money.tell(book); } public static void main(String[] args) { new JavaDemoJ(); }}class book{ public synchronized void tell(money money){ System.out.println("你先给我钱,我才给你书!"); money.get(); } public synchronized void get(){ System.out.println("收到钱,把书给买家!"); }}class money{ public synchronized void tell(book book){ System.out.println("你先给我书,我再给你钱!"); book.get(); } public synchronized void get(){ System.out.println("收到书,把钱给卖家!"); }}
结果:
结论:本程序中采用大量同步处理操作,而死锁一旦出现线程将进入等待操作,并且不会向下继续执行。
(3)生产者消费者问题
(1)生产者生产,消费者取出,生产者生产一个,消费者取出一个
未同步代码:
package ThreadTest;public class JavaDemoM { public static void main(String[] args) { Message message=new Message(); new Thread(new Producer(message)).start(); new Thread(new Consumer(message)).start(); }}class Message{//消息类 private String title;//标题 private String content;//内容 public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public String getContent() { return content; } public void setContent(String content) { this.content = content; }}class Producer implements Runnable{//定义生产者 private Message msg=null; public Producer(Message msg){ this.msg=msg; } @Override public void run() { for(int i=1;i<=50;i++){ try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } if(i%2==0){ msg.setTitle(i+"英雄"); msg.setContent("名垂千古"); }else { msg.setTitle(i+"小人"); msg.setContent("遗臭万年"); } } }}class Consumer implements Runnable{ private Message msg; public Consumer(Message msg){ this.msg=msg; } @Override public void run() { for (int i=1;i<=50;i++){ try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("【"+msg.getTitle()+","+msg.getContent()+"】"); } }}
错误产生结果:
问题分析:
数据错位:假设生产者线程还刚在存储空间添加一个数据的标题,还未添加内容,程序就切换到消费者线程,消费者就会把没有生产者没有添加的内容和上一组生产的内容联系在一起导致数据错位。重复操作:生产者线程放入多组数据,消费者线程才开始取出,或者是消费者还没生产数据,消费者就已经重复取出数据。
同步代码解决:
package ThreadTest;public class JavaDemoN { public static void main(String[] args) { Message2 msg=new Message2(); new Thread(new Producer2(msg)).start(); new Thread(new Consumer2(msg)).start(); }}class Message2 { private String title; private String content; private boolean key=true; //true 允许生产不允许消费 //false 允许消费不允许生产 public synchronized void set(String title,String content) {//同步方法 if(this.key==false){//允许消费不允许生产 try { super.wait();//线程等待 } catch (InterruptedException e) { e.printStackTrace(); } } this.title = title; this.content = content; this.key=false;//生产完毕 super.notify();//唤醒线程 } public synchronized String get() { if(this.key==true){//允许生产不允许消费 try { super.wait();//线程等待 } catch (InterruptedException e) { e.printStackTrace(); } } try { return title +","+ content; }finally { this.key=true; super.notify(); } }}class Producer2 implements Runnable{//定义生产者 private Message2 msg=null; public Producer2(Message2 msg){ this.msg=msg; } @Override public void run() { for(int i=1;i<=50;i++){ try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } if(i%2==0){ this.msg.set(i+"英雄","万古流芳"); }else { this.msg.set(i+"小人","遗臭万年"); } } }}class Consumer2 implements Runnable{ private Message2 msg; public Consumer2(Message2 msg){ this.msg=msg; } @Override public void run() { for (int i=1;i<=50;i++){ try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("【"+msg.get()+"】"); } }}
正确执行结果:
原理解释:
将同步操作设置在Message类中,使用synchronized关键字可以使消息内容同步,不会造成消息错位。
在生产者和消费者线程中根据key判断,是否可以生产或者取出,如果不能生产或者取出,可使用 wait( ) 线程等待方法 等待另一方线程取出或者生产后,使用 notify( ) 线程唤醒,继续执行操作。notifyAll( ) 是唤醒全部等待的线程
以上为个人经验,希望能给大家一个参考,也希望大家多多支持盛行IT。
郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。