线程类、线程池

线程类 Thread

两个构造方法 空参和有参

空参Thread():分配一个新的thread对象,不可指定名字

有参 Thread(String name):创建一个新线程,可起名

常用方法:

start():开启线程

run():执行 描述任务

sleep(long millis):设置毫秒值 让正在执行的线程休眠

创建线程步骤

1.先继承thread类  2.重写run 3.创建子类对象(线程对象)4.调用start方法,开启线程让线程执行,让jvm调用run方法

线程对象调run和调start区别

线程调用run开启线程。调Start 开启线程,让jvm调用run在开启线程中执行

why?继承Thread类原理

Thread类是个默认类,用来描述,我们定义的这个线程做Thread的子类,是为了利用其Thread本有的功能,子继承父,并拥有它的功能

创建线程目的:

目的有单独路径,让多部分代码同时执行

原理图

看似两个方法同时在运行,其实是CPU快速的切换所以两边都在执行

package com.oracle.demo01;
新建子类
public class MyThread extends Thread{
//    重写run方法
    public void run() {
        for(int i=0;i<20;i++){
            System.out.println("myThread-"+i);
        }
        super.run();
    }
}
package com.oracle.demo01;
测试
public class Demo01 {
    public static void main(String[] args) {        
//        创建新线程(创建子类对象)
        MyThread thread=new MyThread();
//        开启线程 调start()
        thread.start();
        for(int i=0;i<20;i++){
            System.out.println("main-"+i);
        }
    }
}

创建线程方式(继承Thread类)

获取线程名称 

currentThread()用谁调用就会返回调用当前方法线程的对象

getName()以前获取方法名是用报错的方法获取名称,现在可以直接用getName来代表

在新线程里获得名称,直接用getName就可以,因为里面没有静态

在main里想获得主线程,先获得主线程的对象,在获得主线程名称

package com.oracle.demo01;
//定义一个类继承Thread
public class MyThread extends Thread{
//    重写run方法
    public void run() {
//        getName()获取当前线程名称
        System.out.println("线程名称为"+getName());
        for(int i=0;i<20;i++){
            System.out.println("myThread-"+i);
        }
        super.run();
    }
}
package com.oracle.demo01;
//用.currentThread()获取名称
public class Demo01 { public static void main(String[] args) { // 获取执行当前代码线程的线程对象 Thread th=Thread.currentThread(); // 获取该线程的名称 System.out.println(th.getName()); // 创建新线程(创建子类对象) MyThread thread=new MyThread(); // 开启线程 调start() thread.start(); for(int i=0;i<20;i++){ System.out.println("main-"+i); } } }

创建线程方式-实现Runnable接口(推荐)

接口中的方法 run():

Thread类构造方法

步骤:

1.定义类实现Runnable接口

2. 覆盖接口的run方法

3.创建Thread类对象

4 .强Runnable接口子类对象作为参数传递给Thread类的构造函数

5.调用Thread类的start()开启线程

 

package com.oracle.demo01;
//定义一个类 实现Runnable
//让它实现Runnable 让MyRunnable作为新线程任务
public class MyRunnable implements Runnable{
    
    public void run() {
        for(int i=0;i<20;i++){
            System.out.println("Thread-0"+i);
        }
    }
} 
package com.oracle.demo01;
//测试Runnable类接口
public class Demo02 {
    public static void main(String[] args) {
//        创建线程任务对象 :MyRunnable:描述任务
        MyRunnable mr=new MyRunnable();
//        创建Thread类对象:开启线程
        Thread th=new Thread(mr);
//        开启线程
        th.start();
        for(int i=0;i<20;i++){
            System.out.println("main-"+i);
        }
    }
}

实现Runnable原理为什么用Runnable

RunnableThread的接口,“推荐用接口的方式”生成线程,因为接口可以实现多继承 避免单一继承局限性,如果用Thread类,只能继承和创建Thread,如果用Runnable可以实现后可再继承,比Thread多了个备用功能 覆盖Runnable接口的run方法,将线程任务定义到run方法中。将线程的任务从线程的子类中分离出来,进行了单独的封装,按照面向对象思想将任务封装成对象

 线程匿名内部类

 匿名内部类最后得到的是子类或实现对象

package com.oracle.demo01;
//两个匿名内部类方式
public class Neibulei {
    public static void main(String[] args) {
        /*new 父类或接口名(){
            需要重写方法
        }*/
//        1.创建线程方式 继承Thread类
        new Thread(){            
//            重写run(): 写出run然后alt+/
            public void run() { 
                System.out.println(getName()+"这是新线程任务");
            }
//            匿名内部类方式:创建和开启线程一步到位
        }.start();
//        2.实现Runnable接口
        Runnable run=new Runnable(){
            public void run() {
//                获取当前执行线程对象
                String name=Thread.currentThread().getName();
                System.out.println(name+"这是匿名内部类第二个方式");
            }    
//        最后得到的是线程任务对象
        };
        new Thread(run).start();
    }
}

pm

线程池

概念:一个容纳多个线程的容器,可以反复使用,省去创先线程的操作,不用反复创建,消耗资源

线程状态

线程池原理

Runnable接口:无返回值,不可抛异常、throws 都是通过线程池工厂创建,再调用线程池中的方法获取线程,再通过线程去执行任务方法

package com.oracle.demo02;

//实现Runnable
public class MyRun implements Runnable{
//    重写里面的run方法
    @Override
    public void run() {
//        获取执行当前线程对象的名字
        String name=Thread.currentThread().getName();
        for(int i=0;i<20;i++){
            System.out.println(name+i);//描述线程任务,让线程做什么任务
//            让这个线程每次执行完都休息一下设置休息毫秒值
//            这里为什么用try catch :这里的run()是Runnable接口的方法,如果父类方法没抛异常,子继承父,实现重写方法,不能抛,只能try catch
            try {
//                打印一次线程 休眠5秒 
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }        
    }    
}

package com.oracle.demo02;

public class Demo01 {
    public static void main(String[] args) {
//        创建线程任务对象
        MyRun mr=new MyRun();
//        创建线程对象
        Thread th=new Thread(mr);
//        开启线程
        th.start();
    }
}

package
com.oracle.demo02; //用接口Runnable的方式实现线程任务 import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class Demo02 { public static void main(String[] args) { // 用线程池的方式完成线程工厂任务 // 1.从线程池工厂中获取装有两条线程池的线程池对象 ExecutorService es=Executors.newFixedThreadPool(2); // 2.创建线程任务对象 MyRun mr=new MyRun(); // 让线程池自主选一条线程执行线程任务 es.submit(mr); es.submit(mr); es.submit(mr);//这里有三个线程任务 但只能完成两条任务 第二个会等第一个的有空了就执行 // 关闭线程池(一般不关) es.shutdown();//关就调用shutdown 关闭后就无法在取线程 } }

 Callable接口:用来指定线程任务,call方法 有返回值, 可以throws,有泛型 与返回值类型相同,

import java.util.concurrent.Callable;

public class MyCallable implements Callable<String>{
    public String call() throws Exception {
        return "abc";
    }
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

public class Demo01 {
    public static void main(String[] args) throws InterruptedException, ExecutionException {
//      1.从线程池工厂中获取线程池对象
        ExecutorService es=Executors.newFixedThreadPool(2);
//      2.创建线程任务对象
        MyCallable mc=new MyCallable();
//      3.让线程池自主选择一条线程执行线程任务
        Future<String>    f=es.submit(mc);
//      4.获取线程任务的返回值
        System.out.println(f.get());
    }
}

 Future接口:记录线程任务执行完毕后产生的结果  线程池创建与使用

 get() 获取Future对象中封装的数据结果

import java.util.concurrent.Callable;

public class MyCall implements Callable<Integer>{
    private int num;
    private int num1;
    public MyCall(){}
    public MyCall(int num,int num1){
        this.num=num;
        this.num1=num1;
    }
    public Integer call() throws Exception {
//       计算求和
        int sum=0;
        for(int i=num;i<=num1;i++){
            sum+=i;
        }
        return sum;
    }
}
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

public class Demo01 {
    //用线程池计算50到100的和------44.....200的和
    public static void main(String[] args) throws InterruptedException, ExecutionException {
//      1.从线程池工厂获取线程池对象
        ExecutorService es=Executors.newFixedThreadPool(2);
//      2.创建线程任务对象
        MyCall mc1=new MyCall(50,100);
        MyCall mc2=new MyCall(44,200);
//      3.让线程池自主选择线程执行任务
        Future<Integer> f1=es.submit(mc1);
        Future<Integer>    f2=es.submit(mc2);
//      4.获取线程任务返回值
        int sum1=f1.get();
        int sum2=f2.get();
        System.out.println(sum1);
        System.out.println(sum2);
    }
}

多线程

线程安全:多线程和单线程每次运行结果相同,其他变量值也和预期一样,就是线程安全

线程同步:Synchronized

①、同步代码块:

  synchronized (锁对象) {

        可能会产生线程安全问题的代码

}
public class Tickets implements Runnable {
    // 创建售票类
    // 定义100张票
    private int tickect = 100;
    // 定义同步锁--必须定义在外面 表示所有的线程都使用同一个锁!
    Object obj = new Object();
    public void run() {
        while (true) {
            /*同步代码块将可能会产生线程安全问题的代码包起来---
            各个线程经过时都需要带着锁obj而进行下一步,如果没有的话需要在此等待。
            同时这个线程结束的时候将锁归还,这样下一个线程就可以拿着这个锁进行下一步操作!
            obj可以写成本类对象的this,因为其唯一性,不需要上面的定义!*/
            synchronized (obj) {
                if (tickect > 0) {
                    try {
                        //假设休眠50--这里是制造线程不安全的情况!只是举例的一种!!
                        Thread.sleep(50);
                    } catch (InterruptedException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                    // 先赋值后--
                    System.out.println(Thread.currentThread().getName() + ":出售第" + tickect-- + "张票");
                }
            }
        }
    }
}
public class Demo01 {
    public static void main(String[] args) {
        //1.创建线程任务
        Tickets tickets=new Tickets();
        //创建三个线程完成同一个任务
        Thread t1=new Thread(tickets);
        Thread t2=new Thread(tickets);
        Thread t3=new Thread(tickets);
        //开启线程
        t1.start();
        t2.start();
        t3.start();
    }
}

同步代码方法

public synchronized void method(){

       可能会产生线程安全问题的代码

  }
public class Tickets2 implements Runnable {
    // 创建售票类
    // 定义100张票
    private int tickect = 100;
    // 定义同步锁--必须定义在外面 表示所有的线程都使用同一个锁!
    Object obj = new Object();
    public void run() {
        while (true) {
            method();
        }
    }
    public synchronized void method() {
        if (tickect > 0) {
            try {
                // 假设休眠50--这里是制造线程不安全的情况!只是举例的一种!!
                Thread.sleep(50);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            // 先赋值后--
            System.out.println(Thread.currentThread().getName() + ":出售第" + tickect-- + "张票");
        }
    }
}

静态同步方法: 在方法声明static synchronized

public static synchronized void method(){

    可能会产生线程安全问题的代码

  }

静态同步方法中的锁对象类名.class(字节码(.class)文件的对象<字节码文件一进内存就自动生成字节码文件对象>唯一性)

lock接口

lock 获取锁   unlock 释放锁

package com.oracle.demo01;
//接口锁
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class Tickets3 implements Runnable {
    // 定义100张票
    private int tickect = 100;
    //创建锁接口对象---用于解决可能出现因为异常而导致锁不被还回来
    Lock lock=new ReentrantLock();
    public void run() {
        while (true) {
            //获取锁
            lock.lock();
            if (tickect > 0) {
                try {
                    // 假设休眠50--这里是制造线程不安全的情况!只是举例的一种!!
                    Thread.sleep(50);
                    System.out.println(Thread.currentThread().getName() + ":出售第" + tickect-- + "张票");
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }finally{
                    //释放锁
                    lock.unlock();        
                }
                // 先赋值后--                
            }
        }
    }
}
posted @ 2019-06-21 21:39  zhansen521  阅读(188)  评论(0编辑  收藏  举报