Java多线程知识点全面总结

目录
  • Java多线程知识点总结
    • (1)什么是进程?什么是线程?
    • (2)多线程的运行状态
    • (3)线程的创建和使用
    • (4)Runnable 接口实现多线程
    • (5)Callable接口实现多线程
  • 多线程常用操作方法
    • (1)线程的命名和取得
    • (2)线程休眠
    • (3)线程中断
    • (4)线程强制执行
    • (5)线程礼让
    • (6)线程优先级
    • (7)如何停止线程
    • (8)后台守护线程
    • (9)volatile 关键字
  • 线程的同步和死锁
    • (1)线程同步问题
    • (2)线程死锁问题
    • (3)生产者消费者问题

Java多线程知识点总结

(1)什么是进程?什么是线程?

进程:

是并发执行程序在执行过程中分配和管理资源的基本单位,当程序进入内存圆形时即为线程

进程三大特点:

  • 独立性: 进程是系统中独立存在的实体,它可以独立拥有资源,每个进程都有自己的独立空间。
  • 动态性: 进程和程序的区别在于进程动态的,进程具有自己的生命周期
  • 并发性:多个进程可以在单个处理器上并发执行互不影响

并行和并发:

  • 并行是指同一时刻,多个命令同时执行;
  • 并发是指在同一时刻,只有一条命令是在处理器上执行的,但多个进程命令被快速轮换执行,使得在宏观上具有多个进程同时执行的效果。

线程:

  • 线程(Thread)被称为轻量级进程,线程是进程的执行单元
  • 线程是进程的组成部分,一个进程可以拥有多个线程,而一个线程必须拥有一个父进程。线程可以拥有堆栈,局部变量…但不能拥有系统资源,它与父进程的其他线程共享该进程的所有资源
  • 一个线程可与其父进程的其他线程共同拥有父进程共享变量和部分环境,共同非合作完成任务。
  • 可以简便理解一个Class类执行时就是一个JVM的进程,其中的成员属性是子线程共享资源,main方法是主线程,其他方法是子线程,子线程(方法)相互合作共同完成Class任务。
  • 线程是独立运行的,线程的执行是抢占式的,也就是说执行的线程可能被挂起,以便运行另一个线程。
  • 一个进程可以创建或者撤销另一个线程,一个进程中的线程是并发执行的。

(2)多线程的运行状态

(1)线程一般具有5种基本状态 :

创建, 就绪, 运行, 阻塞, 终止

(2)

创建状态:在程序中

(3)线程的创建和使用

(1)线程的创建

在Java 中如果要想实现多线程,那么必须依靠线程的主题类,但这个线程主题类在定义的时候,也需要一类特殊的要求,这个类可以继承 Thread ,实现Runnable 接口或者实现 Callable 接口来完成定义。任何一个类只要继承了 Thread 类就可以成为一个类的主类,同时线程中需要覆写父类终点 run( )方法。

(2)线程的使用

public class JavaDemoA {
    public static void main(String[] args) {
        new MyThread("线程A:").start();
        new MyThread("线程B:").start();
        new MyThread("线程C:").start();
    }
}
class MyThread extends Thread{ //线程的主体类
    private String title;
    public MyThread(String title){
        this.title=title;
    }
    @Override
    public void run() {
        for(int i=1;i<=4;i++){
            System.out.println(this.title+i);
        }
    }
}

(4)Runnable 接口实现多线程

Thread是 Runnable 的子类,继承了 Runnable 的接口,Thread 类的确可以方便实现多线程,但这种方式最大的缺陷就是单继承局限性

Runnable 接口从 JDK1.8 开始成为一个函数接口,可以直接利用lambda表达式来实现线程主体代码,同时在该接口中提供有run()方法进行线程功能定义

Thread构造方法:public Thread(Runnable target).

在Thread类中会保存有target属性,该属性保存的是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( ) 是唤醒全部等待的线程

以上为个人经验,希望能给大家一个参考,也希望大家多多支持我们。

(0)

相关推荐

  • Java多线程之如何确定线程数的方法

    关于多线程的线程数的确定,最近研读过几篇paper,在此做一下笔记,方便使用时翻看. 1.<Java 虚拟机并发编程>中介绍 就是说:线程数 = CPU的核心数 * (1 - 阻塞系数) 另一篇:<Java Concurrency in Practice>即<java并发编程实践>,给出的线程池大小的估算公式: Nthreads=Ncpu*Ucpu*(1+w/c),其中 Ncpu=CPU核心数,Ucpu=cpu使用率,0~1:W/C=等待时间与计算时间的比率 仔细推敲两

  • Java指令重排在多线程环境下的解决方式

    目录 一.序言 二.问题复原 (一)关联变量 1.结果预测 2.指令重排 (二)new创建对象 1.解析创建过程 2.重排序过程分析 三.应对指令重排 (一)AtomicReference原子类 (二)volatile关键字 四.指令重排的理解 1.指令重排广泛存在 2.多线程环境指令重排 3.synchronized锁与重排序无关 一.序言 指令重排在单线程环境下有利于提高程序的执行效率,不会对程序产生负面影响:在多线程环境下,指令重排会给程序带来意想不到的错误. 本文对多线程指令重排问题进行

  • Java多线程之悲观锁与乐观锁

    目录 1. 悲观锁存在的问题 2. 通过CAS实现乐观锁 3. 不可重入的自旋锁 4. 可重入的自旋锁 总结 问题: 1.乐观锁和悲观锁的理解及如何实现,有哪些实现方式? 2.什么是乐观锁和悲观锁? 3.乐观锁可以重入吗? 1. 悲观锁存在的问题 独占锁其实就是一种悲观锁,java的synchronized是悲观锁.悲观锁可以确保无论哪个线程持有锁,都能独占式访问临界区.虽然悲观锁的逻辑非常简单,但是存在不少问题. 悲观锁总是假设会发生最坏的情况,每次线程读取数据时,也会上锁.这样其他线程在读取

  • 浅谈java指令重排序的问题

    指令重排序是个比较复杂.觉得有些不可思议的问题,同样是先以例子开头(建议大家跑下例子,这是实实在在可以重现的,重排序的概率还是挺高的),有个感性的认识 /** * 一个简单的展示Happen-Before的例子. * 这里有两个共享变量:a和flag,初始值分别为0和false.在ThreadA中先给 a=1,然后flag=true. * 如果按照有序的话,那么在ThreadB中如果if(flag)成功的话,则应该a=1,而a=a*1之后a仍然为1,下方的if(a==0)应该永远不会为 * 真,

  • Java中volatile防止指令重排

    目录 什么是指令重排? 为什么指令重排能够提高性能 volatile是怎么禁止指令重排的? volatile可以防止指令重排,在多线程环境下有时候我们需要使用volatile来防止指令重排,来保证代码运行后数据的准确性 什么是指令重排? 计算机在执行程序时,为了提高性能,编译器和处理器一般会进行指令重排,一般分为以下三种: 指令重排有以下三个特点: 1.单线程环境下指令重排后可以保证与顺序执行指令的结果一致(就是不进行指令重排的情况) //原来的执行顺序 a=1; b=0; //进行指令重排后执

  • Java多线程之线程安全问题详解

    目录 1.什么是线程安全和线程不安全? 2.自增运算为什么不是线程安全的? 3.临界区资源和竞态条件 总结: 面试题: 什么是线程安全和线程不安全? 自增运算是不是线程安全的?如何保证多线程下 i++ 结果正确? 1. 什么是线程安全和线程不安全? 什么是线程安全呢?当多个线程并发访问某个Java对象时,无论系统如何调度这些线程,也无论这些线程将如何交替操作,这个对象都能表现出一致的.正确的行为,那么对这个对象的操作是线程安全的. 如果这个对象表现出不一致的.错误的行为,那么对这个对象的操作不是

  • Java十分钟入门多线程下篇

    目录 1.线程池: 2.创建线程池: 1.newCacheThreadPool: 2.newSingleThreadExecutor: 3.newFixedThreadPool(inta): 4.newScheduledTreadPool: 3.线程池创建自定义线程: 4.Runnable和Callable的区别: 5.线程池总结: 1.线程池: 什么是线程池? 咱们也不看长篇大论,通俗的来讲,线程池就是装线程的容器,当需要用的时候去池里面取出来,不用的时候放回去或者销毁.这样一个线程就可以反复

  • Java volatile如何实现禁止指令重排

    计算机在执行程序时,为了提高性能,编译器和处理器常常会对指令重排,一般分为以下三种: 源代码 -> 编译器优化的重排 -> 指令并行的重排 -> 内存系统的重排 -> 最终执行指令 单线程环境里面确保最终执行结果和代码顺序的结果一致 处理器在进行重排序时,必须要考虑指令之间的数据依赖性 多线程环境中线程交替执行,由于编译器优化重排的存在,两个线程中使用的变量能否保证一致性是无法确定的,结果无法预测. 指令重排 - example 1 public void mySort() { i

  • Java多线程知识点全面总结

    目录 Java多线程知识点总结 (1)什么是进程?什么是线程? (2)多线程的运行状态 (3)线程的创建和使用 (4)Runnable 接口实现多线程 (5)Callable接口实现多线程 多线程常用操作方法 (1)线程的命名和取得 (2)线程休眠 (3)线程中断 (4)线程强制执行 (5)线程礼让 (6)线程优先级 (7)如何停止线程 (8)后台守护线程 (9)volatile 关键字 线程的同步和死锁 (1)线程同步问题 (2)线程死锁问题 (3)生产者消费者问题 Java多线程知识点总结

  • 分享40个Java多线程问题小结

    Java多线程是什么 Java提供的并发(同时.独立)处理多个任务的机制.多个线程共存于同一JVM进程里面,所以共用相同的内存空间,较之多进程,多线程之间的通信更轻量级.依我的理解,Java多线程完全就是为了提高CPU的利用率.Java的线程有4种状态,新建(New).运行(Runnable).阻塞(Blocked).结束(Dead),关键就在于阻塞(Blocked),阻塞意味着等待,阻塞的的线程不参与线程分派器(Thread Scheduler)的时间片分配,自然也就不会使用到CPU.多线程环

  • java多线程编程之Synchronized关键字详解

    本文介绍JAVA多线程中的synchronized关键字作为对象锁的一些知识点. 所谓对象锁,就是就是synchronized 给某个对象 加锁.关于 对象锁 可参考:这篇文章  一.分析 synchronized可以修饰实例方法,如下形式: public class MyObject { synchronized public void methodA() { //do something.... } 这里,synchronized 关键字锁住的是当前对象.这也是称为对象锁的原因. 为啥锁住当

  • JAVA多线程之中断机制stop()、interrupted()、isInterrupted()

    一,介绍 本文记录JAVA多线程中的中断机制的一些知识点.主要是stop方法.interrupted()与isInterrupted()方法的区别,并从源代码的实现上进行简单分析. JAVA中有3种方式可以终止正在运行的线程 ①线程正常退出,即run()方法执行完毕了 ②使用Thread类中的stop()方法强行终止线程.但stop()方法已经过期了,不推荐使用 ③使用中断机制 线程正常退出没有什么东东,中断机制下面详细介绍,先看下stop()方法的源代码,关键是源代码上的注释.它解释了为什么s

  • Java 多线程实例讲解(一)

    Java多线程(一) 多线程作为Java中很重要的一个知识点,在此还是有必要总结一下的. 一.线程的生命周期及五种基本状态 关于Java中线程的生命周期,首先看一下下面这张较为经典的图: 上图中基本上囊括了Java中多线程各重要知识点.掌握了上图中的各知识点,Java中的多线程也就基本上掌握了.主要包括: Java线程具有五中基本状态 新建状态(New):当线程对象对创建后,即进入了新建状态,如:Thread t = new MyThread(); 就绪状态(Runnable):当调用线程对象的

  • JAVA多线程之方法 JOIN详解及实例代码

    JAVA多线程 JOIN 对于Java开发人员,多线程应该是必须熟练应用的知识点,特别是开发基于Java语言的产品.本文将深入浅出的表述Java多线程的知识点,在后续的系列里将侧重于Java5由Doug Lea教授提供的Concurrent并行包的设计思想以及具体实现与应用. 如何才能深入浅出呢,我的理解是带着问题,而不是泛泛的看.所以该系列基本以解决问题为主,当然我也非常希望读者能够提出更好的解决问题的方案以及提出更多的问题.由于水平有限,如果有什么错误之处,请大家提出,共同讨论,总之,我希望

  • Java多线程中断机制三种方法及示例

    概述 之前讲解Thread类中方法的时候,interrupt().interrupted().isInterrupted()三个方法没有讲得很清楚,只是提了一下.现在把这三个方法同一放到这里来讲,因为这三个方法都涉及到多线程的一个知识点----中断机制. Java没有提供一种安全.直接的方法来停止某个线程,而是提供了中断机制.中断机制是一种协作机制,也就是说通过中断并不能直接终止另一个线程,而需要被中断的线程自己处理.有个例子举个蛮好,就像父母叮嘱出门在外的子女要注意身体一样,父母说了,但是子女

  • Java多线程及分布式爬虫架构原理解析

    这是 Java 爬虫系列博文的第五篇,在上一篇Java 爬虫服务器被屏蔽的解决方案中,我们简单的聊反爬虫策略和反反爬虫方法,主要针对的是 IP 被封及其对应办法.前面几篇文章我们把爬虫相关的基本知识都讲的差不多啦.这一篇我们来聊一聊爬虫架构相关的内容. 前面几章内容我们的爬虫程序都是单线程,在我们调试爬虫程序的时候,单线程爬虫没什么问题,但是当我们在线上环境使用单线程爬虫程序去采集网页时,单线程就暴露出了两个致命的问题: 采集效率特别慢,单线程之间都是串行的,下一个执行动作需要等上一个执行完才能

  • Java多线程中的wait/notify通信模式实例详解

    前言 最近在看一些JUC下的源码,更加意识到想要学好Java多线程,基础是关键,比如想要学好ReentranLock源码,就得掌握好AQS源码,而AQS源码中又有很多Java多线程经典的一些应用:再比如看了线程池的核心源码实现,又学到了很多核心实现,其实这些都可以提出来慢慢消化并变成自己的知识点,今天这个Java等待/通知模式其实是Thread.join()实现的关键,还有线程池工作线程中线程跟线程之间的通信的核心所在,故在此为了加深理解,做此记录! 参考资料<Java并发编程艺术>(电子PD

  • java多线程join()方法的作用和实现原理解析(应用场景)

    1.join() 方法的作用 这个方法的作用是先将当前线程挂起,待其他线程结束后在执行当前线程的代码: 2.应用场景 比如有三个人小红.小李.小王, 三个人相约一起去酒店吃饭,菜已经点好了, 三个人从不同的地方出发,只有三个人都到了酒店之后才会开始上菜:那么这三个人就分别代表三个线程,这三个线程执行完之后才会执行 "上菜" 的代码逻辑, 代码示例 package com.Lock; /** * join方法示例 * 比如有三个人小红.小李.小王, 三个人相约一起去酒店吃饭,菜已经点好了

随机推荐