Java学习篇 -- 多线程笔记(带你看不一样的多线程~)

简介: Java学习篇 -- 多线程笔记(带你看不一样的多线程~)

Java多线程详解

基本概念

  1. 程序
    保存在电脑硬盘上的可执行文件叫做程序
  2. 进程
    在计算机内存中运行的程序,叫做进程
  3. 线程
    在进程中 运行的程序叫做线程
  4. 单线程
    单线程好比就是一个主线程,1个窗口排队买票
    在java中,执行main方法的线程,成为主线程
  5. 多线程
    在进程内部,同时运行多个程序,在java中体现在同时执行多个方法,多线程就是同一时间干不同的事情。例如:
    小明上班打卡时,电话突然响了,边接电话边打卡,这就是多线程。
    多线程的好处:
    可以提高CPU的执行效率,充分利用了CPU的资源,带来良好的用户体验
  6. 并发与并行
    并行:多个CPU同时执行多个任务。比如:多个人同时做不同的事。

并发:一个CPU(采用时间片)同时执行多个任务。比如:多个人做同一件事。
多线程的应用场景:
1.高并发
2.后台处理
3.大任务

多线程的创建与启动

创建线程的四种方式:
方式1:继承Thread类实现多线程

需求:使用多线程技术打印奇数、偶数,其中一个线程打印奇数,另一个线程打印偶数,这两个线程同时运行。

  1. 自定义一个类,并继承java.lang.Thread类
  2. 重写run方法
  3. 调用start方法开启多线程
public class TestJiAndOu {

    public static void main(String[] args) {
        EvenThread even = new EvenThread();
        OddThread odd = new OddThread();
        //注意:需要同时调用start方法,不可创建后,立即调用,这样会达不到多线程
        even.start();
        odd.start();
    }
}

/**
 * 
 * 打印1-100之间的偶数
 */
class EvenThread extends Thread {
    
    @Override
    public void run() {
        for (int i = 1; i <= 100; i++) {
            if (i % 2 == 0) {
                System.out.println("偶数" + i);
            }
        }
    }
}

/**
 * 
 * 打印1-100之间的奇数
 */
class OddThread extends Thread {
    
    @Override
    public void run() {
        for (int i = 1; i <= 100; i++) {
            if (i % 2 != 0) {
                System.out.println("奇数" + i);
            }
        }
    }
}

多线程执行流程

  1. 执行main方法的线程,称为主线程,执行run方法的线程,称为子线程
  2. 执行start方法之前,只执行一次主线程,当调用start方法之后,线程数瞬间由1个变成2个,其中主线程继续执行main方法,然后新创建的子线程执行run方法
  3. main方法执行完毕,主线程结束,run方法执行完毕,子线程结束
Thread类常用方法:
  1. Thread() 创建新对象
  2. Thread(String threadname) 指定线程名称创建线程
  3. void start() 启动线程
  4. run() 线程的主要实现业务在该方法内实现
  5. getName() 返回线程的名称
  6. setName() 设置线程名称
  7. static void sleep(long milis) 让当前线程指定毫秒数休眠
  8. static Thread currentThread() 获得当前线程
  9. setPriority(int newPriority) 更改线程的优先级
  10. void join() 等待该线程终止
  11. void interrupt() 中断线程
  12. booleans isAlive() 测试线程是否出于活动状态

Thread类方法练习

public class ThreadTest {

    public static void main(String[] args) {
        //创建线程,并调用start方法启动多线程
        new Thread(new Thread1()).start();
        //将主线程优先级调为最大,但不会保证一定会是主线程执行
        Thread.currentThread().setPriority(Thread.MAX_PRIORITY);
        for (int i = 1; i <= 10; i++) {
            System.out.println(Thread.currentThread().getName() + ":" + i);
            if (i == 5) {
                //当i==5时等待主线程终止
                try {
                    Thread.currentThread().join();
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        }
    }
}

class Thread1 extends Thread {
    
    @Override
    public void run() {
        for (int i = 1; i <= 10; i++) {
            System.out.println(i);
            //当i==8时终止线程,判断线程是否存活
//            if (i == 8) {
//                Thread.currentThread().interrupt();
//                System.out.println(Thread.currentThread().getName() + " - :线程终止");
//                boolean flag = Thread.currentThread().isAlive();
//                System.out.println(Thread.currentThread().getName() + "是否存活:" + flag);
//            }
            //每循环一次休眠100毫秒
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }
}

龟兔赛跑小练习

需求:编写龟兔赛跑多线程程序,设赛跑长度为30米
兔子每跑完10米休眠10秒
乌龟跑完20米休眠1秒
乌龟和兔子每跑完1米输出一次结果,看看最后谁先跑完

public class Test {

    public static void main(String[] args) {
        Rabbit rabbit = new Rabbit();
        Tortoise tortoise = new Tortoise();
        rabbit.setName("兔子");
        tortoise.setName("乌龟");
        rabbit.start();
        tortoise.start();
    }
}

class Rabbit extends Thread {
    
    @Override
    public void run() {
        for (int i = 1; i <= 30; i++) {
            if (i == 30) {
                System.out.println(Thread.currentThread().getName() + "跑完了");
                break;
            }
            System.out.println(Thread.currentThread().getName() + "跑了" + i + "米");
            if (i % 10 == 0) {
                System.out.println(Thread.currentThread().getName() + "开始休眠");
                try {
                    Thread.sleep(10000);
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + "休眠完毕,开始跑了");
            }
        }
    }
}

class Tortoise extends Thread {
    
    @Override
    public void run() {
        for (int i = 1; i <= 30; i++) {
            if (i == 30) {
                System.out.println(Thread.currentThread().getName() + "跑完了");
                break;
            }
            System.out.println(Thread.currentThread().getName() + "跑了" + i + "米");
            if (i % 20 == 0) {
                System.out.println(Thread.currentThread().getName() + "开始休眠");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + "休眠完毕,开始跑了");
            }
        }
    }
}
方式2:实现Runnable接口实现多线程
public class ThreadTest {

    public static void main(String[] args) {
        Thread1 t = new Thread1();
        new Thread(t).start();
    }
}

class Thread1 implements Runnable{

    @Override
    public void run() {
        for (int i = 1; i <= 10; i++) {
            System.out.println(i);
        }
    }
}
方式3:实现Callable+FutureTask实现多线程

实现Callable接口的方式可以将子线程的结果返回到主线程,也可以处理异常
步骤:

  1. 创建一个类,实现Callable接口
  2. 重写call方法,编写多线程代码,并返回结果
  3. 创建FutrueTask对象,并将Callable接口的实现类对象传递到FutrueTask构造方法
  4. 创建Thread对象,并将FutrueTask对象传递到构造方法,并调用start方法
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

public class ThreadTest {

    public static void main(String[] args) throws InterruptedException, ExecutionException {
        CallableTest call = new CallableTest();
        FutureTask<Integer> ftask = new FutureTask<>(call);
        new Thread(ftask).start();
        Integer i = ftask.get();
        System.out.println("返回的结果为:" + i);
    }
}

class CallableTest implements Callable<Integer> {
    
    @Override
    public Integer call() throws Exception {
        int sum = 0;
        for (int i = 1; i <= 10; i++) {
            System.out.println(i);
            sum += i;
            Thread.sleep(100);
        }
        return sum;
    }
    
}    

运行结果如下:
在这里插入图片描述
使用Callable实现多线程会返回一个指定类型的结果,get方法获得返回的具体数据。

方式4:线程池实现多线程

思路:
可以先初始化一个线程池,需要时从线程池中取出线程执行异步任务,使用完再归还到线程池,这样可以避免频繁的创建、销毁线程,从而提升系统的性能

步骤:

  1. 初始化一个线程池,并指定线程个数【通常是CPU内核数*2】
  2. 从线程池中取出线程执行异步任务
public class ThreadTest {

    public static void main(String[] args) throws InterruptedException, ExecutionException {
        //初始化线程池,创建方式为指定线程池数量创建
        ExecutorService pool = Executors.newFixedThreadPool(15);
        for (int i = 1; i <= 10; i++) {
            CallableTest call = new CallableTest("callable" + i);
            Future<Integer> ft = pool.submit(call);
            System.out.println(call.getName() + "随机到的数字 :" + ft.get());
        }
        pool.shutdown();
    }
}

class CallableTest implements Callable<Integer> {
    
    private String name;
    
    public CallableTest(String name) {
        this.name = name;
    }

    @Override
    public Integer call() throws Exception {
        //当前线程睡一会在执行
        Thread.sleep(100);
        //返回一个10-100的整数
        return (int)(Math.random()*90)+10;
    }
    
    public String getName() {
        return name;
    }
}

原生方式创建线程池:

核心7个参数:
corePoolSize:核心线程池的数量,初始化线程池时创建的线程个数
maximumPoolSize,线程池中最多创建多少个线程
keepAliveTime,保持存活的时间,异步任务执行完毕后,等待多长时间销毁多余的线程
unit,存活时间单位,例如:时、分、秒
workQueue,工作队列/阻塞队列,如果任务有很多,就会将多余的的任务放到队列里面,只要有线程空闲,就会去队列里面取出新的任务执行,通常使用 LinkedBlockingQueue(无限队列)
threadFactory,创建线程的工厂,采用默认值{Executors.defaultThreadFactory}
handler,阻塞队列满了,按照我们指定的拒绝策略拒绝执行任务
//初始化线程池,创建方式为指定线程池数量创建
    public static ThreadPoolExecutor pool = 
            new ThreadPoolExecutor(
                //核心线程池的数量
                5,
                //最大线程能开到多少
                100,
                //线程空闲后,多开的线程多长时间休眠
                10,
                //线程休眠的时间单位
                TimeUnit.SECONDS,
                //工作/阻塞队列,一旦核心线程放满后,多余的任务会放进该队列,采用链表阻塞队列
                new LinkedBlockingDeque<>(100),
                //默认工厂模式
                Executors.defaultThreadFactory(),
                //阻塞队列满后,采用拒绝策略拒绝服务
                new ThreadPoolExecutor.AbortPolicy()
            );
    
    public static void main(String[] args) throws InterruptedException, ExecutionException {
        
        for (int i = 1; i <= 10; i++) {
            CallableTest call = new CallableTest("callable" + i);
            Future<Integer> ft = pool.submit(call);
            System.out.println(call.getName() + "随机到的数字 :" + ft.get());
        }
        pool.shutdown();
    }
}

class CallableTest implements Callable<Integer> {
    
    private String name;
    
    public CallableTest(String name) {
        this.name = name;
    }

    @Override
    public Integer call() throws Exception {
        //当前线程睡一会在执行
        Thread.sleep(100);
        //返回一个10-100的整数
        return (int)(Math.random()*90)+10;
    }
    
    public String getName() {
        return name;
    }
}

线程安全

当多个线程操作同一个共享数据时,当一个线程还未结束时,其他的线程参与进去,此时就会导致共享数据的不一致。
例:

public class ThreadTest {

    //初始化线程池,创建方式为指定线程池数量创建
    public static ThreadPoolExecutor pool = 
            new ThreadPoolExecutor(
                //核心线程池的数量
                5,
                //最大线程能开到多少
                100,
                //线程空闲后,多开的线程多长时间休眠
                10,
                //线程休眠的时间单位
                TimeUnit.SECONDS,
                //工作/阻塞队列,一旦核心线程放满后,多余的任务会放进该队列,采用链表阻塞队列
                new LinkedBlockingDeque<>(100),
                //默认工厂模式
                Executors.defaultThreadFactory(),
                //阻塞队列满后,采用拒绝策略拒绝服务
                new ThreadPoolExecutor.AbortPolicy()
            );
    
    public static void main(String[] args) throws InterruptedException, ExecutionException {
        
        CallableTest call = new CallableTest();
        Future<Integer> ft = null;
        for (int i = 1; i <= 100; i++) {
            ft = pool.submit(call);
        }
        System.out.println(ft.get());
        
        pool.shutdown();
    }
}

class CallableTest implements Callable<Integer> {
    

    private int num = 0;
    
    @Override
    public Integer call() throws Exception {
        for (int i = 1; i <= 100; i++) {
            num ++;
        }
        return num;
    }
}

结果应该是10000,因为多个线程同时去改变资源,导致数字不是10000,共有2个问题导致数字不对:

  1. 主线程提前运行,不管子线程是否执行完毕
  2. 子线程互相争夺资源,导致资源不一致,A线程取出数字,改变数字后想要放进去,但B线程已经拿走了A线程未改变之前的值,A线程改变完后放入,B线程取出改变后放入,这样就看起来是1一个线程改变了值,所以说原本是3,结果确是2,线程多了,资源也就差的越来越多。

改变:
利用synchronized同步代码块
利用synchronized同步方法
利用Lock显示锁

synchronized隐式锁实现线程安全

public class ThreadTest {

    //初始化线程池,创建方式为指定线程池数量创建
    public static ThreadPoolExecutor pool = 
            new ThreadPoolExecutor(
                //核心线程池的数量
                5,
                //最大线程能开到多少
                100,
                //线程空闲后,多开的线程多长时间休眠
                10,
                //线程休眠的时间单位
                TimeUnit.SECONDS,
                //工作/阻塞队列,一旦核心线程放满后,多余的任务会放进该队列,采用链表阻塞队列
                new LinkedBlockingDeque<>(100),
                //默认工厂模式
                Executors.defaultThreadFactory(),
                //阻塞队列满后,采用拒绝策略拒绝服务
                new ThreadPoolExecutor.AbortPolicy()
            );
    
    public static void main(String[] args) throws InterruptedException, ExecutionException {
        
        CallableTest call = new CallableTest();
        List<Future<Integer>> fts = new ArrayList<>();
        for (int i = 1; i <= 100; i++) {
            Future<Integer> ft = pool.submit(call);
            fts.add(ft);
        }
        int num = -1;
        for (Future<Integer> future : fts) {
            num = future.get() > num ? future.get() : num;
        }
        System.out.println(num);
        pool.shutdown();
    }
}

class CallableTest implements Callable<Integer> {
    

    private int num = 0;
    
    
    @Override
    public Integer call() throws Exception {
        for (int i = 1; i <= 100; i++) {
            //同步代码块实现线程同步
//            synchronized (this) {
//                num ++;
//            }
            intPlus();
        }
        return num;
    }
    
    //同步方法实现线程同步
    public synchronized void intPlus() {
        num++;
    }
}

Lock锁显示锁实现线程安全

public class ThreadTest {

    //初始化线程池,创建方式为指定线程池数量创建
    public static ThreadPoolExecutor pool = 
            new ThreadPoolExecutor(
                //核心线程池的数量
                5,
                //最大线程能开到多少
                100,
                //线程空闲后,多开的线程多长时间休眠
                10,
                //线程休眠的时间单位
                TimeUnit.SECONDS,
                //工作/阻塞队列,一旦核心线程放满后,多余的任务会放进该队列,采用链表阻塞队列
                new LinkedBlockingDeque<>(100),
                //默认工厂模式
                Executors.defaultThreadFactory(),
                //阻塞队列满后,采用拒绝策略拒绝服务
                new ThreadPoolExecutor.AbortPolicy()
            );
    
    public static void main(String[] args) throws InterruptedException, ExecutionException {
        
        CallableTest call = new CallableTest();
        List<Future<Integer>> fts = new ArrayList<>();
        for (int i = 1; i <= 100; i++) {
            Future<Integer> ft = pool.submit(call);
            fts.add(ft);
        }
        int num = -1;
        for (Future<Integer> future : fts) {
            num = future.get() > num ? future.get() : num;
        }
        System.out.println(num);
        pool.shutdown();
    }
}

class CallableTest implements Callable<Integer> {
    

    private int num = 0;
    
    //该类实现于Lock接口,是Lock接口的实现类,一般使用该类进行开锁及关锁操作
    ReentrantLock lock = new ReentrantLock();
    
    @Override
    public Integer call() throws Exception {
        for (int i = 1; i <= 100; i++) {
            lock.lock();
            //为什么要加上try/finally呢?
            //因为在开锁之后有可能会发生异常导致当前线程一直占用资源不释放,不加的话后面的代码不会被执行
            //加上try/finally后不管有没有异常都能正常执行关锁操作,不会导致一个线程一直占用资源发生死锁
            try {
                num++;
            } finally {
                lock.unlock();
            }
            //同步代码块实现线程同步
//            synchronized (this) {
//                num ++;
//            }
//            intPlus();
        }
        return num;
    }
    
    //同步方法实现线程同步
//    public synchronized void intPlus() {
//        num++;
//    }
}

线程通信

线程通信:线程通信就是A线程满足一定条件后通知B线程执行操作。
例子:生产者与消费者模型
生产者生产好产品后通知消费这消费。

wait让当前线程进入阻塞状态,同时会释放锁

线程通信用到的3个方法
wait() 一旦执行此方法,当前线程就进入阻塞状态,并释放同步监视器。
notify():一旦执行此方法,就会唤醒被wait的一个线程。如果有多个线程被wait,就唤醒优先级高的那个。
notifyAll():一旦执行此方法,就会唤醒所有被wait的线程。
说明:
1.wait(),notify(),notifyAll()三个方法必须使用在同步代码块或同步方法中。
2.wait(),notify(),notifyAll()三个方法的调用者必须是同步代码块或同步方法中的同步监视器。
否则,会出现IllegalMonitorStateException异常

3.wait(),notify(),notifyAll()三个方法是定义在java.lang.Object类中。

上题:
商城有100个商品上架,等待买家购买
假设买家100个,需要让每个买家买到的商品都不一样,买家买到商品后直接走,限购1份

//采用链表阻塞队列实现商家上架商品与买家购买商品
    //方法:add()表示向队列中添加元素,poll()表示从队列取出元素
    static Queue<Integer> duilie = new LinkedBlockingDeque<Integer>();
    
    public static void main(String[] args) {
        ExecutorService pool = Executors.newCachedThreadPool();
        
        EMallRun emall = new EMallRun();
        for (int i = 1; i <= 100; i++) {
            Customor customor = new Customor("customor" + i);
            pool.submit(customor);
        }
        pool.submit(emall);
        pool.shutdown();
    }
}

class EMallRun implements Runnable {

    @Override
    public void run() {
        for (int i = 1; i <= 100; i++) {
            synchronized (StoreTest.duilie) {
                StoreTest.duilie.add(i);
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
                System.out.println("商家已上货,等待买家购买...");
                //通知买家有货,可以购买
                StoreTest.duilie.notify();
            }
        }
    }

}

class Customor implements Runnable {

    private String num;
    
    public Customor(String num) {
        super();
        this.num = num;
    }

    @Override
    public void run() {
        synchronized (StoreTest.duilie) {
            try {
                System.out.println("等待商家上货");
                //阻塞当前线程,并释放同步监听器
                StoreTest.duilie.wait();
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            //获取队列中的商品
            Integer i = StoreTest.duilie.poll();
            if (i != null) {
                System.out.println(num + "买到第" + i + "个商品");
            }
        }
    }
}

再来道题巩固下:
基于生产/消费模式实现网上商城抢货模式
网上商城推出高性价比产品,
10个买家来抢货,
商城共放出100个产品
最后输出每个买家抢到的产品数量

思路:商城推出商品后,通知全体买家有货,让所有买家一块来抢货,谁抢到是谁的,抢到后,商城继续上架,直到上架产品到100不再上架,抢货结束

import java.util.ArrayList;
import java.util.List;
import java.util.Queue;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.LinkedBlockingDeque;


public class MallTest2 {

    public static void main(String[] args) {
        //创建线程池
        ExecutorService pool = Executors.newFixedThreadPool(15);
        
        EMallRun2 emall = new EMallRun2();
        
        //实例化顾客集合,为了好获取数据
        List<CustomorRun2> customorRuns = new ArrayList<>();
        //实例化顾客的返回值集合,为了将排出来的线程有序
        List<Future<Boolean>> futures = new ArrayList<>();
        for (int i = 1; i <= 10; i++) {
            //实例化线程,并设置名称
            CustomorRun2 customorRun = new CustomorRun2("customor" + i);
            //放入顾客集合
            customorRuns.add(customorRun);
            //将顾客先提交到线程池,然后接收返回值,
            Future<Boolean> future = pool.submit(customorRun);
            //再将返回的值放入顾客返回值集合中
            futures.add(future);
        }
        //将商城类提交到线程池中
        pool.submit(emall);
        
        //关闭线程池
        pool.shutdown();
        
        //存放总共抢到的数量
        int sumCount = 0;
        
        //循环取出数据
        for (int i = 0; i < 10; i++) {
            //获取当前索引对应的线程的返回值
            try {
                futures.get(i).get();
            } catch (InterruptedException | ExecutionException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            //运行到这里表示已经返回值,从顾客集合中取出顾客
            CustomorRun2 customor = customorRuns.get(i);
            //输出顾客信息
            System.out.println(customor.getCustomorName() + " - 抢了" + customor.getMyBuyedCount() + "个");
            //循环加入每个顾客抢到的商品数量
            sumCount += customor.getMyBuyedCount();
        }
        System.out.println("总共抢了" + sumCount + "个商品");
        
    }
}

class EMallRun2 implements Runnable {

    static Queue<Integer> queue = new LinkedBlockingDeque<Integer>();
    
    //商家总共上架的数量
    static int proCount = 0;
    
    @Override
    public void run() {
        //商家上架100个货物
        for (int i = 1; i <= 100; i++) {
            //进行线程同步,当前线程监听器与买家监听器需相同
            synchronized (queue) {
                //满足要求后,向队列中添加商品
                queue.add(i);
                //当前线程队列通知全体买家已有货物,可以抢货
                queue.notifyAll();
            }
            try {
                Thread.sleep((int)(Math.random()*100)+50);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }
    
}

class CustomorRun2 implements Callable<Boolean> {

    private String customorName;
    
    private int myBuyedCount = 0;
    
    public CustomorRun2(String customorName) {
        super();
        this.customorName = customorName;
    }


    @Override
    public Boolean call() throws Exception {
        //无限循环去抢商品,10个买家去抢购
        while (true) {
            //上锁,数据会不安全,商家队列作为锁的监听器,
            synchronized (EMallRun2.queue) {
                //当商家的货全部上架完毕后,退出循环,抢货结束
                if (EMallRun2.proCount >= 100) {
                    break;
                }
                //当前买家去阻塞线程,同时释放锁,等待商家的线程通知全体线程后,根据CPU调用线程优先级,来决定谁能够抢到
                EMallRun2.queue.wait();
                //获取商家商家的商品数量,是否上架
                Integer i = EMallRun2.queue.poll();
                //上架商品
                if (i != null) {
                    //当前线程抢到货物,
                    System.out.println(customorName + " - 抢到第" + i + "个商品");
                    //抢到货后需改变商家的后台余量,总共上架100个,
                    EMallRun2.proCount ++;
                    //改变当前线程抢到的货物数量
                    myBuyedCount ++;
                }
            }
        }
        //返回一个结果,表示当前线程执行完毕
        return true;
    }

    public String getCustomorName() {
        return customorName;
    }

    public int getMyBuyedCount() {
        return myBuyedCount;
    }
    
}

好了,线程问题到此告一环节,需要掌握知识远不止这一点,代码之路任重道远,我们携手并肩作战!,都看到这里了,给我一个鼓励,一键三连吧~~🤞💕

相关文章
|
2天前
|
Java
Java—多线程实现生产消费者
本文介绍了多线程实现生产消费者模式的三个版本。Version1包含四个类:`Producer`(生产者)、`Consumer`(消费者)、`Resource`(公共资源)和`TestMain`(测试类)。通过`synchronized`和`wait/notify`机制控制线程同步,但存在多个生产者或消费者时可能出现多次生产和消费的问题。 Version2将`if`改为`while`,解决了多次生产和消费的问题,但仍可能因`notify()`随机唤醒线程而导致死锁。因此,引入了`notifyAll()`来唤醒所有等待线程,但这会带来性能问题。
Java—多线程实现生产消费者
|
4天前
|
安全 Java Kotlin
Java多线程——synchronized、volatile 保障可见性
Java多线程中,`synchronized` 和 `volatile` 关键字用于保障可见性。`synchronized` 保证原子性、可见性和有序性,通过锁机制确保线程安全;`volatile` 仅保证可见性和有序性,不保证原子性。代码示例展示了如何使用 `synchronized` 和 `volatile` 解决主线程无法感知子线程修改共享变量的问题。总结:`volatile` 确保不同线程对共享变量操作的可见性,使一个线程修改后,其他线程能立即看到最新值。
|
4天前
|
消息中间件 缓存 安全
Java多线程是什么
Java多线程简介:本文介绍了Java中常见的线程池类型,包括`newCachedThreadPool`(适用于短期异步任务)、`newFixedThreadPool`(适用于固定数量的长期任务)、`newScheduledThreadPool`(支持定时和周期性任务)以及`newSingleThreadExecutor`(保证任务顺序执行)。同时,文章还讲解了Java中的锁机制,如`synchronized`关键字、CAS操作及其实现方式,并详细描述了可重入锁`ReentrantLock`和读写锁`ReadWriteLock`的工作原理与应用场景。
|
5天前
|
安全 Java 编译器
深入理解Java中synchronized三种使用方式:助您写出线程安全的代码
`synchronized` 是 Java 中的关键字,用于实现线程同步,确保多个线程互斥访问共享资源。它通过内置的监视器锁机制,防止多个线程同时执行被 `synchronized` 修饰的方法或代码块。`synchronized` 可以修饰非静态方法、静态方法和代码块,分别锁定实例对象、类对象或指定的对象。其底层原理基于 JVM 的指令和对象的监视器,JDK 1.6 后引入了偏向锁、轻量级锁等优化措施,提高了性能。
21 3
|
5天前
|
存储 安全 Java
Java多线程编程秘籍:各种方案一网打尽,不要错过!
Java 中实现多线程的方式主要有四种:继承 Thread 类、实现 Runnable 接口、实现 Callable 接口和使用线程池。每种方式各有优缺点,适用于不同的场景。继承 Thread 类最简单,实现 Runnable 接口更灵活,Callable 接口支持返回结果,线程池则便于管理和复用线程。实际应用中可根据需求选择合适的方式。此外,还介绍了多线程相关的常见面试问题及答案,涵盖线程概念、线程安全、线程池等知识点。
58 2
|
5天前
|
NoSQL Redis
单线程传奇Redis,为何引入多线程?
Redis 4.0 引入多线程支持,主要用于后台对象删除、处理阻塞命令和网络 I/O 等操作,以提高并发性和性能。尽管如此,Redis 仍保留单线程执行模型处理客户端请求,确保高效性和简单性。多线程仅用于优化后台任务,如异步删除过期对象和分担读写操作,从而提升整体性能。
17 1
|
13天前
|
安全 Java API
java如何请求接口然后终止某个线程
通过本文的介绍,您应该能够理解如何在Java中请求接口并根据返回结果终止某个线程。合理使用标志位或 `interrupt`方法可以确保线程的安全终止,而处理好网络请求中的各种异常情况,可以提高程序的稳定性和可靠性。
44 6
|
21天前
|
安全 算法 Java
Java多线程编程中的陷阱与最佳实践####
本文探讨了Java多线程编程中常见的陷阱,并介绍了如何通过最佳实践来避免这些问题。我们将从基础概念入手,逐步深入到具体的代码示例,帮助开发者更好地理解和应用多线程技术。无论是初学者还是有经验的开发者,都能从中获得有价值的见解和建议。 ####
|
21天前
|
Java 调度
Java中的多线程编程与并发控制
本文深入探讨了Java编程语言中多线程编程的基础知识和并发控制机制。文章首先介绍了多线程的基本概念,包括线程的定义、生命周期以及在Java中创建和管理线程的方法。接着,详细讲解了Java提供的同步机制,如synchronized关键字、wait()和notify()方法等,以及如何通过这些机制实现线程间的协调与通信。最后,本文还讨论了一些常见的并发问题,例如死锁、竞态条件等,并提供了相应的解决策略。
45 3
|
7月前
|
存储 安全 Java
深入理解Java并发编程:线程安全与锁机制
【5月更文挑战第31天】在Java并发编程中,线程安全和锁机制是两个核心概念。本文将深入探讨这两个概念,包括它们的定义、实现方式以及在实际开发中的应用。通过对线程安全和锁机制的深入理解,可以帮助我们更好地解决并发编程中的问题,提高程序的性能和稳定性。