精华内容
下载资源
问答
  • Java多线程

    千次阅读 2021-01-21 15:16:40
    目录 一、实验目的 二、实验代码 1. 通过继承Thread类的方法创建两个线程,在Thread构造方法中指定线程的名称,并将这两个线程的名字打印出来。 2. 通过实现Runnable接口的...1. 掌握Java多线程的创建及其启动..

    目录

    一、实验目的

    二、实验代码

    1. 通过继承Thread类的方法创建两个线程,在Thread构造方法中指定线程的名称,并将这两个线程的名字打印出来。

    2. 通过实现Runnable接口的方法创建一个新线程,要求main线程打印100次“main”,新线程打印50次“new”。

    3. 模拟三个老师同时发80份学习笔记本,每次只发放一份笔记本,每个老师相当于一个线程。

    4. 编写如图6-1所示的界面,当程序运行时:

    每文一语


     

     

    一、实验目的

    1. 掌握Java多线程的创建及其启动,多线程的两种常用创建方式及其区别;

    2. 掌握多线程的生命周期及五种基本状态,分别是新建状态(New)、就绪状态(Runnable)、运行状态(Running)、阻塞状态(Blocked)、死亡状态(Dead);

    3. 掌握引起Java线程阻塞的主要方法,如:jion()方法、sleep()方法、yeild()方法;

    4. 掌握线程安全及其解决机制,如:同步方法、同步代码块等。

    二、实验代码

    1. 通过继承Thread类的方法创建两个线程,在Thread构造方法中指定线程的名称,并将这两个线程的名字打印出来。

    package 作业练习.test6;
    public class Study_1 extends Thread {
        public Study_1(String name) {
            super(name);
        }
        public void run() {
            System.out.println(this.getName());
        }
        public static void main(String[] args) {
            new Study_1("Thread1").start();
            new Study_1("Thread2").start();
        }
    }
    

    2. 通过实现Runnable接口的方法创建一个新线程,要求main线程打印100次“main”,新线程打印50次“new”。

    package 作业练习.test6;
    
    public class Study_2 implements Runnable {
        public void run() {
            for (int i = 0; i < 50; i++) {
                System.out.println("new");
            }
        }
        public static void main(String[] args) {
            new Thread(new Study_2()).start();
            for (int i = 0; i < 100; i++) {
                System.out.println("main");
            }
        }
    }
    

    3. 模拟三个老师同时发80份学习笔记本,每次只发放一份笔记本,每个老师相当于一个线程。

    package 作业练习.test6;
    public class Study_3 implements Runnable {
        private int number = 80;  //总共80份学习笔记
        private int count = 0;    //发第几份
        @Override
        public void run() {
            while (true) {
                synchronized (this) {
                    if (number <= 0) {
                        break;
                    }
                    try {
                        Thread.sleep(500);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    number--;
                    count++;
                    System.out.println(Thread.currentThread().getName() + "正在发第" + count + "份学习笔记,还有" + number + "份学习笔记。");
                }
            }
        }
        public static void main(String[] args) {
            Study_3 homeWork3 = new Study_3();
            new Thread(homeWork3, "老师甲").start();
            new Thread(homeWork3, "老师乙").start();
            new Thread(homeWork3, "老师丙").start();
        }
    }
    

    4. 编写如图6-1所示的界面,当程序运行时:

    1)每隔两秒钟在显示字母区域随机显示一个字母(如图所示,显示的字母是“g”);

    2)用户在文本框中使用输入相应的字母,如果输入正确则得1分,否则得0分;

    3)并将用户的得分累计显示在的得分栏。

    package 作业练习.test6;
    
    import javafx.application.Application;
    import javafx.event.EventHandler;
    import javafx.scene.input.KeyCode;
    import javafx.stage.Stage;
    
    import java.awt.*;
    import java.awt.event.KeyEvent;
    
    public class Text4 extends Application {
    
        public static void main(String[] args) {
            launch(args);
        }
    
        @Override
        public void start(Stage stage) {
            Label l1 = new Label("显示字母:");
            TextField l2 = new TextField("A");
            l2.setEditable(false);
            l2.setPrefWidth(30.0);//文本框大小
            Label l3 = new Label("输入所显示的字母(回车)");
            TextField l4 = new TextField();
            Label l5 = new Label("得分");
            Label l6 = new Label("0");
            new Thread(new Runnable() {
                @Override
                public void run() {
                    while(true){
                        char str;
                        str = (char)(Math.random()*26+'a');
                        try {
                            Thread.sleep(2000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        String s = String.valueOf(str);
                        l2.setText(s);
                    }
                }
            }).start();
    
            l4.setOnKeyPressed(new EventHandler<KeyEvent>() {
                @Override
                public void handle(KeyEvent e) {
                    if (e.getCode()== KeyCode.ENTER){
                        String text1 = l2.getText();
                        String text2 = l4.getText();
                        String text3 = l6.getText();
                        int i = Integer.parseInt(text3);
                        if (text1.equals(text2)){
                            i+=1;
                            l6.setText(String.valueOf(i));
    
                        }
                    }
                    l4.clear();
                }
            });
    
    
            FlowPane flowPane = new FlowPane();    //添加面板
            flowPane.getChildren().addAll(l1,l2,l3,l4,l5,l6);    //吧空控件添加到面板上
            Scene scene = new Scene(flowPane);   //创建场景
            stage.setScene(scene);   //吧场景添加到舞台
            stage.setWidth(900);
            stage.show();
    
        }
    }
    

    每文一语

    有些东西,只有有了遗憾才会懂得珍惜!

    展开全文
  • Java多线程:彻底搞懂线程池

    万次阅读 多人点赞 2019-07-09 19:27:00
    熟悉Java多线程编程的同学都知道,当我们线程创建过多时,容易引发内存溢出,因此我们就有必要使用线程池的技术了。 目录 1 线程池的优势 2 线程池的使用 3 线程池的工作原理 4 线程池的参数 4.1 任务队列...

    熟悉Java多线程编程的同学都知道,当我们线程创建过多时,容易引发内存溢出,因此我们就有必要使用线程池的技术了。

    目录

    1 线程池的优势

    2 线程池的使用

    3 线程池的工作原理

    4 线程池的参数

    4.1 任务队列(workQueue)

    4.2 线程工厂(threadFactory)

    4.3 拒绝策略(handler)

    5 功能线程池

    5.1 定长线程池(FixedThreadPool)

    5.2 定时线程池(ScheduledThreadPool )

    5.3 可缓存线程池(CachedThreadPool)

    5.4 单线程化线程池(SingleThreadExecutor)

    5.5 对比

    6 总结

    参考


    1 线程池的优势

    总体来说,线程池有如下的优势:

    (1)降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。

    (2)提高响应速度。当任务到达时,任务可以不需要等到线程创建就能立即执行。

    (3)提高线程的可管理性。线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配,调优和监控。

    2 线程池的使用

    线程池的真正实现类是ThreadPoolExecutor,其构造方法有如下4种:

    public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue) {
        this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
             Executors.defaultThreadFactory(), defaultHandler);
    }
    
    public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory) {
        this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
             threadFactory, defaultHandler);
    }
    
    public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              RejectedExecutionHandler handler) {
        this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
             Executors.defaultThreadFactory(), handler);
    }
    
    public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler) {
        if (corePoolSize < 0 ||
            maximumPoolSize <= 0 ||
            maximumPoolSize < corePoolSize ||
            keepAliveTime < 0)
            throw new IllegalArgumentException();
        if (workQueue == null || threadFactory == null || handler == null)
            throw new NullPointerException();
        this.corePoolSize = corePoolSize;
        this.maximumPoolSize = maximumPoolSize;
        this.workQueue = workQueue;
        this.keepAliveTime = unit.toNanos(keepAliveTime);
        this.threadFactory = threadFactory;
        this.handler = handler;
    }

    可以看到,其需要如下几个参数:

    • corePoolSize(必需):核心线程数。默认情况下,核心线程会一直存活,但是当将allowCoreThreadTimeout设置为true时,核心线程也会超时回收。
    • maximumPoolSize(必需):线程池所能容纳的最大线程数。当活跃线程数达到该数值后,后续的新任务将会阻塞。
    • keepAliveTime(必需):线程闲置超时时长。如果超过该时长,非核心线程就会被回收。如果将allowCoreThreadTimeout设置为true时,核心线程也会超时回收。
    • unit(必需):指定keepAliveTime参数的时间单位。常用的有:TimeUnit.MILLISECONDS(毫秒)、TimeUnit.SECONDS(秒)、TimeUnit.MINUTES(分)。
    • workQueue(必需):任务队列。通过线程池的execute()方法提交的Runnable对象将存储在该参数中。其采用阻塞队列实现。
    • threadFactory(可选):线程工厂。用于指定为线程池创建新线程的方式。
    • handler(可选):拒绝策略。当达到最大线程数时需要执行的饱和策略。

    线程池的使用流程如下:

    // 创建线程池
    ThreadPoolExecutor threadPool = new ThreadPoolExecutor(CORE_POOL_SIZE,
                                                 MAXIMUM_POOL_SIZE,
                                                 KEEP_ALIVE,
                                                 TimeUnit.SECONDS,
                                                 sPoolWorkQueue,
                                                 sThreadFactory);
    // 向线程池提交任务
    threadPool.execute(new Runnable() {
        @Override
        public void run() {
            ... // 线程执行的任务
        }
    });
    // 关闭线程池
    threadPool.shutdown(); // 设置线程池的状态为SHUTDOWN,然后中断所有没有正在执行任务的线程
    threadPool.shutdownNow(); // 设置线程池的状态为 STOP,然后尝试停止所有的正在执行或暂停任务的线程,并返回等待执行任务的列表

    3 线程池的工作原理

    下面来描述一下线程池工作的原理,同时对上面的参数有一个更深的了解。其工作原理流程图如下:

    通过上图,相信大家已经对所有参数有个了解了。下面再对任务队列、线程工厂和拒绝策略做更多的说明。

    4 线程池的参数

    4.1 任务队列(workQueue)

    任务队列是基于阻塞队列实现的,即采用生产者消费者模式,在Java中需要实现BlockingQueue接口。但Java已经为我们提供了7种阻塞队列的实现:

    1. ArrayBlockingQueue:一个由数组结构组成的有界阻塞队列(数组结构可配合指针实现一个环形队列)。
    2. LinkedBlockingQueue: 一个由链表结构组成的有界阻塞队列,在未指明容量时,容量默认为Integer.MAX_VALUE
    3. PriorityBlockingQueue: 一个支持优先级排序的无界阻塞队列,对元素没有要求,可以实现Comparable接口也可以提供Comparator来对队列中的元素进行比较。跟时间没有任何关系,仅仅是按照优先级取任务。
    4. DelayQueue:类似于PriorityBlockingQueue,是二叉堆实现的无界优先级阻塞队列。要求元素都实现Delayed接口,通过执行时延从队列中提取任务,时间没到任务取不出来。
    5. SynchronousQueue: 一个不存储元素的阻塞队列,消费者线程调用take()方法的时候就会发生阻塞,直到有一个生产者线程生产了一个元素,消费者线程就可以拿到这个元素并返回;生产者线程调用put()方法的时候也会发生阻塞,直到有一个消费者线程消费了一个元素,生产者才会返回。
    6. LinkedBlockingDeque: 使用双向队列实现的有界双端阻塞队列。双端意味着可以像普通队列一样FIFO(先进先出),也可以像栈一样FILO(先进后出)。
    7. LinkedTransferQueue: 它是ConcurrentLinkedQueueLinkedBlockingQueueSynchronousQueue的结合体,但是把它用在ThreadPoolExecutor中,和LinkedBlockingQueue行为一致,但是是无界的阻塞队列。

    注意有界队列和无界队列的区别:如果使用有界队列,当队列饱和时并超过最大线程数时就会执行拒绝策略;而如果使用无界队列,因为任务队列永远都可以添加任务,所以设置maximumPoolSize没有任何意义。

    4.2 线程工厂(threadFactory)

    线程工厂指定创建线程的方式,需要实现ThreadFactory接口,并实现newThread(Runnable r)方法。该参数可以不用指定,Executors框架已经为我们实现了一个默认的线程工厂:

    /**
     * The default thread factory.
     */
    private static class DefaultThreadFactory implements ThreadFactory {
        private static final AtomicInteger poolNumber = new AtomicInteger(1);
        private final ThreadGroup group;
        private final AtomicInteger threadNumber = new AtomicInteger(1);
        private final String namePrefix;
    
        DefaultThreadFactory() {
            SecurityManager s = System.getSecurityManager();
            group = (s != null) ? s.getThreadGroup() :
                                  Thread.currentThread().getThreadGroup();
            namePrefix = "pool-" +
                          poolNumber.getAndIncrement() +
                         "-thread-";
        }
    
        public Thread newThread(Runnable r) {
            Thread t = new Thread(group, r,
                                  namePrefix + threadNumber.getAndIncrement(),
                                  0);
            if (t.isDaemon())
                t.setDaemon(false);
            if (t.getPriority() != Thread.NORM_PRIORITY)
                t.setPriority(Thread.NORM_PRIORITY);
            return t;
        }
    }

    4.3 拒绝策略(handler)

    当线程池的线程数达到最大线程数时,需要执行拒绝策略。拒绝策略需要实现RejectedExecutionHandler接口,并实现rejectedExecution(Runnable r, ThreadPoolExecutor executor)方法。不过Executors框架已经为我们实现了4种拒绝策略:

    1. AbortPolicy(默认):丢弃任务并抛出RejectedExecutionException异常。
    2. CallerRunsPolicy:由调用线程处理该任务。
    3. DiscardPolicy:丢弃任务,但是不抛出异常。可以配合这种模式进行自定义的处理方式。
    4. DiscardOldestPolicy:丢弃队列最早的未处理任务,然后重新尝试执行任务。

    5 功能线程池

    嫌上面使用线程池的方法太麻烦?其实Executors已经为我们封装好了4种常见的功能线程池,如下:

    • 定长线程池(FixedThreadPool)
    • 定时线程池(ScheduledThreadPool )
    • 可缓存线程池(CachedThreadPool)
    • 单线程化线程池(SingleThreadExecutor)

    5.1 定长线程池(FixedThreadPool)

    创建方法的源码:

    public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
    }
    public static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>(),
                                      threadFactory);
    }
    • 特点:只有核心线程,线程数量固定,执行完立即回收,任务队列为链表结构的有界队列。
    • 应用场景:控制线程最大并发数。

    使用示例:

    // 1. 创建定长线程池对象 & 设置线程池线程数量固定为3
    ExecutorService fixedThreadPool = Executors.newFixedThreadPool(3);
    // 2. 创建好Runnable类线程对象 & 需执行的任务
    Runnable task =new Runnable(){
      public void run() {
         System.out.println("执行任务啦");
      }
    };
    // 3. 向线程池提交任务
    fixedThreadPool.execute(task);

    5.2 定时线程池(ScheduledThreadPool )

    创建方法的源码:

    private static final long DEFAULT_KEEPALIVE_MILLIS = 10L;
    
    public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
        return new ScheduledThreadPoolExecutor(corePoolSize);
    }
    public ScheduledThreadPoolExecutor(int corePoolSize) {
        super(corePoolSize, Integer.MAX_VALUE,
              DEFAULT_KEEPALIVE_MILLIS, MILLISECONDS,
              new DelayedWorkQueue());
    }
    
    public static ScheduledExecutorService newScheduledThreadPool(
            int corePoolSize, ThreadFactory threadFactory) {
        return new ScheduledThreadPoolExecutor(corePoolSize, threadFactory);
    }
    public ScheduledThreadPoolExecutor(int corePoolSize,
                                       ThreadFactory threadFactory) {
        super(corePoolSize, Integer.MAX_VALUE,
              DEFAULT_KEEPALIVE_MILLIS, MILLISECONDS,
              new DelayedWorkQueue(), threadFactory);
    }
    • 特点:核心线程数量固定,非核心线程数量无限,执行完闲置10ms后回收,任务队列为延时阻塞队列。
    • 应用场景:执行定时或周期性的任务。

    使用示例:

    // 1. 创建 定时线程池对象 & 设置线程池线程数量固定为5
    ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(5);
    // 2. 创建好Runnable类线程对象 & 需执行的任务
    Runnable task =new Runnable(){
      public void run() {
         System.out.println("执行任务啦");
      }
    };
    // 3. 向线程池提交任务
    scheduledThreadPool.schedule(task, 1, TimeUnit.SECONDS); // 延迟1s后执行任务
    scheduledThreadPool.scheduleAtFixedRate(task,10,1000,TimeUnit.MILLISECONDS);// 延迟10ms后、每隔1000ms执行任务

    5.3 可缓存线程池(CachedThreadPool)

    创建方法的源码:

    public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
    }
    public static ExecutorService newCachedThreadPool(ThreadFactory threadFactory) {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>(),
                                      threadFactory);
    }
    • 特点:无核心线程,非核心线程数量无限,执行完闲置60s后回收,任务队列为不存储元素的阻塞队列。
    • 应用场景:执行大量、耗时少的任务。

    使用示例:

    // 1. 创建可缓存线程池对象
    ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
    // 2. 创建好Runnable类线程对象 & 需执行的任务
    Runnable task =new Runnable(){
      public void run() {
         System.out.println("执行任务啦");
      }
    };
    // 3. 向线程池提交任务
    cachedThreadPool.execute(task);

    5.4 单线程化线程池(SingleThreadExecutor)

    创建方法的源码:

    public static ExecutorService newSingleThreadExecutor() {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>()));
    }
    public static ExecutorService newSingleThreadExecutor(ThreadFactory threadFactory) {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>(),
                                    threadFactory));
    }
    • 特点:只有1个核心线程,无非核心线程,执行完立即回收,任务队列为链表结构的有界队列。
    • 应用场景:不适合并发但可能引起IO阻塞性及影响UI线程响应的操作,如数据库操作、文件操作等。

    使用示例:

    // 1. 创建单线程化线程池
    ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();
    // 2. 创建好Runnable类线程对象 & 需执行的任务
    Runnable task =new Runnable(){
      public void run() {
         System.out.println("执行任务啦");
      }
    };
    // 3. 向线程池提交任务
    singleThreadExecutor.execute(task);

    5.5 对比

    6 总结

    Executors的4个功能线程池虽然方便,但现在已经不建议使用了,而是建议直接通过使用ThreadPoolExecutor的方式,这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。

    其实Executors的4个功能线程有如下弊端:

    • FixedThreadPoolSingleThreadExecutor:主要问题是堆积的请求处理队列均采用LinkedBlockingQueue,可能会耗费非常大的内存,甚至OOM。
    • CachedThreadPoolScheduledThreadPool:主要问题是线程数最大数是Integer.MAX_VALUE,可能会创建数量非常多的线程,甚至OOM。

     

    参考

     

    展开全文
  • Java多线程04_线程同步问题

    万次阅读 2020-08-30 16:25:59
    Java多线程04_线程同步问题 关键字 synchronized 可以保证在同一时刻,只有一个线程可以执行某个方法或某个代码块 案例1:不安全的买票 (多个线程访问同一个对象的同一个方法): public class ByTicket { public ...

    Java多线程04_线程同步问题

    关键字 synchronized 可以保证在同一时刻,只有一个线程可以执行某个方法或某个代码块


    案例1:不安全的买票 (多个线程访问同一个对象的同一个方法):

    public class ByTicket {
    	public static void main(String[] args) {
    		BuyThread buyThread = new BuyThread();
    		new Thread(buyThread,"zhangsan").start();
    		new Thread(buyThread,"lisi").start();
    		new Thread(buyThread,"wangwu").start();
    	}
    }
    
    class BuyThread implements Runnable{
    	
    	private int ticketNums = 10;
    	boolean flag = true;
    
    	@Override
    	public void run() {
    		while(flag) {
    			try {
    				buy();
    			} catch (InterruptedException e) {
    				e.printStackTrace();
    			}
    		}
    	}
    	
    	private void buy() throws InterruptedException {
    		if(ticketNums<=0) {
    			flag = false;
    			return;
    		}
    		
    		Thread.sleep(200);
    		
    		System.out.println(Thread.currentThread().getName()+"买到了"+ ticketNums--);
    	}
    	
    }
    
    wangwu买到了10
    zhangsan买到了8
    lisi买到了9
    lisi买到了7
    wangwu买到了7
    zhangsan买到了7
    zhangsan买到了6
    lisi买到了4
    wangwu买到了5
    lisi买到了3
    wangwu买到了3
    zhangsan买到了2
    wangwu买到了1
    lisi买到了0
    zhangsan买到了-1
    

    解决办法:在 buy() 方法上增加 synchronized 修饰,使其成为同步方法

    private synchronized void buy() throws InterruptedException {
    	
    }
    
    zhangsan买到了10
    zhangsan买到了9
    zhangsan买到了8
    wangwu买到了7
    wangwu买到了6
    wangwu买到了5
    lisi买到了4
    lisi买到了3
    lisi买到了2
    lisi买到了1
    

    案例2:不安全的取钱(多个线程操作同一个对象account):

    public class Bank {
    	public static void main(String[] args) {
    		Account account = new Account(100,"存款");
    		DrawMoney blu = new DrawMoney(account,20,"BLU");
    		DrawMoney gf = new DrawMoney(account,100,"gf");
    		
    		blu.start();
    		gf.start();
    	}
    }
    
    class Account{
    	int money;
    	String name;
    	
    	public Account(int money, String name) {
    		super();
    		this.money = money;
    		this.name = name;
    	}
    	
    }
    
    class DrawMoney extends Thread{
    	Account account;
    	int draw;
    	int nowmoney;
    	
    	public DrawMoney(Account account, int draw, String name) {
    		super(name);
    		this.account = account;
    		this.draw = draw;
    		this.nowmoney = nowmoney;
    	}
    	
    	@Override
    	public void run() {
    		
    		try {
    			Thread.sleep(1000);
    		} catch (InterruptedException e) {
    			e.printStackTrace();
    		}
    		
    		if(account.money-draw<0) {
    			System.out.println(Thread.currentThread().getName()+"账户余额不足");
    			return;
    		}
    		
    		account.money = account.money - draw;
    		nowmoney = nowmoney + draw;
    		System.out.println(account.name+"余额为:"+account.money);
    		System.out.println(Thread.currentThread().getName()+"手里的钱:"+nowmoney);
    	}
    	
    }
    
    存款余额为:80
    gf手里的钱:100
    存款余额为:80
    BLU手里的钱:20
    

    解决办法:使用synchronized (account) {} 对给定对象account加锁,进入同步代码块前要获得给定对象的锁

    @Override
    public void run() {
    	
    	synchronized (account) {
    		try {
    			Thread.sleep(1000);
    		} catch (InterruptedException e) {
    			e.printStackTrace();
    		}
    		
    		if(account.money-draw<0) {
    			System.out.println(Thread.currentThread().getName()+"账户余额不足");
    			return;
    		}
    		
    		account.money = account.money - draw;
    		nowmoney = nowmoney + draw;
    		System.out.println(account.name+"余额为:"+account.money);
    		System.out.println(Thread.currentThread().getName()+"手里的钱:"+nowmoney);
    	}
    
    }
    
    存款余额为:80
    BLU手里的钱:20
    gf账户余额不足
    

    案例3:不安全的集合(原因是两个线程可能会在同一瞬间操作了同一位置):

    public class UnsafeList {
    	
    	public static void main(String[] args) {
    		List<String> list = new ArrayList<String>();
    		for(int i=0;i<10000;i++) {
    			new Thread(()->{
    				list.add(Thread.currentThread().getName());
    			}).start();
    		}
    		System.out.println(list.size());
    	}
    }
    
    9998
    

    解决办法:锁住 list 对象

    import java.util.ArrayList;
    import java.util.List;
    
    public class UnsafeList {
    	
    	public static void main(String[] args) {
    		List<String> list = new ArrayList<String>();
    		for(int i=0;i<10000;i++) {
    			new Thread(()->{
    				synchronized (list) {
    					list.add(Thread.currentThread().getName());
    				}
    			}).start();
    		}
    		System.out.println(list.size());
    	}
    }
    
    10000
    
    展开全文
  • Java多线程03_线程状态、优先级、用户线程和守护线程 线程方法: setPriority() 更改线程优先级 static void sleep() 线程休眠 void join() 插队 static void yield() 礼让 void interrupt() 中断...

    Java多线程03_线程状态、优先级、用户线程和守护线程


    在这里插入图片描述


    线程方法:

    setPriority()				更改线程优先级
    static void sleep()			线程休眠
    void join()					插队
    static void yield()			礼让
    void interrupt()			中断线程
    boolean isAlive()			是否存活
    

    停止线程:

    不推荐JDK提供的stop()、destory()方法 【已废弃】

    推荐让线程正常停止,不要死循环!

    建议使用一个标志位进行终止,当flag=false,则终止线程运行

    public class TestStop implements Runnable{
    
    	private boolean flag = true;
    	
    	@Override
    	public void run() {
    		int i = 0;
    		while(flag) {
    			System.out.println("run...Thread"+i++);
    		}
    	}
    	
    	//设置公开方法利用标志位停止线程
    	public void stopThread() {
    		this.flag = false;
    		System.out.println("线程停止了");
    	}
    	
    	public static void main(String[] args) {
    		TestStop testStop = new TestStop();
    		new Thread(testStop).start();
    		
    		for(int i=0;i<1000;i++) {
    			System.out.println("main...,"+i);
    			if(i==900) {
    				testStop.stopThread();
    			}
    		}
    		
    	}
    	
    }
    

    线程休眠:

    sleep方法存在异常:InterruptedException

    sleep时间结束,线程进入就绪状态

    每个对象都有一个锁,sleep不会释放锁

    Thread.sleep(1000);
    

    线程礼让:

    让当前执行的线程暂停,但不阻塞

    将线程从运行状态转为就绪状态

    礼让不一定成功,看cpu心情

    Thread.yield();
    

    Join合并线程,待此线程执行完成后,再执行其他线程,其他线程阻塞(插队)

    main线程和Thread线程交替执行,当main线程执行到200时,Thread线程插队,一直到执行结束,main线程才能重新执行

    package com.blu.demo1;
    
    public class TestJoin implements Runnable{
    
    	@Override
    	public void run() {
    		for(int i = 0;i < 1000;i++) {
    			System.out.println("vip线程正在执行....."+i);
    		}
    	}
    	
    	public static void main(String[] args) throws InterruptedException {
    		TestJoin testJoin = new TestJoin();
    		Thread thread = new Thread(testJoin);
    		thread.start();
    		
    		for(int i = 0;i < 1000;i++) {
    			if(i==200) {
    				thread.join();
    			}
    			System.out.println("main线程正在执行....."+i);
    		}
    	}
    
    }
    

    getState()监测线程状态:

    返回枚举类型:

    NEW	
    RUNNABLE
    BLOCKED
    WAITING
    TIMED_WAITING
    TERMINATED
    

    线程优先级:

    线程优先级用1-10之间的整数表示,1为最低优先级,10为最高优先级

    主线程优先级默认为5,且不可修改

    枚举
    Thread.MAX_PRIORITY = 10
    Thread.NORM_PRIORITY = 5
    Thread.MIN_PRIORITY = 1

    优先级高不一定先执行,由CPU调度决定

    public class TestPriority {
    	
    	public static void main(String[] args) {
    		//主线程优先级
    		System.out.println(Thread.currentThread().getName()+"-->"+Thread.currentThread().getPriority());
    		MyPriority myPriority = new MyPriority();
    		Thread t1 = new Thread(myPriority);
    		Thread t2 = new Thread(myPriority);
    		Thread t3 = new Thread(myPriority);
    		Thread t4 = new Thread(myPriority);
    		Thread t5 = new Thread(myPriority);
    		Thread t6 = new Thread(myPriority);
    		//设置优先级
    		t1.start();
    		t2.setPriority(1);
    		t3.setPriority(4);
    		t4.setPriority(Thread.MAX_PRIORITY);
    		t5.setPriority(5);
    		t6.setPriority(7);
    		t2.start();
    		t3.start();
    		t4.start();
    		t5.start();
    		t6.start();
    	}
    	
    }
    
    class MyPriority implements Runnable{
    
    	@Override
    	public void run() {
    		System.out.println(Thread.currentThread().getName()+"-->"+Thread.currentThread().getPriority());
    	}
    	
    }
    

    守护线程

    线程分为用户线程(比如main)守护线程(比如gc)

    虚拟机必须确保用户线程执行完毕,不用等待守护线程执行完毕

    当只剩下守护线程时,JVM就会退出,只要存在一个用户线程,JVM就不会退出

    public class TestDaemon {
    	
    	public static void main(String[] args) {
    		God god = new God();
    		You you = new You();
    		Thread thread = new Thread(god);
    		thread.setDaemon(true);
    		thread.start();
    		new Thread(you).start();
    	}
    }
    
    class God implements Runnable{
    	
    	@Override
    	public void run() {
    		while(true) {
    			System.out.println("上帝保佑着你");
    		}
    	}
    }
    
    class You implements Runnable{
    
    	@Override
    	public void run() {
    		for(int i = 0;i<=100;i++) {
    			System.out.println("你开心地活了"+i+"年");
    		}
    		System.out.println("GoodBye,World!");
    	}
    	
    }
    
    展开全文
  • 万字图解Java多线程

    万次阅读 多人点赞 2020-09-06 14:45:07
    java多线程我个人觉得是javaSe中最难的一部分,我以前也是感觉学会了,但是真正有多线程的需求却不知道怎么下手,实际上还是对多线程这块知识了解不深刻,不知道多线程api的应用场景,不知道多线程的运行流程等等,...
  • java多线程

    万次阅读 2019-09-28 19:43:58
    多线程 实现多线程有三种方法,这里说两种 第一:继承Thread类 public class TestThread extends Thread{ /** * 1.通过继承Thread类来创建多线程 * 2.重写run方法,在run方法中执行子线程 * 3.主方法中创建对象...
  • Java多线程之初识线程

    万次阅读 2020-09-10 23:49:26
    文章目录实现多线程的两种方式区别继承Thread示例实现Runnable接口示例start()的执行步骤 实现多线程的两种方式 1、继承Thread类; 2、实现Runnable接口。 区别 Java语言是单继承的,使用实现Runnable方式创建线程,...
  • Java多线程05_死锁

    万次阅读 2020-09-03 21:08:57
    Java多线程05_死锁 /** * 死锁:多个线程互相占着对方需要的资源,形成僵持状态 * @author BLU */ public class DeadLock { public static void main(String[] args) { Makeup girl1 = new Makeup(0, "女一号...
  • Java多线程02_Thread与静态代理

    万次阅读 2020-08-27 12:22:16
    Java多线程02_Thread与静态代理 静态代理示例: 真实对象和代理对象都要 实现同一个接口 代理是对方法的 增强 public class StaticProxy { public static void main(String[] args) { WeddingCompany ...
  • Java 多线程

    千次阅读 2018-10-02 17:02:16
    这篇文章主要是对多线程的问题进行总结的,因此罗列了40个多线程的问题。  这些多线程的问题,有些来源于各大网站、有些来源于自己的思考。可能有些问题网上有、可能有些问题对应的答案也有、也可能有些各位网友也...
  • Java多线程06_重入锁ReentrantLock

    万次阅读 2020-09-03 21:42:16
    Java多线程06_重入锁ReentrantLock public class TestLock { public static void main(String[] args) { TestLock2 testLock2 = new TestLock2(); new Thread(testLock2).start(); new Thread(testLock2)....
  • Java多线程学习(吐血超详细总结)

    万次阅读 多人点赞 2015-03-14 13:13:17
    本文主要讲了java多线程的使用方法、线程同步、线程数据传递、线程状态及相应的一些线程函数用法、概述等。
  • Java多线程01_创建线程三种方式

    万次阅读 2020-08-26 21:23:39
    一个进程中可包含线程java中有两个默认线程:main线程 和 gc线程 线程是CPU调度和执行的单位 线程创建1:继承Thread类(不推荐,避免OOP单继承局限性) 创建线程:继承Thread类,重写run方法 public class ...
  • java 多线程

    千次阅读 2012-11-19 15:52:09
    java中要想实现多线程,有两种手段,一种是继续Thread类,另外一种是实现Runable接口。 Thread和Runnable的区别: 如果一个类继承Thread,则不适合资源共享。但是如果实现了Runable接口的话,则很容易的实现资源...
  • 一篇文章弄懂Java多线程基础和Java内存模型

    万次阅读 多人点赞 2020-04-12 19:07:17
    文章目录一、多线程的生命周期及五种基本状态二、Java多线程的创建及启动1.继承Thread类,重写该类的run()方法2.通过实现Runnable接口创建线程类3.通过Callable和Future接口创建线程三、Java内存模型概念四、内存间...
  • Java多线程面试题

    万次阅读 2020-10-25 15:56:40
    sleep 方法: 是 Thread 类的静态方法,当前线程将睡眠 n 毫秒,线程进入阻塞状态。当睡眠时间到了,会解除阻塞,进行可运行状态,等待 CPU 的到来。睡眠不释放锁(如果有的话); wait 方法: 是 Object 的方法...
  • java多线程编程实例

    万次阅读 多人点赞 2018-05-25 10:01:22
    这篇文章主要介绍了java多线程编程实例,分享了几则多线程的实例代码,具有一定参考价值,加深多线程编程的理解还是很有帮助的,需要的朋友可以参考下。1.相关知识:Java多线程程序设计到的知识:(1)对同一个数量...
  • Java多线程学习

    千次阅读 多人点赞 2018-04-09 00:45:52
    一、java多线程系列Java多线程学习(一)Java多线程入门Java多线程学习(二)synchronized关键字(1)Java多线程学习(二)synchronized关键字(2)Java多线程学习(三)volatile关键字Java多线程学习(四)等待/...
  • Java多线程技术~线程的定义与同步

    万次阅读 热门讨论 2020-06-16 11:38:33
    Java多线程技术 线程和进程 程序Program        程序是一段静态的代码,它是应用程序执行的蓝本 进程Process        进程是指一种正在运行的...
  • Java 多线程:线程优先级

    千次阅读 2017-01-15 14:45:11
    Java 多线程:线程优先级
  • Java 多线程 -- 从入门到精通

    千次阅读 多人点赞 2020-11-04 20:48:26
    Java 多线程 -- 从入门到精通Java线程与线程的区别多线程的实现方法Thread中start和run方法的区别Thread和Runnable的关系使用Callable和Future创建线程线程返回值的处理方法线程的六个状态线程不安全解决线程不安全...
  • Java多线程超详解

    万次阅读 多人点赞 2019-06-11 01:00:30
    随着计算机的配置越来越高,我们需要将进程进一步优化,细分为线程,充分提高图形化界面的多线程的开发。这就要求对线程的掌握很彻底。 那么话不多说,今天本帅将记录自己线程的学习。 线程的相关API //获取当前...
  • Java多线程——基本概念

    万次阅读 多人点赞 2019-10-23 10:36:25
    线程和多线程 程序:是一段静态的代码,是应用软件执行的蓝本 进程:是程序的一次动态执行过程,它对应了从代码加载、执行至执行完毕的一个完整过程,这个过程也是进程本身从产生、发展至消亡的过程 线程:是比...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 178,625
精华内容 71,450
关键字:

java多线程

java 订阅