zoukankan      html  css  js  c++  java
  • java多线程学习笔记

    java多线程

    一、引言

    通常,用并发结局的问题大体上可以分为“速度”和“设计可管理性”两种。

    • 速度:多线程机制可以通过频繁的线程间切换,有效的避免“阻塞”问题;
    • 设计可管理性:也就是改善代码设计,并发编程更符合人的实际逻辑,可以有效的解决仿真相关的问题。

    二、java线程

    1.创建与启动

    java中通常有两种方式创建线程:

    • 扩展java.lang.Thread类

        public class taskT extends Thread{
            public void run() {
                System.out.println("正在执行Runnable!");
            }
            public static void main(String[] args) {
                Thread t =new taskT();
                t.start();
            }
        }
      
    • 实现java.lang.Runnable接口

        public class taskR implements Runnable{
            @Override
            public void run() {
                System.out.println("正在执行Runnable!");
            }
        }
      
        public static void main(String[] args) {
            taskR r = new taskR();
            Thread thread = new Thread(r);
            thread.start();
        }
      

    一旦创建一个新线程并开始执行,即调用start方法时,虚拟机中就又多一个进程,这个进程和原先的进程时并行的。

    public class TaskR implements Runnable{
        @Override
        public void run() {
            System.out.println("正在执行线程B!");
            System.out.println("已经执行完线程B!");
        }
    }
    
    public class test {
        public static void main(String[] args) {
            System.out.println("正在执行main函数111!");
            TaskR b = new TaskR();
            Thread threadB = new Thread(b);
            System.out.println("准备添加线程B!");
            threadB.start();
            System.out.println("继续执行main函数222!");
    //      for (int i = 0; i < 999999; i++);//延时代码
            System.out.println("正在执行main函数333!");
        }
    }
    

    如上程序执行结果输出如下:

    正在执行main函数111!
    准备添加线程B!
    继续执行main函数222!
    正在执行main函数333!
    正在执行线程B!
    已经执行完线程B!
    

    将main函数的注释代码去除注释,增加代码延时,输出如下:

    正在执行main函数111!
    准备添加线程B!
    继续执行main函数222!
    正在执行线程B!
    已经执行完线程B!
    正在执行main函数333!
    

    由上两组对比不难发现,当线程threadB一旦开始执行(执行threadbB.start()),main函数这条线程和线程threadB是并发的。

    另外需要注意的是,多线程表面上给人的感觉是多个任务同时进行,实际上并非如此,虚拟机通过频繁的切换进程来给人同时执行的错觉。

    2.线程状态的转换

    线程的状态是线程控制的基础。线程状态总的可以分为五大类:生、等待、阻塞、睡眠、死。

    • 生:线程已经被new,且未执行;
    • start():线程调用start()方法后即进入等待/可运行状态;
    • yield():此外正在执行的线程也可以通过Thread.yield()方法使当前线程暂停执行,并进入可运行状态,从而让步其他线程,但也可能让步失败,因为该线程可能在进入运行状态后又被再次选中。(注:让步的线程是接着执行线程,还是重新执行,经程序测试是接着执行未执行的代码);
    • 阻塞:线程卡死,或不停执行,跳不出run()方法;
    • sleep():调用Thread.sleep()方法,使线程暂停执行一段时间,即睡眠,可以用来帮助其他进程获得运行机会;
    • 死:线程执行完毕,执行完run()方法。

    3.线程的同步与锁

    当多个线程同时访问互斥(可交换)数据时,应该同步以保护数据,确保两个线程不会同时更改它。通常用synchronized字段实现,而且只能同步方法或同步代码快。

    如果两个线程要执行一个类中的synchronized方法,并且两个线程使用相同的实例来调用方法,那么一次只能有一个线程能够执行方法,另一个需要等待,直到锁被释放。

    4.线程的交换

    • notify()
    • notifyAll()
    • wait()

    5.线程的调度

    • 休眠:Thread.sleep()
    • 优先级
    • 让步:Thread.yield()
    • 合并:join():join()方法是是在某个线程a中,加入一个线程b,线程b没执行完前,a不得执行。

        public class TaskR implements Runnable{
            @Override
            public void run() {     
                System.out.println("正在执行线程B!");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("已经执行完线程B!");
            }
        }
        public class test {
            public static void main(String[] args) throws InterruptedException {
                System.out.println("正在执行main函数!");
                TaskR b = new TaskR();
                Thread threadB = new Thread(b);
                System.out.println("准备添加线程B!");
                threadB.start();
                System.out.println("执行完main");
            }
        }
        public class test2 {
            public static void main(String[] args) throws InterruptedException {
                System.out.println("正在执行main函数!");
                TaskR b = new TaskR();
                Thread threadB = new Thread(b);
                System.out.println("准备添加线程B!");
                threadB.start();
                System.out.println("threadB.join()");
                threadB.join();
                System.out.println("执行完main");
            }
        }
      

      分别执行test1和test2中的main执行结果如下:

        正在执行main函数!
        准备添加线程B!
        threadB.join()
        执行完main
        正在执行线程B!
        已经执行完线程B!
      

      test2中添加join()方法:

        正在执行main函数!
        准备添加线程B!
        threadB.join()
        正在执行线程B!
        已经执行完线程B!
        执行完main
      

      在threadB中调用sleep是为了让main函数这条线程获得执行机会。有结果,可知当threadB调用join方法后,即使sleep了main函数都不能跑完,说明join方法的作用,即必须threadB执行完才能继续执行main函数。

    • 守护线程

    线程池

    java.util.concurrent.Executors。 例:

    ExecutorService pool = Executors.newFixedThreadPool(2);
    Thread t1 = new MyThread(); 
    Thread t2 = new MyThread();
    Thread t3 = new MyThread();
    Thread t4 = new MyThread();
    pool.execute(t1);
    pool.execute(t2);
    pool.execute(t3);
    pool.execute(t4);
    pool.shutdown();
    
  • 相关阅读:
    topcoder srm 681 div1
    topcoder srm 683 div1
    topcoder srm 684 div1
    topcoder srm 715 div1
    topcoder srm 685 div1
    topcoder srm 687 div1
    topcoder srm 688 div1
    topcoder srm 689 div1
    topcoder srm 686 div1
    topcoder srm 690 div1 -3
  • 原文地址:https://www.cnblogs.com/byonecry/p/4149554.html
Copyright © 2011-2022 走看看