JAVA学习笔记08——多线程

多线程

1.基本概念
并发:两个或者多个事件在同一时间段内发生
并行:两个或者多个事件在同一时刻发生

进程:一个内存中运行的应用程序,每个进程都有一个独立的内存空间,一个应用程序可以同时运行多个进程;进程也是程序依此执行过程,是系统运行程序的基本单位;系统运行一个程序即是一个进程从创建运行到消亡的过程
线程:进程中的一个执行单元,负责当前进程中程序的执行,一个进程中至少有一个线程,一个进程中是可以有多个线程的

2.主线程
所有的线程对象都必须是Thread类或者其子类的实例。每个线程的作用是完成一定的任务,实际上就是执行一段程序流即一段顺序执行的代码
单线程程序:JAVA程序中只有一个线程执行从main方法开始从上到下依次执行

创建线程类

//创建一个Thread类的子类
public class MyThread extends Thread{
    
    
    //在子类中重写Thread类中的run方法,设置线程任务
    @Override
    public void run() {
    
    
        for (int i=0;i<20;i++){
    
    
            System.out.println("Thread子类方法"+i);
        }
    }
}
public class Demo01 {
    
    
    public static void main(String[] args) {
    
    
        //创建Thread子类对象
        MyThread mt=new MyThread();
        //调用Thread类中的start()方法,开启新的线程执行run方法
        mt.start();
        for (int i=0;i<20;i++){
    
    
            System.out.println("main方法"+i);
        }
    }
}

3.Thread类
构造方法:
public Thread():分配一个新的线程对象
public Thread(String name):分配一个指定名字的新的线程对象
public Thread(Runnable target):分配一个带有指定目标新的线程对象
public Thread(Runnable target,String name):分配一个带有指定目标新的线程对象并指定名字
常用方法:
public String getName():获取当前线程名称
public void start():导致此线程开始执行
public void run():此线程要执行的任务在此处定义代码
public static void sleep(long millis):使当前正在执行的线程以指定的毫秒数暂停
public static Thread currentThread():返回当前正在执行的线程对象的引用

//创建一个Thread类的子类
public class MyThread extends Thread{
    
    
    //在子类中重写Thread类中的run方法,设置线程任务
    @Override
    public void run() {
    
    
        //获取线程名称
        String name=getName();
        System.out.println(name);
    }
}
//创建一个Thread类的子类
public class MyThread extends Thread{
    
    
    //在子类中重写Thread类中的run方法,设置线程任务
    @Override
    public void run() {
    
    
        //获取线程名称
        Thread t=Thread.currentThread();
        System.out.println(t);
    }
}
public class Demo01 {
    
    
    public static void main(String[] args) {
    
    
        //创建Thread子类对象
        MyThread mt=new MyThread();
        //调用Thread类中的start()方法,开启新的线程执行run方法
        mt.start();
        new MyThread().start();
        //Thread-0
        //Thread-1
    }
}

设置线程名称:

/*
    设置线程名称:
    1.使用Thread类中的方法setName(名字)
    void setName(String name)改变线程名称使之与name相同
    2.创建一个带参数的构造方法,参数传递线程的名称,把线程名称传递给父类
    让父类给子线程起一个名字 Thread(String name)分配新的Thread对象
 */
public class MyThread extends Thread{
    
    
    public MyThread() {
    
    
    }

    public MyThread(String name) {
    
    
        super(name);    //把线程名称传递给父类
        // 让父类给子线程起一个名字 Thread(String name)分配新的Thread对象
    }

    @Override
    public void run() {
    
    
        //获取线程名称
        System.out.println(Thread.currentThread().getName());
    }
}
public class Demo01 {
    
    
    public static void main(String[] args) {
    
    
        MyThread mt=new MyThread();
        //调用Thread类中的start()方法,开启新的线程执行run方法
        mt.setName("线程名称1");
        mt.start();
        new MyThread("线程名称2").start();
    }
}

线程sleep

public class Demo01 {
    
    
    public static void main(String[] args) {
    
    
        for(int i=1;i<=30;i++){
    
    
            System.out.println(i);
            try{
    
    
                Thread.sleep(1000);
            }
            catch(InterruptedException e){
    
    
                e.printStackTrace();
            }
        }
    }
}

创建线程二
Runnable接口应该由那些打算通过某一线程执行其实例的类来实现。类必须定义一个称为run的无参数方法

//创建一个Runnable接口的实现类
public class RunnableImpl implements Runnable{
    
    
    //2.在实现类中重写Runnable接口的run方法,设置线程任务
    @Override
    public void run() {
    
    
        for (int i=0;i<10;i++){
    
    
            System.out.println(Thread.currentThread().getName()+i);
        }
    }
}
public class Demo02 {
    
    
    public static void main(String[] args) {
    
    
        //创建一个Runnable接口的实现类对象
        Runnable run=new RunnableImpl();
        //创建一个Thread类对象,开启新的线程执行run方法
        Thread t=new Thread(run);
        //调用Thread类中的start方法,开启新线程执行run方法
        t.start();
        for (int i=0;i<10;i++){
    
    
            System.out.println(Thread.currentThread().getName()+i);
        }
    }
}

Thread与Runnable区别
实现Runnable接口比继承Thread所具有的优势:
1.适合多个相同的程序代码的线程去共享同一个资源
2.可以避免JAVA中的单继承的局限性
3.增加程序的健壮性,实现解耦操作,代码可以被多个线程共享,代码和线程独立
4.线程池只能放入实现Runnable或者Callable类线程,不能直接放入继承Thread类

匿名内部类方式实现线程创建
简化代码,把实现接口,重写接口中的方法,创建实现类对象合成一部完成
语法:
new 父类/接口(){
重写父类/接口中的方法
}

public class Demo03 {
    
    
    public static void main(String[] args) {
    
    
        new Thread(){
    
    
            @Override
            public void run() {
    
    
                for (int i=0;i<10;i++){
    
    
                    System.out.println(Thread.currentThread().getName()+i);
                }
            }
        }.start();
        //线程接口方式
        Runnable r=new Runnable(){
    
    
            @Override
            public void run() {
    
    
                for (int i=0;i<10;i++){
    
    
                    System.out.println(Thread.currentThread().getName()+i);
                }
            }
        };
        new Thread(r).start();
    }
}

4.线程安全与同步
完成同步操作:
1.同步代码块
synchronized关键字可以用于方法中的某个区块中,表示只对这个区块的资源实行互斥访问。
语法:
synchronized(同步锁){需要同步操作的代码}
同步锁:
1.锁对象可以任意类型
2.多个线程对象,需要同一把锁

public class RunnableImpl implements Runnable{
    
    
    private int ticket=100;
    Object obj=new Object();
    @Override
    public void run() {
    
    
        while(true){
    
    
            synchronized (obj){
    
    
                if(ticket>0){
    
    
                    try{
    
    
                        Thread.sleep(10);
                    }catch(InterruptedException e){
    
    
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName()+"正在卖第"+ticket+"张票");
                    ticket--;
                }
            }
        }
    }
}
public class Demo01 {
    
    
    public static void main(String[] args) {
    
    
        RunnableImpl run=new RunnableImpl();
        Thread to=new Thread(run);
        Thread t1=new Thread(run);
        Thread t2=new Thread(run);
        to.start();
        t1.start();
        t2.start();
    }
}

2.同步方法
使用synchronized修饰的方法,叫做同步方法。保证A线程执行该方法的时候,其他线程只能在方法外等待
语法:
public synchronized void method(){可能产生线程安全问题的代码}
对于非static方法,同步锁是this
对于static方法,我们使用当前方法所在类的字节码对象

public class RunnableImpl implements Runnable{
    
    
    private int ticket=100;
    Object obj=new Object();
    @Override
    public void run() {
    
    
        while(true){
    
    
            method();
        }
    }
    public synchronized void method(){
    
    
        if(ticket>0){
    
    
            try{
    
    
                Thread.sleep(10);
            }catch(InterruptedException e){
    
    
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+"正在卖第"+ticket+"张票");
            ticket--;
        }
    }
}

3.锁机制
public void lock():加同步锁
public void unlock():释放同步锁

public class RunnableImpl implements Runnable{
    
    
    private int ticket=100;
    Object obj=new Object();
    Lock l=new ReentrantLock();
    @Override
    public void run() {
    
    
        while(true){
    
    
            l.lock();
            if(ticket>0){
    
    
                try{
    
    
                    Thread.sleep(10);
                }catch(InterruptedException e){
    
    
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName()+"正在卖第"+ticket+"张票");
                ticket--;
            }
            l.unlock();
        }
    }
}

5.线程通信
概念:多个线程在处理同一个资源,但是处理动作却不相同
public void wait():在其他线程调用此对象的notify()方法或者notifyAll()导致当前线程等待
public void notify():唤醒在此对象监视器上等待的单个线程,会继续执行wait方法之后代码

public class Demo02 {
    
    
    public static void main(String[] args) {
    
    
        //创建锁对象
        Object obj=new Object();
        //创建一个消费者
        new Thread(){
    
    
            @Override
            public void run() {
    
    
                synchronized (obj){
    
    
                    System.out.println("需求资源");
                    //调用wait方法,放弃cpu的执行,进入到WAITING状态
                    try {
    
    
                        obj.wait();
                    } catch (InterruptedException e) {
    
    
                        e.printStackTrace();
                    }
                    //唤醒之后代码
                    System.out.println("消费资源");
                }
            }
        }.start();
        //创建生产者
        new Thread(){
    
    
            @Override
            public void run() {
    
    
                try {
    
    
                    Thread.sleep(5000);
                } catch (InterruptedException e) {
    
    
                    e.printStackTrace();
                }
                synchronized (obj){
    
    
                    System.out.println("产生资源");
                    obj.notify();
                }
            }
        }.start();
    }
}

进入到TimeWaiting两种方式:
1.使用sleep(long m)方法,在毫秒值结束之后,线程进入到Runnable/Blocked状态
2.使用wait(long m)方法,wait方法如果在毫秒值结束之后,还没有被notify()唤醒,就会自动进入到Runnable/Blocked状态
唤醒方法:
void notify():唤醒在此对象监视器上等待的单个线程
void notifyAll():唤醒所有等待线程

6.线程池
可以容纳多个线程的容器,其中的线程可以反复利用,省去了频繁的创建线程对象的操作,无需反复创建线程而消耗过多的资源
当程序第一次启动的时候,创建多个线程,保存到一个集合中;当我们想要使用线程的时候,就可以从集合中取出线程使用
Thread t=list.remove():返回的是被移除的对象
Thread t=linked.removeFirst()
当使用完毕线程,需要把线程归还给线程池
list.add(t) linked.addLast(t);
线程池优点:
1.降低资源消耗。减少创建和销毁线程的次数,每个工作线程都可以被重复利用,可执行多个任务
2.提高响应速度。当任务到达时,任务可以不需要等到线程创建就能立即执行
3.提高线程的可管理性。可以根据系统的承受能力,调整线程池中工作线线程的数目,防止消耗过多内存

使用方法
java.util.concurrent.Executors:线程池的工厂类,用来生成线程池
Executors类中的静态方法:
static ExecutorService newFixedThreadPool(int nThreads) 创建一个可重用固定线程数的线程池
参数:
int nThreads:创建线程池中包含的线程数量
返回值:返回的是ExecutorService接口的实现类对象,我们可以使用ExecutorService接口接收
线程池使用步骤:
1.使用线程池的工厂类Executors里面提供的静态方法newFixedThreadPool生产一个指定线程数量的线程池
2.创建一个类,实现Runnable接口,重写run方法,设置线程任务
3.调用ExecutorService中的方法submit,传递线程任务(实现类),开启线程,执行run方法
4.调用ExecutorService中的方法shutdown销毁线程池

public class RunnableImpl implements  Runnable{
    
    
    @Override
    public void run() {
    
    
        //创建一个类,实现Runnable接口,重写run方法,设置线程任务
        System.out.println(Thread.currentThread().getName()+"已经创建一个新线程");
    }
}
public class Demo01 {
    
    
    public static void main(String[] args) {
    
    
        //使用线程池的工厂类Executors里面提供的静态方法newFixedThreadPool生产一个指定线程数量的线程池
        ExecutorService es= Executors.newFixedThreadPool(2);
        es.submit(new RunnableImpl());
        es.shutdown();
    }
}

7.Lambda表达式

public class Demo03 {
    
    
    //使用Lambda表达式
    public static void main(String[] args) {
    
    
        new Thread(()->{
    
    
            System.out.println(Thread.currentThread().getName()+"创建新线程");
        }).start();
    }
}

语义分析
Runnable接口只有一个run方法的定义:
public abstract void run();
即制定了一种做事情的方案:
无参数:不需要任何条件即可执行
无返回值:该方案不产生任何结果
代码块:该方案的具体执行步骤

对于Lambda表达式:
()->System.out.println()
前面一对小括号即run方法的参数,代表不需要任何条件
中间的一个箭头代表将前面的参数传递给后面的代码
后面的输出语句即业务逻辑代码

语法:
(参数列表)->{一些重写方法的代码}
无参数无返回值

public class Demo03 {
    
    
    public static void main(String[] args) {
    
    
        /*Function(new Method() {
            @Override
            public void method() {
                System.out.println("Lambda表达式");
            }
        }); 原方法
         */
        Function(()->{
    
    
            System.out.println("Lambda表达式");
        });
    }
    private static void Function(Method m){
    
    
        m.method();
    }
}

有参数有返回值

public class Demo04 {
    
    
    public static void main(String[] args) {
    
    
        Person[] array={
    
    
                new Person("Kobe",24),
                new Person("James",23),
                new Person("Curry",30),
                new Person("Kobe",8)
        };
        Arrays.sort(array,(Person p1,Person p2)->{
    
    
            return p1.getAge()-p2.getAge();
        }); //Lambda表达式
        for (Person person : array) {
    
    
            System.out.println(person.getName()+":"+person.getAge());
        }
    }
}

省略规则:
1.小括号内参数的类型可以省略
2.如果小括号内有且仅有一个参数,则小括号可以省略
3.如果大括号内有且仅有一个语句,则无论是否有返回值,都可以省略大括号,return关键字及语句分号

猜你喜欢

转载自blog.csdn.net/qq_44708714/article/details/106916417