精华内容
下载资源
问答
  • 主要介绍了Java线程批量数据导入的方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,下面小编和大家来一起学习下吧
  • 本文给大家分享java线程实现异步调用的方法,感兴趣的朋友跟着脚本之家小编一起学习吧
  • 主要为大家详细介绍了Java线程并发开发之DelayQueue使用示例,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
  • Java线程超详解

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

    引言

    随着计算机的配置越来越高,我们需要将进程进一步优化,细分为线程,充分提高图形化界面的多线程的开发。这就要求对线程的掌握很彻底。
    那么话不多说,今天本帅将记录自己线程的学习。

    程序,进程,线程的基本概念+并行与并发:

    程序:是为完成特定任务,用某种语言编写的一组指令的集合,即指一段静态的代码,静态对象。
    进程:是程序的一次执行过程,或是正在运行的一个程序,是一个动态的过程,有它自身的产生,存在和消亡的过程。-------生命周期
    线程:进程可进一步细化为线程,是一个程序内部的一条执行路径

    即:线程《线程(一个程序可以有多个线程)
    程序:静态的代码 进程:动态执行的程序
    线程:进程中要同时干几件事时,每一件事的执行路径成为线程。

    并行:多个CPU同时执行多个任务,比如:多个人同时做不同的事
    并发:一个CPU(采用时间片)同时执行多个任务,比如秒杀平台,多个人做同件事

    线程的相关API

    //获取当前线程的名字
    Thread.currentThread().getName()

    1.start():1.启动当前线程2.调用线程中的run方法
    2.run():通常需要重写Thread类中的此方法,将创建的线程要执行的操作声明在此方法中
    3.currentThread():静态方法,返回执行当前代码的线程
    4.getName():获取当前线程的名字
    5.setName():设置当前线程的名字
    6.yield():主动释放当前线程的执行权
    7.join():在线程中插入执行另一个线程,该线程被阻塞,直到插入执行的线程完全执行完毕以后,该线程才继续执行下去
    8.stop():过时方法。当执行此方法时,强制结束当前线程。
    9.sleep(long millitime):线程休眠一段时间
    10.isAlive():判断当前线程是否存活

    判断是否是多线程

    一条线程即为一条执行路径,即当能用一条路径画出来时即为一个线程
    例:如下看似既执行了方法一,又执行了方法2,但是其实质就是主线程在执行方法2和方法1这一条路径,所以就是一个线程

    public class Sample{
    		public void method1(String str){
    			System.out.println(str);
    		}
    	
    	public void method2(String str){
    		method1(str);
    	}
    	
    	public static void main(String[] args){
    		Sample s = new Sample();
    		s.method2("hello");
    	}
    }
    

    在这里插入图片描述

    线程的调度

    调度策略:
    时间片:线程的调度采用时间片轮转的方式
    抢占式:高优先级的线程抢占CPU
    Java的调度方法:
    1.对于同优先级的线程组成先进先出队列(先到先服务),使用时间片策略
    2.对高优先级,使用优先调度的抢占式策略

    线程的优先级

    等级:
    MAX_PRIORITY:10
    MIN_PRIORITY:1
    NORM_PRIORITY:5

    方法:
    getPriority():返回线程优先级
    setPriority(int newPriority):改变线程的优先级

    注意!:高优先级的线程要抢占低优先级的线程的cpu的执行权。但是仅是从概率上来说的,高优先级的线程更有可能被执行。并不意味着只有高优先级的线程执行完以后,低优先级的线程才执行。

    多线程的创建方式

    1. 方式1:继承于Thread类

    1.创建一个集成于Thread类的子类 (通过ctrl+o(override)输入run查找run方法)
    2.重写Thread类的run()方法
    3.创建Thread子类的对象
    4.通过此对象调用start()方法

    start与run方法的区别:

    start方法的作用:1.启动当前线程 2.调用当前线程的重写的run方法(在主线程中生成子线程,有两条线程)
    调用start方法以后,一条路径代表一个线程,同时执行两线程时,因为时间片的轮换,所以执行过程随机分配,且一个线程对象只能调用一次start方法。
    run方法的作用:在主线程中调用以后,直接在主线程一条线程中执行了该线程中run的方法。(调用线程中的run方法,只调用run方法,并不新开线程)

    总结:我们不能通过run方法来新开一个线程,只能调用线程中重写的run方法(可以在线程中不断的调用run方法,但是不能开启子线程,即不能同时干几件事),start是开启线程,再调用方法(即默认开启一次线程,调用一次run方法,可以同时执行几件事)
    在这里插入图片描述

    多线程例子(火车站多窗口卖票问题)

    	package com.example.paoduantui.Thread;
    	
    	import android.view.Window;
    	
    	/**
    	 *
    	 * 创建三个窗口卖票,总票数为100张,使用继承自Thread方式
    	 * 用静态变量保证三个线程的数据独一份
    	 * 
    	 * 存在线程的安全问题,有待解决
    	 *
    	 * */
    	
    	public class ThreadDemo extends Thread{
    	
    	    public static void main(String[] args){
    	        window t1 = new window();
    	        window t2 = new window();
    	        window t3 = new window();
    	
    	        t1.setName("售票口1");
    	        t2.setName("售票口2");
    	        t3.setName("售票口3");
    	
    	        t1.start();
    	        t2.start();
    	        t3.start();
    	    }
    	
    	}
    	
    	class window extends Thread{
    	    private static int ticket = 100; //将其加载在类的静态区,所有线程共享该静态变量
    	
    	    @Override
    	    public void run() {
    	        while(true){
    	            if(ticket>0){
    	//                try {
    	//                    sleep(100);
    	//                } catch (InterruptedException e) {
    	//                    e.printStackTrace();
    	//                }
    	                System.out.println(getName()+"当前售出第"+ticket+"张票");
    	                ticket--;
    	            }else{
    	                break;
    	            }
    	        }
    	    }
    	}
    

    2. 方式2:实现Runable接口方式

    1.创建一个实现了Runable接口的类
    2.实现类去实现Runnable中的抽象方法:run()
    3.创建实现类的对象
    4.将此对象作为参数传递到Thread类中的构造器中,创建Thread类的对象
    5.通过Thread类的对象调用start()

    具体操作,将一个类实现Runable接口,(插上接口一端)。
    另外一端,通过实现类的对象与线程对象通过此Runable接口插上接口实现

    	package com.example.paoduantui.Thread;
    	
    	public class ThreadDemo01 {
    	    
    	    public static  void main(String[] args){
    	        window1 w = new window1();
    	        
    	        //虽然有三个线程,但是只有一个窗口类实现的Runnable方法,由于三个线程共用一个window对象,所以自动共用100张票
    	        
    	        Thread t1=new Thread(w);
    	        Thread t2=new Thread(w);
    	        Thread t3=new Thread(w);
    	
    	        t1.setName("窗口1");
    	        t2.setName("窗口2");
    	        t3.setName("窗口3");
    	        
    	        t1.start();
    	        t2.start();
    	        t3.start();
    	    }
    	}
    	
    	class window1 implements Runnable{
    	    
    	    private int ticket = 100;
    	
    	    @Override
    	    public void run() {
    	        while(true){
    	            if(ticket>0){
    	//                try {
    	//                    sleep(100);
    	//                } catch (InterruptedException e) {
    	//                    e.printStackTrace();
    	//                }
    	                System.out.println(Thread.currentThread().getName()+"当前售出第"+ticket+"张票");
    	                ticket--;
    	            }else{
    	                break;
    	            }
    	        }
    	    }
    	}
    

    比较创建线程的两种方式:
    开发中,优先选择实现Runable接口的方式
    原因1:实现的方式没有类的单继承性的局限性
    2:实现的方式更适合用来处理多个线程有共享数据的情况
    联系:Thread也是实现自Runable,两种方式都需要重写run()方法,将线程要执行的逻辑声明在run中

    3.新增的两种创建多线程方式

    1.实现callable接口方式:

    与使用runnable方式相比,callable功能更强大些:
    runnable重写的run方法不如callaalbe的call方法强大,call方法可以有返回值
    方法可以抛出异常
    支持泛型的返回值
    需要借助FutureTask类,比如获取返回结果

    package com.example.paoduantui.Thread;
    
    
    import java.util.concurrent.Callable;
    import java.util.concurrent.ExecutionException;
    import java.util.concurrent.FutureTask;
    
    /**
     * 创建线程的方式三:实现callable接口。---JDK 5.0新增
     *是否多线程?否,就一个线程
     *
     * 比runable多一个FutureTask类,用来接收call方法的返回值。
     * 适用于需要从线程中接收返回值的形式
     * 
     * //callable实现新建线程的步骤:
     * 1.创建一个实现callable的实现类
     * 2.实现call方法,将此线程需要执行的操作声明在call()中
     * 3.创建callable实现类的对象
     * 4.将callable接口实现类的对象作为传递到FutureTask的构造器中,创建FutureTask的对象
     * 5.将FutureTask的对象作为参数传递到Thread类的构造器中,创建Thread对象,并调用start方法启动(通过FutureTask的对象调用方法get获取线程中的call的返回值)
     * 
     * */
    
    
    //实现callable接口的call方法
    class NumThread implements Callable{
    
        private int sum=0;//
    
        //可以抛出异常
        @Override
        public Object call() throws Exception {
            for(int i = 0;i<=100;i++){
                if(i % 2 == 0){
                    System.out.println(Thread.currentThread().getName()+":"+i);
                    sum += i;
                }
            }
            return sum;
        }
    }
    
    public class ThreadNew {
    
        public static void main(String[] args){
            //new一个实现callable接口的对象
            NumThread numThread = new NumThread();
    
            //通过futureTask对象的get方法来接收futureTask的值
            FutureTask futureTask = new FutureTask(numThread);
    
            Thread t1 = new Thread(futureTask);
            t1.setName("线程1");
            t1.start();
    
            try {
                //get返回值即为FutureTask构造器参数callable实现类重写的call的返回值
               Object sum = futureTask.get();
               System.out.println(Thread.currentThread().getName()+":"+sum);
            } catch (ExecutionException e) {
                e.printStackTrace();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    
    使用线程池的方式:

    背景:经常创建和销毁,使用量特别大的资源,比如并发情况下的线程,对性能影响很大。
    思路:提前创建好多个线程,放入线程池之,使用时直接获取,使用完放回池中。可以避免频繁创建销毁,实现重复利用。类似生活中的公共交通工具。(数据库连接池)
    好处:提高响应速度(减少了创建新线程的时间)
    降低资源消耗(重复利用线程池中线程,不需要每次都创建)
    便于线程管理
    corePoolSize:核心池的大小
    maximumPoolSize:最大线程数
    keepAliveTime:线程没有任务时最多保持多长时间后会终止
    。。。。。。

    JDK 5.0 起提供了线程池相关API:ExecutorService 和 Executors
    ExecutorService:真正的线程池接口。常见子类ThreadPoolExecutor.
    void execute(Runnable coommand):执行任务/命令,没有返回值,一般用来执行Runnable
    Futuresubmit(Callable task):执行任务,有返回值,一般又来执行Callable
    void shutdown():关闭连接池。

    Executors工具类,线程池的工厂类,用于创建并返回不同类型的线程池
    Executors.newCachedThreadPool()创建一个可根据需要创建新线程的线程池
    Executors.newFixedThreadPool(n)创建一个可重用固定线程数的线程池
    Executors.newSingleThreadExecutor():创建一个只有一个线程的线程池
    Executors.newScheduledThreadPool(n)创建一个线程池,它可安排在给定延迟后运行命令或者定期地执行。

    线程池构造批量线程代码如下:

    package com.example.paoduantui.Thread;
    
    
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    
    /**
     * 创建线程的方式四:使用线程池(批量使用线程)
     *1.需要创建实现runnable或者callable接口方式的对象
     * 2.创建executorservice线程池
     * 3.将创建好的实现了runnable接口类的对象放入executorService对象的execute方法中执行。
     * 4.关闭线程池
     *
     * */
    
    class NumberThread implements Runnable{
    
    
        @Override
        public void run() {
            for(int i = 0;i<=100;i++){
                if (i % 2 ==0 )
                System.out.println(Thread.currentThread().getName()+":"+i);
            }
        }
    }
    
    class NumberThread1 implements Runnable{
        @Override
        public void run() {
            for(int i = 0;i<100; i++){
                if(i%2==1){
                    System.out.println(Thread.currentThread().getName()+":"+i);
                }
            }
        }
    }
    
    public class ThreadPool {
    
        public static void main(String[] args){
    
            //创建固定线程个数为十个的线程池
            ExecutorService executorService = Executors.newFixedThreadPool(10);
    
            //new一个Runnable接口的对象
            NumberThread number = new NumberThread();
            NumberThread1 number1 = new NumberThread1();
    
            //执行线程,最多十个
            executorService.execute(number1);
            executorService.execute(number);//适合适用于Runnable
    
            //executorService.submit();//适合使用于Callable
            //关闭线程池
            executorService.shutdown();
        }
    
    }
    

    目前两种方式要想调用新线程,都需要用到Thread中的start方法。

    java virtual machine(JVM):java虚拟机内存结构

    程序(一段静态的代码)——————》加载到内存中——————》进程(加载到内存中的代码,动态的程序)
    进程可细分为多个线程,一个线程代表一个程序内部的一条执行路径
    每个线程有其独立的程序计数器(PC,指导着程序向下执行)与运行栈(本地变量等,本地方法等)
    在这里插入图片描述

    大佬传送门:https://blog.csdn.net/bluetjs/article/details/52874852

    线程通信方法:

    wait()/ notify()/ notifayAll():此三个方法定义在Object类中的,因为这三个方法需要用到锁,而锁是任意对象都能充当的,所以这三个方法定义在Object类中。

    由于wait,notify,以及notifyAll都涉及到与锁相关的操作
    wait(在进入锁住的区域以后阻塞等待,释放锁让别的线程先进来操作)---- Obj.wait 进入Obj这个锁住的区域的线程把锁交出来原地等待通知
    notify(由于有很多锁住的区域,所以需要将区域用锁来标识,也涉及到锁) ----- Obj.notify 新线程进入Obj这个区域进行操作并唤醒wait的线程

    有点类似于我要拉粑粑,我先进了厕所关了门,但是发现厕所有牌子写着不能用,于是我把厕所锁给了别人,别人进来拉粑粑还是修厕所不得而知,直到有人通知我厕所好了我再接着用。

    所以wait,notify需要使用在有锁的地方,也就是需要用synchronize关键字来标识的区域,即使用在同步代码块或者同步方法中,且为了保证wait和notify的区域是同一个锁住的区域,需要用锁来标识,也就是锁要相同的对象来充当

    线程的分类:

    java中的线程分为两类:1.守护线程(如垃圾回收线程,异常处理线程),2.用户线程(如主线程)

    若JVM中都是守护线程,当前JVM将退出。(形象理解,唇亡齿寒)

    线程的生命周期:

    JDK中用Thread.State类定义了线程的几种状态,如下:

    线程生命周期的阶段描述
    新建当一个Thread类或其子类的对象被声明并创建时,新生的线程对象处于新建状态
    就绪处于新建状态的线程被start后,将进入线程队列等待CPU时间片,此时它已具备了运行的条件,只是没分配到CPU资源
    运行当就绪的线程被调度并获得CPU资源时,便进入运行状态,run方法定义了线程的操作和功能
    阻塞在某种特殊情况下,被人为挂起或执行输入输出操作时,让出CPU并临时终止自己的执行,进入阻塞状态
    死亡线程完成了它的全部工作或线程被提前强制性地中止或出现异常导致结束

    在这里插入图片描述

    线程的同步:在同步代码块中,只能存在一个线程。

    线程的安全问题:

    什么是线程安全问题呢?
    线程安全问题是指,多个线程对同一个共享数据进行操作时,线程没来得及更新共享数据,从而导致另外线程没得到最新的数据,从而产生线程安全问题。

    上述例子中:创建三个窗口卖票,总票数为100张票
    1.卖票过程中,出现了重票(票被反复的卖出,ticket未被减少时就打印出了)错票。
    2.问题出现的原因:当某个线程操作车票的过程中,尚未完成操作时,其他线程参与进来,也来操作车票。(将此过程的代码看作一个区域,当有线程进去时,装锁,不让别的线程进去)
    生动理解的例子:有一个厕所,有人进去了,但是没有上锁,于是别人不知道你进去了,别人也进去了对厕所也使用造成错误。
    3.如何解决:当一个线程在操作ticket时,其他线程不能参与进来,直到此线程的生命周期结束
    4.在java中,我们通过同步机制,来解决线程的安全问题。

    方式一:同步代码块
    使用同步监视器(锁)
    Synchronized(同步监视器){
    //需要被同步的代码
    }
    说明:

    1. 操作共享数据的代码(所有线程共享的数据的操作的代码)(视作卫生间区域(所有人共享的厕所)),即为需要共享的代码(同步代码块,在同步代码块中,相当于是一个单线程,效率低)
    2. 共享数据:多个线程共同操作的数据,比如公共厕所就类比共享数据
    3. 同步监视器(俗称:锁):任何一个的对象都可以充当锁。(但是为了可读性一般设置英文成lock)当锁住以后只能有一个线程能进去(要求:多个线程必须要共用同一把锁,比如火车上的厕所,同一个标志表示有人)

    Runable天生共享锁,而Thread中需要用static对象或者this关键字或者当前类(window。class)来充当唯一锁

    方式二:同步方法
    使用同步方法,对方法进行synchronized关键字修饰
    将同步代码块提取出来成为一个方法,用synchronized关键字修饰此方法。
    对于runnable接口实现多线程,只需要将同步方法用synchronized修饰
    而对于继承自Thread方式,需要将同步方法用static和synchronized修饰,因为对象不唯一(锁不唯一)

    总结:1.同步方法仍然涉及到同步监视器,只是不需要我们显示的声明。
    2.非静态的同步方法,同步监视器是this
    静态的同步方法,同步监视器是当前类本身。继承自Thread。class

    方式三:JDK5.0新增的lock锁方法

    package com.example.paoduantui.Thread;
    
    
    import java.util.concurrent.locks.ReentrantLock;
    
    class Window implements Runnable{
        private int ticket = 100;//定义一百张票
        //1.实例化锁
        private ReentrantLock lock = new ReentrantLock();
    
        @Override
        public void run() {
            
                while (true) {
    
                    //2.调用锁定方法lock
                    lock.lock();
    
                    if (ticket > 0) {
                        try {
                            Thread.sleep(100);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
    
                        System.out.println(Thread.currentThread().getName() + "售出第" + ticket + "张票");
                        ticket--;
                    } else {
                        break;
                    }
                }
    
    
            }
    }
    
    public class LockTest {
    
        public static void main(String[] args){
           Window w= new Window();
    
           Thread t1 = new Thread(w);
           Thread t2 = new Thread(w);
           Thread t3 = new Thread(w);
    
           t1.setName("窗口1");
           t2.setName("窗口1");
           t3.setName("窗口1");
    
           t1.start();
           t2.start();
           t3.start();
        }
    
    }
    

    总结:Synchronized与lock的异同?

    相同:二者都可以解决线程安全问题
    不同:synchronized机制在执行完相应的代码逻辑以后,自动的释放同步监视器
    lock需要手动的启动同步(lock()),同时结束同步也需要手动的实现(unlock())(同时以为着lock的方式更为灵活)

    优先使用顺序:
    LOCK-》同步代码块-》同步方法

    判断线程是否有安全问题,以及如何解决:

    1.先判断是否多线程
    2.再判断是否有共享数据
    3.是否并发的对共享数据进行操作
    4.选择上述三种方法解决线程安全问题

    例题:

    	package com.example.paoduantui.Thread;
    	
    	/***
    	 * 描述:甲乙同时往银行存钱,存够3000
    	 *
    	 *
    	 * */
    	
    	//账户
    	class Account{
    	    private double balance;//余额
    	    //构造器
    	    public Account(double balance) {
    	        this.balance = balance;
    	    }
    	    //存钱方法
    	    public synchronized void deposit(double amt){
    	        if(amt>0){
    	            balance +=amt;
    	            try {
    	                Thread.sleep(1000);
    	            } catch (InterruptedException e) {
    	                e.printStackTrace();
    	            }
    	            System.out.println(Thread.currentThread().getName()+"存钱成功,余额为:"+balance);
    	        }
    	    }
    	}
    	
    	//两个顾客线程
    	class Customer extends Thread{
    	     private Account acct;
    	
    	     public Customer(Account acct){
    	         this.acct = acct;
    	     }
    	
    	
    	
    	    @Override
    	    public void run() {
    	        for (int i = 0;i<3;i++){
    	            acct.deposit(1000);
    	        }
    	    }
    	}
    	
    	//主方法,之中new同一个账户,甲乙两个存钱线程。
    	public class AccountTest {
    	
    	    public static void main(String[] args){
    	        Account acct = new Account(0);
    	        Customer c1 = new Customer(acct);
    	        Customer c2 = new Customer(acct);
    	
    	        c1.setName("甲");
    	        c2.setName("乙");
    	
    	        c1.start();
    	        c2.start();
    	    }
    	
    	}
    

    解决单例模式的懒汉式的线程安全问题:

    单例:只能通过静态方法获取一个实例,不能通过构造器来构造实例
    1.构造器的私有化:
    private Bank(){}//可以在构造器中初始化东西
    private static Bank instance = null;//初始化静态实例

    public static Bank getInstance(){
    if(instance!=null){
    instance = new Bank();
    }
    return instance;
    }

    假设有多个线程调用此单例,而调用的获取单例的函数作为操作共享单例的代码块并没有解决线程的安全问题,会导致多个线程都判断实例是否为空,此时就会导致多个实例的产生,也就是单例模式的线程安全问题。

    解决线程安全问题的思路:

    1. 将获取单例的方法改写成同部方法,即加上synchronized关键字,此时同步监视器为当前类本身。(当有多个线程并发的获取实例时,同时只能有一个线程获取实例),解决了单例模式的线程安全问题。
    2. 用同步监视器包裹住同步代码块的方式。

    懒汉式单例模式的模型,例如:生活中的限量版的抢购:
    当一群人并发的抢一个限量版的东西的时候,可能同时抢到了几个人,他们同时进入了房间(同步代码块内)
    但是只有第一个拿到限量版东西的人才能到手,其余人都不能拿到,所以效率稍高的做法是,当东西被拿走时,我们在门外立一块牌子,售罄。
    这样就减少了线程的等待。即下面效率稍高的懒汉式写法:

    package com.example.paoduantui.Thread;
    
    public class Bank {
        //私有化构造器
        private Bank(){}
        //初始化静态实例化对象
        private static  Bank instance = null;
    
        //获取单例实例,此种懒汉式单例模式存在线程不安全问题(从并发考虑)
    
        public static  Bank getInstance(){
            if(instance==null){
                instance = new Bank();
            }
            return  instance;
        }
    
        //同步方法模式的线程安全
        public static synchronized Bank getInstance1(){
            if(instance==null){
                instance = new Bank();
            }
            return  instance;
        }
        //同步代码块模式的线程安全(上锁)
        public  static Bank getInstance2(){
            synchronized (Bank.class){
                if(instance==null){
                    instance = new Bank();
                }
                return  instance;
            }
        }
        
        //效率更高的线程安全的懒汉式单例模式
        /**
         * 由于当高并发调用单例模式的时候,类似于万人夺宝,只有第一个进入房间的人才能拿到宝物,
         * 当多个人进入这个房间时,第一个人拿走了宝物,也就另外几个人需要在同步代码块外等候,
         * 剩下的人只需要看到门口售罄的牌子即已知宝物已经被夺,可以不用进入同步代码块内,提高了效率。
         * 
         * 
         * */
        public static Bank getInstance3(){
            if (instance==null){
                synchronized (Bank.class){
                    if(instance==null){
                        instance = new Bank();
                    }
                }
            }
            return  instance;
        }
    }
    

    线程的死锁问题:

    线程死锁的理解:僵持,谁都不放手,一双筷子,我一只你一只,都等对方放手(死锁,两者都进入阻塞,谁都吃不了饭,进行不了下面吃饭的操作)
    出现死锁以后,不会出现提示,只是所有线程都处于阻塞状态,无法继续

    package com.example.paoduantui.Thread;
    
    
    /**
     * 演示线程的死锁问题
     *
     * */
    public class Demo {
    
        public static void main(String[] args){
    
            final StringBuffer s1 = new StringBuffer();
            final StringBuffer s2 = new StringBuffer();
    
    
            new Thread(){
                @Override
                public void run() {
                    //先拿锁一,再拿锁二
                    synchronized (s1){
                        s1.append("a");
                        s2.append("1");
    
                        synchronized (s2) {
                            s1.append("b");
                            s2.append("2");
    
                            System.out.println(s1);
                            System.out.println(s2);
                        }
                    }
                }
            }.start();
    
            //使用匿名内部类实现runnable接口的方式实现线程的创建
            new Thread(new Runnable() {
                @Override
                public void run() {
                    synchronized (s2){
                        s1.append("c");
                        s2.append("3");
    
                        synchronized (s1) {
                            s1.append("d");
                            s2.append("4");
    
                            System.out.println(s1);
                            System.out.println(s2);
                        }
                    }
                }
            }).start();
        }
    
    }
    

    运行结果:
    1.先调用上面的线程,再调用下面的线程:
    在这里插入图片描述
    2.出现死锁:
    在这里插入图片描述
    3.先调用下面的线程,再调用上面的线程。
    在这里插入图片描述

    死锁的解决办法:

    1.减少同步共享变量
    2.采用专门的算法,多个线程之间规定先后执行的顺序,规避死锁问题
    3.减少锁的嵌套。

    线程的通信

    通信常用方法:

    通信方法描述
    wait()一旦执行此方法,当前线程就进入阻塞状态,并释放同步监视器
    notify一旦执行此方法,就会唤醒被wait的一个线程,如果有多个线程,就唤醒优先级高的线程
    notifyAll一旦执行此方法,就会唤醒所有被wait()的线程

    使用前提:这三个方法均只能使用在同步代码块或者同步方法中。

    package com.example.paoduantui.Thread;
    
    
    /**
     * 线程通信的例子:使用两个线程打印1—100,线程1,线程2交替打印
     *
     * 当我们不采取线程之间的通信时,无法达到线程1,2交替打印(cpu的控制权,是自动分配的)
     * 若想达到线程1,2交替打印,需要:
     * 1.当线程1获取锁以后,进入代码块里将number++(数字打印并增加)操作完以后,为了保证下个锁为线程2所有,需要将线程1阻塞(线程1你等等wait())。(输出1,number为2)
     * 2.当线程2获取锁以后,此时线程1已经不能进入同步代码块中了,所以,为了让线程1继续抢占下一把锁,需要让线程1的阻塞状态取消(通知线程1不用等了notify()及notifyAll()),即应该在进入同步代码块时取消线程1的阻塞。
     *
     * */
    
    class Number implements Runnable{
    
        private int number = 1;//设置共享数据(线程之间对于共享数据的共享即为通信)
    
    
        //对共享数据进行操作的代码块,需要线程安全
        @Override
        public synchronized void run() {
    
            while(true){
                //使得线程交替等待以及通知交替解等待
                notify();//省略了this.notify()关键字
                if(number<100){
                    try {
                        Thread.sleep(10);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName()+":"+number);
                    number++;
                    try {
                        wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }else{
                    break;
                }
            }
        }
    }
    
    public class CommunicationTest {
    
        public static void main(String[] args){
            //创建runnable对象
            Number number = new Number();
    
            //创建线程,并实现runnable接口
            Thread t1 = new Thread(number);
            Thread t2 = new Thread(number);
    
            //给线程设置名字
            t1.setName("线程1");
            t2.setName("线程2");
    
            //开启线程
            t1.start();
            t2.start();
    
        }
    
    }
    

    sleep和wait的异同:

    相同点:一旦执行方法以后,都会使得当前的进程进入阻塞状态
    不同点:
    1.两个方法声明的位置不同,Thread类中声明sleep,Object类中声明wait。
    2.调用的要求不同,sleep可以在任何需要的场景下调用,wait必须使用在同步代码块或者同步方法中
    3.关于是否释放同步监视器,如果两个方法都使用在同步代码块或同步方法中,sleep不会释放,wait会释放

    经典例题:生产者/消费者问题:

    生产者(Priductor)将产品交给店员(Clerk),而消费者(Customer)从店员处取走产品,店员一次只能持有固定数量的产品(比如20个),如果生产者视图生产更多的产品,店员会叫生产者停一下,如果店中有空位放产品了再通知生产者继续生产:如果店中没有产品了,店员会告诉消费者等一下,如果店中有产品了再通知消费者来取走产品。

    这里可能出现两个问题:
    生产者比消费者快的时候,消费者会漏掉一些数据没有收到。
    消费者比生产者快时,消费者会去相同的数据。

    package com.example.paoduantui.Thread;
    
    
    /**
     * 线程通信的应用:生产者/消费者问题
     *
     * 1.是否是多线程问题?是的,有生产者线程和消费者线程(多线程的创建,四种方式)
     * 2.多线程问题是否存在共享数据? 存在共享数据----产品(同步方法,同步代码块,lock锁)
     * 3.多线程是否存在线程安全问题? 存在----都对共享数据产品进行了操作。(三种方法)
     * 4.是否存在线程间的通信,是,如果生产多了到20时,需要通知停止生产(wait)。(线程之间的通信问题,需要wait,notify等)
     *
     * */
    
    
    	class Clerk{
    	
    	    private int productCount = 0;
    	
    	
    	    //生产产品
    	    public synchronized void produceProduct() {
    	
    	        if(productCount<20) {
    	            productCount++;
    	
    	            System.out.println(Thread.currentThread().getName()+":开始生产第"+productCount+"个产品");
    	            notify();
    	        }else{
    	            //当有20个时,等待wait
    	            try {
    	                wait();
    	            } catch (InterruptedException e) {
    	                e.printStackTrace();
    	            }
    	        }
    	    }
    	
    	    //消费产品
    	    public synchronized void consumeProduct() {
    	        if (productCount>0){
    	            System.out.println(Thread.currentThread().getName()+":开始消费第"+productCount+"个产品");
    	            productCount--;
    	            notify();
    	        }else{
    	            //当0个时等待
    	            try {
    	                wait();
    	            } catch (InterruptedException e) {
    	                e.printStackTrace();
    	            }
    	        }
    	    }
    	}
    	
    	class Producer extends Thread{//生产者线程
    	
    	    private Clerk clerk;
    	
    	    public Producer(Clerk clerk) {
    	        this.clerk = clerk;
    	    }
    	
    	    @Override
    	    public void run() {
    	
    	        try {
    	            sleep(10);
    	        } catch (InterruptedException e) {
    	            e.printStackTrace();
    	        }
    	        System.out.println(Thread.currentThread().getName()+";开始生产产品......");
    	
    	        while(true){
    	            clerk.produceProduct();
    	        }
    	    }
    	}
    	
    	class Consumer implements Runnable{//消费者线程
    	
    	    private Clerk clerk;
    	
    	    public Consumer(Clerk clerk) {
    	        this.clerk = clerk;
    	    }
    	
    	    @Override
    	    public void run() {
    	
    	        System.out.println(Thread.currentThread().getName()+":开始消费产品");
    	
    	        while(true){
    	            try {
    	                Thread.sleep(1);
    	            } catch (InterruptedException e) {
    	                e.printStackTrace();
    	            }
    	
    	            clerk.consumeProduct();
    	        }
    	
    	    }
    	}
    	
    	public class ProductTest {
    	
    	    public static void main(String[] args){
    	        Clerk clerk = new Clerk();
    	
    	        Producer p1 = new Producer(clerk);
    	        p1.setName("生产者1");
    	
    	        Consumer c1 = new Consumer(clerk);
    	        Thread t1 = new Thread(c1);
    	        t1.setName("消费者1");
    	
    	        p1.start();
    	        t1.start();
    	
    	    }
    	
    	}
    
    展开全文
  • 通过定时器的方式创建线程,设置线程多久后开始,每过多久再次执行一次(循环) AboutTimerThread.java package com.beikai.springboottestdemo.Thread.timer; import java.util.Date; import java.util.Timer; import ...
    通过定时器的方式创建线程,设置线程多久后开始,每过多久再次执行一次(循环)

    AboutTimerThread.java

    package com.beikai.springboottestdemo.Thread.timer;
    
    import java.util.Date;
    import java.util.Timer;
    import java.util.TimerTask;
    
    /**
     * @ClassName AboutTimerThread
     * @Description TODO
     * @Author Admin
     * @Date 2019/4/7 22:25
     * @Version 1.0
     *  定时的线程 测试类
     **/
    public class AboutTimerThread {
    
        public static void main(String[] args) throws InterruptedException {
            System.out.println("-------------start--------------");
    
            // 创建一个时间对象
            Timer timer = new Timer();
    
            // 在指定的时间执行
            //timer.schedule(task,time);
    
            // 延迟指定的时间执行
            //timer.schedule(task,delay);
    
            // 指定的时间开始,然后每隔指定时间执行
            //timer.schedule(taks,firstTime,period);
    
            timer.schedule(new TimerTask() {
                @Override
                public void run() {
                    System.out.println(new Date());
                }
            },3000,1000);
    
    
            // 主线程将睡眠
            Thread.sleep(10000);
            System.out.println("---------end---------");
        }
    
    }
    
    展开全文
  • Java线程(超详细)

    千次阅读 2021-01-12 21:14:38
    线程学习思路:为什么学习线程?为了解决CPU利用率问题,提高CPU利用率。 =》 什么是进程?什么是线程? =》 怎么创建线程?有哪几种方式?有什么特点? =》 分别怎么启动线程? =》 多线程带来了数据安全问题,该...

    多线程学习思路:为什么学习线程?为了解决CPU利用率问题,提高CPU利用率。 =》 什么是进程?什么是线程? =》 怎么创建线程?有哪几种方式?有什么特点? =》 分别怎么启动线程? =》 多线程带来了数据安全问题,该怎么解决? =》 怎么使用synchronized(同步)决解? =》使用同步可能会产生死锁,该怎么决解? =》 线程之间是如何通信的? =》 线程有返回值吗?该如何拿到? =》 怎么才能一次性启动几百上千个的线程?

    线程的概念

    什么是进程
    进程是操作系统中正在执行的不同的应用程序,例如:我们可以同时打开Word和记事本

    什么是线程
    线程是一个应用程序进程中不同的执行路径,例如:我们的WEB服务器,能够为多个用户同时提供请求服务
    进程是不活泼的。进程从来不执行任何东西,它只是线程的容器。线程总是在某个进程环境中创建的,而且它的整个寿命期都在该进程中。

    – Java的多线程
    • Java 中的多线程是通过java.lang.Thread类来实现的.
    • 一个Java应用程序java.exe,其实至少有三个线程: main()主线程, gc()垃圾回收线程,异常处理线程。当然如果发生异常,会影响主线程。
    • 使用多线程的优点。
    – 背景:以单核CPU为例,只使用单个线程先后完成多个任务(调用多个方法),肯定比用多个线程来完成用的时间更短(因为单线程的可以减少cup的调度消耗的时间),为何仍需多线程呢?
    – 多线程程序的优点:
    1.提高应用程序的响应。对图形化界面更有意义,可增强用户体验。同时做多个事情。比如:一边听歌、一边写代码。
    2.提高计算机系统CPU的利用率。
    3.改善程序结构。将既长又复杂的进程分为多个线程,独立运行,利于理解和修改。
    • 何时需要多线程
    – 程序需要同时执行两个或多个任务。
    – 需要一些后台运行的程序时。比如:Java后台运行的GC功能。
    主线程
    – 概念
    • 即使Java程序没有显示的来声明一个线程,Java也会有一个线程存在该线程叫做主线程
    • 可以调用Thread.currentThread()来获得当前线程

    线程的创建方法

    有两种方法来创建线程

    • 继承Thread类
    – MyThread extends Thread
    » 需要覆盖run方法

    • 实现Runnable接口
    – Runnable 中有一个方法run用来定义线程执行代码
    – public void run();

    后面还会介绍两种,一共是四种创建方式。

    线程的启动和终止

    线程的启动
    • 线程的启动需要调用Thread的start方法,不能直接调用run方法,如果直接调用run方法相当于方法调用。
    线程的终止
    • 当run方法返回,线程终止,一旦线程终止后不能再次启动。
    • 线程的终止可以调用线程的interrupt方法,但该方法不是最佳方法,最好是设置一个标记来控制线程的终止。
    注意事项:一个线程只能启动一次,不能多次启动同一个线程。

    线程控制基本方法
    • Thread类的有关方法(1)
    – void start():启动线程并执行对象的run()方法
    – run():线程在被调度时执行的操作
    – String getName():返回线程的名称
    – void setName(String name):设置该线程名称
    – static Thread currentThread():返回当前线程。
    • 线程控制的基本方法
    在这里插入图片描述

    线程的优先级
    – 线程的优先级越高占用CPU时间越长
    – 最高为10级,最低为1级,默认是5级
    线程的状态转换
    在这里插入图片描述
    线程创建的选择
    • 创建线程的两种方式。
    开发中:优先选择实现Runnable 接口的方式来创建线程。
    – 1.实现接口的方式没有类的单继承性的局限性。
    – 2.实现接口的方式更适合来处理多个线程有共享数据的情况。

    线程的练习
    创建三个窗口卖票,总票数为100张。(通过线程的两种实现方式分别来完成)
    方式一:

    //练习,100张票三人卖
    public class WindowTest2  implements Runnable {
    	private int ticket = 100;
    	@Override
    	public void run() {
    		while (true) {
    			if (ticket <= 0) {
    				return;
    			}
    			System.out.println(Thread.currentThread().getName() + "-->售出第:" + (101 - ticket) + "票");
    			ticket--;
    			try {
    				Thread.sleep(50);
    			} catch (InterruptedException e) {
    				e.printStackTrace();
    			}
    		}
    	}
    
    	public static void main(String[] args) {
    
    		WindowTest2 wt1 = new WindowTest2();
    		//创建三个线程,并启动,通过的是同一个对象来创建,所以票数可以是非静态的
    		new Thread(wt1).start();
    		new Thread(wt1).start();
    		new Thread(wt1).start();
    		//线程安全问题:同一个时间,多个线程访问(修改)同一个对象,造成结果不可预测(混乱)
    		//线程安全问题的条件:1.同一时间、2.多个线程一起访问、3操作的是同一个对象
    		/*输出的部分结果:
    		 * Thread-1-->售出第:1票 
    		 * Thread-0-->售出第:1票 
    		 * Thread-2-->售出第:1票 
    		 * Thread-1-->售出第:4票
    		 * Thread-2-->售出第:4票 
    		 * Thread-0-->售出第:4票 
    		 * Thread-1-->售出第:7票 
    		 * Thread-2-->售出第:7票
    		 * Thread-0-->售出第:7票
    		 */
    
    	}
    }
    
    

    方式二:

    //练习,100张票三人卖
    public class WindowTest1 extends Thread {
    	private static int ticket = 100;
    	@Override
    	public void run() {
    		while (true) {
    				if (ticket <= 0) {
    					return;
    				}
    				getTicket();
    		}
    	}
    	private static void getTicket() {
    		if(ticket==0) return;
    		System.out.println(Thread.currentThread().getName() + "-->售出第:" + (101 - ticket) + "票");
    		ticket--;
    		try {
    			sleep(100);
    		} catch (InterruptedException e) {
    			e.printStackTrace();
    		}
    	}
    	
    	public static void main(String[] args) {
    		WindowTest1 wt1 = new WindowTest1();
    		WindowTest1 wt2 = new WindowTest1();
    		WindowTest1 wt3 = new WindowTest1();
    		wt1.start();
    		wt2.start();
    		wt3.start();
    		//线程安全问题:同一个时间,多个线程访问(修改)同一个对象,造成结果不可预测(混乱)
    		//线程安全问题的条件:1.同一时间、2.多个线程一起访问、3操作的是同一个对象
    		/*输出的部分结果:
    		 * Thread-1-->售出第:1票 
    		 * Thread-0-->售出第:1票 
    		 * Thread-2-->售出第:1票 
    		 * Thread-1-->售出第:4票
    		 * Thread-2-->售出第:4票 
    		 * Thread-0-->售出第:4票 
    		 * Thread-1-->售出第:7票 
    		 * Thread-2-->售出第:7票
    		 * Thread-0-->售出第:7票
    		 */
    	}
    }
    
    

    在这里买票的三个窗口,会出现买了同一张票的问题,后面将会决解这个问题。

    线程的同步

    • 为什么需要线程同步:一个银行账号在同一时间不能接受多个线程的访问,因为这样会造成混乱
    • 线程的同步
    synchronized
    线程安全问题:
    1 同一时间
    2 多个线程
    3 操作同一个账号
    就会出现混乱情况,这种由于多线程引发的混乱情况,我们就称他为:线程安全问题
    如何解决呢?同步操作了解决线程问题。

    synchronized关键字
    • synchronized 是Java语言的关键字,当它用来修饰一个方法或者一个代码块的时候,能够保证在同一时刻最多只有一个线程执行该段代码。
    1、当两个并发线程访问同一个对象object中的这个synchronized(this)同步代码块时,一个时间内只能有一个线程得到执行。另一个线程必须等待当前线程执行完这个代码块以后才能执行该代码块。
    2、然而,当一个线程访问object的一个synchronized(this)同步代码块时,另一个线程仍然可以访问该object中的非synchronized(this)同步代码块。
    3、尤其关键的是,当一个线程访问object的一个synchronized(this)同步代码块时,其他线程对object中所有其它synchronized(this)同步代码块的访问将被阻塞。
    4、第三个例子同样适用其它同步代码块。也就是说,当一个线程访问object的一个synchronized(this)同步代码块时,它就获得了这个object的对象锁。结果,其它线程对该object对象所有同步代码部分的访问都被暂时阻塞。
    • 方式一:同步代码块
    synchronized(同步监视器){
    //需要被同步的代码
    }
    说明:
    1. 操作共享数据的代码,即为需要被同步的代码
    2.共享数据:多个线程共同操作的变量。比如: ticket 就是共享数据。
    3.同步监视器,俗称:锁。任何一个类的对象,都可以充当锁。
    补充:在实现Runnable接口创建多线程的方式中,我们可以考虑使用this充当同步监视器。

    通过第一种同步方式修改售票问题

    //练习,100张票三人卖
    public class WindowTest4 implements Runnable {
    	private int ticket = 100;
    	@Override
    	public void run() {
    		while (true) {
    			// 同步关键字括号中是同步监视器
    			if (ticket <= 0) {
    				return;
    			}
    			getTicket();
    		}
    	}
    
    	private synchronized void getTicket() {
    		if (ticket == 0)
    			return;
    		System.out.println(Thread.currentThread().getName() + "-->售出第:" + (101 - ticket) + "票");
    		ticket--;
    		try {
    			Thread.sleep(50);
    		} catch (InterruptedException e) {
    			e.printStackTrace();
    		}
    	}
    
    	public static void main(String[] args) {
    		WindowTest4 wt1 = new WindowTest4();
    		// 创建三个线程,并启动,通过的是同一个对象来创建,所以票数可以是非静态的
    		new Thread(wt1).start();
    		new Thread(wt1).start();
    		new Thread(wt1).start();
    		/*部分输出结果:
    		 * Thread-1-->售出第:1票 
    		 * Thread-1-->售出第:2票 
    		 * Thread-1-->售出第:3票 
    		 * Thread-1-->售出第:4票
    		 * Thread-2-->售出第:5票 
    		 * Thread-2-->售出第:6票 
    		 * Thread-2-->售出第:7票 
    		 * Thread-0-->售出第:8票
    		 * Thread-2-->售出第:9票 
    		 * Thread-2-->售出第:10票 
    		 * Thread-1-->售出第:11票 
    		 * Thread-1-->售出第:12票
    		 */
    	}
    }
    

    单例模式:懒汉模式的同步问题及解决

    /*
     * 单例模式:懒汉模式
     */
    public class UserManager {
    	// 懒汉模式:用的时候才创建,不用的时候为null
    	private static UserManager instance;
    	private int id;
    	private String name;
    
    	private UserManager() {
    
    	}
    	// 方式一:给方法加入synchronized关键字
    	// public static synchronized UserManager getInstance() {
    	//
    	// if(instance==null) {
    	// instance = new UserManager();
    	// }
    	// return instance;
    	// }
    
    	// 方式二:通过同步代码块的方式实现线程安全问题
    	// public static UserManager getInstance() {
    	//
    	// synchronized (UserManager.class) {
    	// if (instance == null) {
    	// instance = new UserManager();
    	// }
    	// }
    	// return instance;
    	// }
    
    	// 方式三:通过双检测机制实现对象的创建,更安全,效率更高
    	public static UserManager getInstance() {
    		if (instance == null) {
    			synchronized (UserManager.class) {
    				if (instance == null) {
    					instance = new UserManager();
    				}
    			}
    		}
    		return instance;
    	}
    
    	public int getId() {
    		return id;
    	}
    
    	public void setId(int id) {
    		this.id = id;
    	}
    
    	public String getName() {
    		return name;
    	}
    
    	public void setName(String name) {
    		this.name = name;
    	}
    }
    
    

    死锁

    线程同步带来的问题:死锁
    理解什么是死锁?
    死锁问题的产生:
     不同的线程分别占用对方需要的同步资源不放弃,都在等待对方放弃自己需要的同步资源,就形成了线程的死锁。
     出现死锁后,不会出现异常,不会出现提示,只是所有的线程都处于阻塞状态,无法继续。
    解决方法
    ➢通过逻辑算法来避免出现死锁。
    ➢尽量减少同步资源的定义。
    ➢尽量避免嵌套同步。

    线程的通信

    • wait/notify/notifyAll
    – wait():执行此方法,当前线程就进入阻塞状态,并释放同步监视器。
    – notify():执行此方法,就会唤醒被wait的一个线程。如果有多个线程被wait,就唤醒优先级高的。
    – notifyAll():执行此方法,就会唤醒所有被wait的线程。
    • 说明:

    1. wait(), notify(), notifyAll()三个方法必须使用在同步代码块或同步方法中。
    2. wait(), notify(), notifyAll三个方法的调用者必须是同步代码块或同步方法中的同步监视器否则,会出现IllegaLMonitorStateException异常.

    经典案例:生成者、消费者问题

    public class ProducerConsumer {
    
    	public static void main(String[] args) {
    		BaoziStack baoziStack = new BaoziStack();
    		Producer p1 = new Producer(baoziStack);
    		Consumer c1 = new Consumer(baoziStack);
    
    		p1.start();
    		c1.start();
    
    	}
    }
    
    // 包子类
    class Baozi {
    	int id;
    
    	public Baozi(int id) {
    		this.id = id;
    	}
    
    	@Override
    	public String toString() {
    		return "包子  : " + id;
    	}
    }
    
    // 包子筐
    class BaoziStack {
    	Baozi[] bz = new Baozi[10];
    	int index = 0;
    
    	// 装包子
    	public synchronized void pushBZ(Baozi baozi) {
    		if (index >= bz.length) {
    			try {
    				wait();
    			} catch (InterruptedException e) {
    				e.printStackTrace();
    			}
    		}
    
    		bz[index] = baozi;
    		index++;
    		notify();
    	}
    
    	// 取包子
    	public synchronized Baozi popBZ() {
    
    		if (index <= 0) {
    			try {
    				wait();
    			} catch (InterruptedException e) {
    				e.printStackTrace();
    			}
    		}
    		index--;
    		Baozi baozi = bz[index];
    		notify();
    		return baozi;
    	}
    }
    
    // 生产者:生产包子,放到包子筐里
    class Producer extends Thread {
    
    	private BaoziStack baoziStack;
    
    	public Producer(BaoziStack baoziStack) {
    		this.baoziStack = baoziStack;
    	}
    
    	@Override
    	public void run() {
    		// 生产包子(一天生产100个包子)
    		for (int i = 1; i <= 100; i++) {
    			Baozi baozi = new Baozi(i);
    			System.out.println("生产者生产了一个包子ID为: " + i);
    			baoziStack.pushBZ(baozi);
    			try {
    				sleep(50);
    			} catch (InterruptedException e) {
    				e.printStackTrace();
    			}
    		}
    	}
    }
    
    // 消费者
    class Consumer extends Thread {
    
    	private BaoziStack baoziStack;
    
    	public Consumer(BaoziStack baoziStack) {
    		this.baoziStack = baoziStack;
    	}
    
    	@Override
    	public void run() {
    		// 一天的消费量为100个包子
    		for (int i = 1; i <= 100; i++) {
    			Baozi baozi = baoziStack.popBZ();
    			System.out.println("消费者消费了一个包子ID为:" + baozi.id + "的包子");
    			try {
    				sleep(1500);
    			} catch (InterruptedException e) {
    				e.printStackTrace();
    			}
    		}
    	}
    }
    
    

    线程拓展部分内容:

    JDK5.0新增方式一:实现Callable接口
    Runnable和Callable的区别:
    1、Callable规定的方法是call(),Runnable规定的方法是run().
    2、Callable的任务执行后可返回值,而Runnable的任务是不能有返回值。
    3、call方法可以抛出异常,run方法不可以。

    在这里插入图片描述
    在这里插入图片描述

    DK5.0新增方式二:使用线程池
    背景:经常创建和销毁、使用量特别大的资源,比如并发情况下的线程,对性能影响很大。
    思路:提前创建好多个线程,放入线程池中,使用时直接获取,使用完放回池中。可以避免频繁创建销毁、实现重复利用。

    JDK5.0起提供了线程池相关API: ExecutorService 和Executors
    ExecutorService:真正的线程池接口。常见子类ThreadPoolExecutor
    void execute(Runnable command):执行任务1命令,没有返回值,-般用来执行Runnable
    < T > Future< T > submit(Callable< T > task):执行任务, 有返回值,一般来执行Callable
    void shutdown():关闭连接池
    Executors: 工具类、线程池的工厂类,用于创建并返回不同类型的线程池
    Executors.newCachedThreadPool(): 创建一个可根据需要创建新线程的线程池
    Executors.newFixedThreadPool(n); 创建一个可重用固定线程数的线程池
    Executors.newSingleThreadExecutor(): 创建一个只有一个线程的线程池
    Executors.newScheduledThreadPool(n): 创建一个线程池,它可安排在给定延迟后运行命令或者定期地执行。
    好处:
    1.提高响应速度(减少创建新线程的时间)
    2.降低资源消耗(重复利用线程池中线程,不需要每次都创建)
    3.便于线程管理
    corePoolSize:核心池的大小
    maximumPoolsize:最大线程数
    keepAliveTime:线程没有任务时最多保持多长时间后会终止

    线程池实例如下
    在这里插入图片描述
    newFixedThreadPool() 创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。
    在这里插入图片描述

    收工

    感谢阅览~~~
    看只是学习的一种输入途径而已,重要的是理解、实践和输出。输入和输出要保持好唷~~不然只有输入没有多上输出很快就会把学过的知识忘记了ヽ(*。>Д<)o゜

    展开全文
  • java创建新执行线程有两种方法。 一种方法是将类声明为 Thread 的子类。该子类应重写 Thread 类的 run 方法。接下来可以分配并启动该子类的实例。 例如,计算大于某一规定值的质数的线程可以写成: class ...

    java创建新执行线程有两种方法。
    一种方法是将类声明为 Thread 的子类。该子类应重写 Thread 类的 run 方法。接下来可以分配并启动该子类的实例。

    例如,计算大于某一规定值的质数的线程可以写成: 
    class PrimeThread extends Thread {
             long minPrime;
             PrimeThread(long minPrime) {
                 this.minPrime = minPrime;
             }
    
             public void run() {
                 // compute primes larger than minPrime
                  . . .
             }
         }
    然后,下列代码会创建并启动一个线程: 
    
    
         PrimeThread p = new PrimeThread(143);
         p.start();

    创建线程的另一种方法是声明实现 Runnable 接口的类。该类然后实现 run 方法。然后可以分配该类的实例,在创建 Thread 时作为一个参数来传递并启动。

    采用这种风格的同一个例子如下所示: 
    class PrimeRun implements Runnable {
             long minPrime;
             PrimeRun(long minPrime) {
                 this.minPrime = minPrime;
             }
    
             public void run() {
                 // compute primes larger than minPrime
                  . . .
             }
         }
    然后,下列代码会创建并启动一个线程: 
    
    
         PrimeRun p = new PrimeRun(143);
         new Thread(p).start();

    每个线程都有一个标识名,多个线程可以同名。如果线程创建时没有指定标识名,就会为其生成一个新名称。

    展开全文
  • JAVA线程和线程池

    千次阅读 2019-08-25 11:06:12
    1、线程状态 (1) 新建状态 (2) 就绪状态 (3) 运行状态 (4) 阻塞状态 (5) 死亡状态 2、线程优先级 3、同步工具synchronized、wait、notify 4、创建线程 (1) 实现 Runnable 接口 (2) 继承 Thread 类 (3) ...
  • Java线程实现接口调用

    千次阅读 2019-11-23 19:37:24
    CustQueryOneThread.java 线程类 CustInfoOneServiceImpl.java 业务逻辑类 每次取数据的mybatis的 xml文件 < select id = " selectCustInfoList " resultMap = " BaseResultMap " > select < ...
  • Java线程并发中支持并发的list对象

    千次阅读 2021-03-08 16:59:50
    Java线程并发编程中并发容器第二篇之List的并发类讲解概述本文我们将详细讲解list对应的并发容器以及用代码来测试ArrayList、vector以及CopyOnWriteArrayList在100个线程向list中添加1000个数据后的比较本文是...
  • 线程Java中不可避免的一个重要主体。下面是对“JDK中新增JUC包”之前的Java线程内容的讲解,JUC包是由Java大师Doug Lea完成并在JDK1.5版本添加到Java中的
  • java代码多线程批量插入数据

    千次阅读 2021-02-12 16:17:50
    package root.report.control.dict;import org.apache.ibatis.session.SqlSession;import root.report.db....import java.sql.Connection;import java.sql.PreparedStatement;import java.sql.SQLException;i...
  • java线程批量插入数据 带返回值

    千次阅读 2019-12-17 19:11:28
    之前写过一篇文章是关于多线程如何操作数据库,且控制事务的全局回滚,今天继续上一次进行扩展,上一次主要是针对单个线程操作没有返回值,而有时候我们希望进行多个线程批量操作数据库的同时,能返回每次成功插入到...
  • java线程安全

    千次阅读 2018-12-12 16:03:33
    线程不安全产生的主要原因:因为多个线程共享一个内存,所以当多个线程共享一个全局变量的时候,可能会受到其他干扰。 如线程更新会先在本地内存更新,然后再同步到共享内存中,当多个线程同时读写的时候,数据会...
  • JAVA线程——实现同步

    千次阅读 多人点赞 2018-07-26 17:20:33
    java允许多线程并发控制,当多个线程同时操作一个可共享的资源变量时(如数据的增删改查), 将会导致数据不准确,相互之间产生冲突,因此加入同步锁以避免在该线程没有完成操作之前,被其他线程的调用, 从而保证了...
  • 通俗易懂的java线程(又干货又可爱哦)

    千次阅读 多人点赞 2020-04-14 19:55:08
    小李开始了日复一日的修炼,然而在修炼了一年之后,小李的进境缺很慢,小李百思不得其解,正在恼怒之际,一位仙风道骨的老人传授他一门心法,名叫“多线程”,这门功法的强大之处就在于可以分心多用,同时修炼多种...
  • Java线程面试题,我丝毫不慌

    万次阅读 多人点赞 2020-07-28 09:18:51
    文章目录 一、什么是多线程 一、初识多线程 1.1介绍进程 1.2回到线程 1.3进程与线程 1.4并行与并发 1.5Java实现多线程 1.5.1继承Thread,重写run方法 1.5.2实现Runnable接口,重写run方法 1.6Java实现多线程需要注意...
  • 万字图解Java线程

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

    千次阅读 2019-06-14 16:40:27
    工欲善其事,必先利其器 ...定义:当多个线程访问某个类时,不管采用任何调度方式,不需要额外的同步或者协调,这个类都能表现出正确的结果,这个类就成为是线程安全的 线程安全性主要体现在 原子性: 互斥访问,同一时...
  • 日常项目中,经常会出现一个场景,同时批量插入数据库数据,由于逻辑复杂或者其它原因,我们无法使用sql进行批量插入,这个时候我们首先想到多线程并发插入,但是如何控制事务呢 … 直接上干货 实现效果 开启多条子...
  • java线程与并发编程详解

    千次阅读 2018-03-17 11:35:28
    一、多线程1、操作系统有两个容易混淆的概念,进程和线程。进程:一个计算机程序的运行实例,包含了需要执行的指令;有自己的独立地址空间,包含程序内容和数据;不同进程的地址空间是互相隔离的;进程拥有各种资源...
  • 最近开始学习Java线程相关的知识了,想要基础入门的话推荐读《Java线程编程核心技术》,内容偏实战,想要深入理解多线程的话推荐读《Java并发编程的艺术》和《Java并发编程实战》,最近的话在看《Java并发编程的...
  • JAVA线程基础 之十一 并发队列Queue

    千次阅读 2019-05-23 11:30:36
    并发队列Queue 在并发队列上JDK提供了...下面这张图是 Java 并发类库提供的各种各样的线程安全队列实现,注意,图中并未将非线程安全部分包含进来。 Queue 阻塞队列与非阻塞队列 阻塞队列与普通队列的区别在...
  • Java线程中锁的理解与使用

    万次阅读 多人点赞 2017-10-14 18:45:51
    锁作为并发共享数据,保证一致性的工具,在JAVA平台有多种实现(如 synchronized 和 ReentrantLock等 )
  • Java线程Condition接口原理详解

    千次阅读 2017-05-21 19:15:23
    Condition接口详解Condition定义了等待/通知两种类型的方法,当前线程调用这些方法时,需要提前获取到Condition对象关联的锁。Condition对象是由Lock对象(调用Lock对象的newCondition()方法)创建出来的,换句话
  • java线程同步的实现方式

    万次阅读 多人点赞 2019-03-08 01:47:21
    这里抛砖引玉,为何要使用同步?...下面总结一些java线程实现同步方式,大致有下面几种: 1.同步方法 使用 synchronized关键字,可以修饰普通方法、静态方法,以及语句块。由于java的每个对象都有一个内置锁...
  • 如果要将java.util.concurrent工具包中的各种工具类进行详细的功能分类,那么在这个工具包中...为了适应高并发的程序工作场景,java.util.concurrent工具提供了丰富用于高并发场景的,线程安全的Queue/Deque结构集合
  • 线程Java中不可避免的一个重要主体。...接下来的内容,是对“JDK中新增JUC包”之前的Java线程内容的讲解,涉及到的内容包括,Object类中的wait(), notify()等接口;Thread类中的接口;synchronized关键字。
  • Java面试题总结 - Java线程篇(附答案)

    千次阅读 多人点赞 2021-05-29 20:56:17
    一、线程的run()和start()有什么区别? 每个线程都是通过某个特定Thread对象所对应的方法run()来完成其操作的,run()方法称为线程体。通过调用Thread类的start()方法来启动一个线程。 start() 方法用于启动线程,...
  • 搞定Java线程:concurrent并发包梳理

    千次阅读 2020-02-21 11:07:05
    在多线程并发编程中,java.util.concurrent 是重中之重,里面提供的方法类非常实用,当然页面面试要点,需要耐心梳理。 主要分这几类, tools:CountDownLatch(闭锁)、CyclicBarrier(栅栏)、Semaphore(信号量...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 108,623
精华内容 43,449
关键字:

java新增线程

java 订阅