java复习七:多线程

程序-进程-线程

程序:静态的代码

进程:运行中的程序

线程:一个进程中可能有多个线程,每个线程有自己独立的虚拟机栈和程序计数器,共享方法区和堆

java应用程序里至少有三个线程:main主线程,gc垃圾回收线程,异常处理线程

多线程优点:
1 提高应用程序的相应,堆图形化界面更有意义,增强用户体验

2 提高系统的CPU利用率

3 修改程序结构。将即长又复杂的进程分为多个线程,独立运行,利于理解和修改

何时需要多线程:

1 程序需要同时执行两个或多个任务

2 程序需要实现一些等待的任务时,如用户输入,文件读写。。。

3 需要一些后台运行的程序时。

创建多线程
通过继承Thread类实现多线程
/**
 * TODO
 * 创建多线程
 *
 * 步骤
 * 1 继承Thread类的子类
 * 2 重写Thread类的run方法
 * 3 创建Thread子类的对象
 * 4 通过此对象调用start()
 * @Author: xiazhui
 * version 1.0
 * @Date: 2022/07/29/16:20
 */
public class ThreadTest {
    public static void main(String[] args) {
        MyThread myThread = new MyThread();
        //start方法:1启用当前线程,2调用run方法
        myThread.start();
        //使用run不会创建新线程
        myThread.run();

        //不可以让已经start的线程再执行,会有IllegalThreadStateException
        //myThread.start();
        //需要创建多个对象
        MyThread myThread1=new MyThread();
        myThread1.start();
        for (int i=0;i<100;i++){
            if (i%3==0)
            {

                System.out.println("-------------------------------");
            }
        }

    }
}
class MyThread extends Thread{
    @Override
    public void run() {
        for (int i=0;i<100;++i){
            System.out.println(MyThread.currentThread().getName()+i);
        }
    }
}
Thread中常用方法
package javaBasic;

import exer.ThreadTest1;

/**
 * TODO
 * 测试Thread中的常用方法
 * 1 start():启动当前线程,调用线程的run()方法
 * 2 run(): 通常需要重写Thread类中的此方法,将创建的线程要执行的操作声明在此方法中
 * 3 currentThread():静态方法,返回当前代码执行的线程
 * 4 getName():获取当前线程的名字
 * 5 setName(): 设置当前线程的名字
 * 6 yield(): 释放当前cpu的执行权
 * 7 join(): 在线程a中调用线程b的join(),此时线程a就进入阻塞状态
 *           直到线程b完全执行完之后,线程a才结束阻塞状态
 * 8 stop(): 已过时。当执行此方法时,强制结束当前线程
 * 9 static sleep(long millitime): 让档期那线程"睡眠"指定的millitime毫秒,在指定的millitime时间内,线程时阻塞状态
 * 10 isAlvie(): 判断当前线程是否存活
 * @Author: xiazhui
 * version 1.0
 * @Date: 2022/07/29/17:24
 */
public class ThreadMethodTest {
    public static void main(String[] args) {
        Thread.currentThread().setName("主方法");
        System.out.println(Thread.currentThread().getName());

        ThreadMethodTest1 threadMethodTest1 = new ThreadMethodTest1("大头");
        //threadMethodTest1.setName("线程");
        threadMethodTest1.start();
        for (int i=0;i<100;i++){
            System.out.println(i);
            if (i==20){
                try {
                    threadMethodTest1.join();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}
class ThreadMethodTest1 extends Thread{
    public ThreadMethodTest1(String name){
        super(name);
    }

    public ThreadMethodTest1(){

    }

    @Override
    public void run() {
        for (int i=0;i<100;i++){
            if (i%2==0){
                System.out.println(getName()+":"+i);
            }
            if(i%20==0){
                yield();
            }
        }
    }
}
通过实现Runnable接口创建多线程
package runnableTest;

/**
 * TODO:
 * 实现runnable接口的方式创建多线程
 *
 * 步骤
 * 1 创建类实现runnable接口
 * 2 实现类去实现run()方法
 * 3 创建实现类的对象
 * 4 将此对象传递到Thread类的构造器中,创建Thread类的对象
 * 5 通过Thread类的对象调用start()方法
 * @Author: xiazhui
 * version 1.0
 * @Date: 2022/07/30/16:44
 */
public class RunnableTest {
    public static void main(String[] args) {
        MyThread myThread = new MyThread();
        Thread thread=new Thread(myThread);
        //run()方法。如果target不为空,那么调用Runable类型的target的run方法
        thread.start();

        Thread thread1=new Thread(myThread);
        thread1.start();
    }
}
class MyThread implements Runnable{

    @Override
    public void run() {
        for (int i=0;i<100;i++){
            if (i%2==0){
                System.out.println(Thread.currentThread().getName()+i);
            }
        }
    }
}
通过实现Callable接口创建多线程
package com.xiazhui.java;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

/**
 * TODO
 * 通过实现Callable接口实现多线程
 *
 * 步骤
 * 1 定义Callable的实现类
 * 2 重写Callable的call方法
 * 3 创建Callable的实现类的对象
 * 4 将Callable的实现类的对象传入FutureTask构造器创建FutureTask的对象
 * 5 将FutureTask的对象传入Thread类并调用start()方法
 * 6 如果需要返回值,可以通过FutureTask的get()方法得到call方法的返回值
 *
 * 小结:Callable接口的方式创建多线程相对于实现Runnable接口创建多线程的优点
 *      1 call()可以有返回值
 *      2 call()可以抛出异常,被外面的操作捕获,获取异常的信息
 *      3 Callable是支持泛型的
 *
 *  Callable需要借助FutureTask类获取返回值
 *  关于Future接口
 *      1 可以对具体Runnable,Callable任务的执行结果进行取消,查询是否完成,获取结果等
 *      2 FutureTask是Future接口的唯一实现类
 *      3 FutureTask同时实现了Runnable,Future接口,它既可以作为Runnable线程被执行,
 *        又可以作为Future得到Callable的返回值
 * @Author: xiazhui
 * version 1.0
 * @Date: 2022/08/01/8:58
 */
public class CallableTest {
    public static void main(String[] args) {
        Sum sum = new Sum();
        FutureTask futureTask = new FutureTask(sum);
        new Thread(futureTask).start();

        try {
            Object result=futureTask.get();
            System.out.println(result);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }

    }
}
class Sum implements Callable{

    @Override
    public Object call() throws Exception {
        int sum=0;
        for (int i=0;i<100;i++){
            if(i%2==0){
                System.out.println(i);
                sum+=i;
            }
        }
        return sum;
    }
}
使用线程池创建多线程
package com.xiazhui.java;

import java.util.concurrent.*;

/**
 * TODO
 * 通过数据库连接池创建线程
 *
 * 1 通过Excutors的方法创建数据库连接池对象
 * 2 将实现了Runnable接口和实现了Callable接口的方法
 *   传给连接的excute和submit方法执行
 * 3 调用数据库连接池对象的shutdown方法关闭数据库连接池
 *
 * 线程池的好处:
 * 1 提高响应速度(减少了创建线程的时间)
 * 2 降低资源消耗(重复利用线程池中线程,不需要每次都创建)
 * 3 便于线程管理
 *
 * corePoolSize 核心池的大小
 * maximumPoolsize 最大线程数
 * keepAliveTime 线程没有任务时最多会保持多长时间会终止
 *
 * @Author: xiazhui
 * version 1.0
 * @Date: 2022/08/01/9:35
 */
public class ThreadPool {
    public static void main(String[] args) {
        //此处的ExcutorService是一个接口,是一个多态的用法
        // ThreadPoolExecutor继承了AbstractExecutorService类
        // 而AbstractExecutorService类实现了executorService实现了ExecutorService接口
        ExecutorService executorService = Executors.newFixedThreadPool(10);
        ThreadPoolExecutor threadPoolExecutor= (ThreadPoolExecutor) executorService;

        threadPoolExecutor.setKeepAliveTime(3, TimeUnit.SECONDS);

        executorService.execute(new Runnable() {
            @Override
            public void run() {
                for (int i=0;i<100;i++){
                    if (i%2==0) System.out.println(Thread.currentThread().getName()+":"+i);
                }
            }
        });

        executorService.submit(new Callable() {
            @Override
            public Object call() throws Exception {
                for (int i=0;i<100;i++){
                    if (i%2!=0) System.out.println(Thread.currentThread().getName()+":"+i);
                }
                return null;
            }
        });

        executorService.shutdown();
    }
}
/**
 * TODO:
 * Thread的匿名子类
 * @Author: xiazhui
 * version 1.0
 * @Date: 2022/07/29/16:48
 */
public class ThreadTest1 {
    public static void main(String[] args) {
       new Thread(){
            @Override
            public void run() {
                for (int i=0;i<100;i++){
                    if (i%2!=0){
                        System.out.println(Thread.currentThread().getName()+":"+i);
                    }
                }
            }
        }.start();

        new Thread(){
            @Override
            public void run() {
                for (int i=0;i<100;i++){
                    if(i%2==0){
                        System.out.println(Thread.currentThread().getName()+":"+i);
                    }
                }
            }
        }.start();

    }
}
解决线程安全问题
同步代码块解决实现runnable接口的线程安全问题

/**
 * TODO
 * 通过实现Runnable接口的方式创建线程实现售票,解决线程同步
 *
 * 方法一:同步代码块
 * synchronized(同步监视器){
 *     //需要被同步的代码
 * }
 * 说明:操作共享数据的代码,即为需要被同步的代码
 *      共享数据:多个线程共同操作的变量。比如:ticket
 *      同步监视器,俗称:锁,任何一个类的对象,都可以充当锁
 *          要求:多个线程必须要共用同一把锁
 *
 *
 * 同步的方式,解决了线程的安全问题。
 * 操作同步代码时,只能有一个线程参与,其他线程等待。相当于是一个单线程的过程,效率低。
 *
 * implements实现中可以考虑使用this作为关键字
 * 继承Thread实现中慎用this作为关键字
 *
 *
 * @Author: xiazhui
 * version 1.0
 * @Date: 2022/07/30/17:16
 */
public class ProperityTest1 {
    public static void main(String[] args) {
        MyThread1 myThread1 = new MyThread1();
        //新建的三个线程共用一个Runnable的实现类,因为共用票数
        Thread thread1 = new Thread(myThread1);
        Thread thread2 = new Thread(myThread1);
        Thread thread3 = new Thread(myThread1);

        thread1.setName("线程1");
        thread2.setName("线程2");
        thread3.setName("线程3");

        thread1.start();
        thread2.start();
        thread3.start();
    }

}
class MyThread1 implements Runnable{

    private int ticket=100;
    //Object obj=new Object();
    @Override
    public void run() {
        while (true){

            synchronized(this) {
                if (ticket > 0) {

                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }

                    System.out.println(Thread.currentThread().getName() + ":" + ticket--);
                } else break;
            }
        }
    }
}
同步代码块解决继承Thread类的线程安全问题
import java.awt.*;

/**
 * @Author: xiazhui
 * version 1.0
 * @Date: 2022/07/31/12:54
 */
public class ProperityTest2 {
    public static void main(String[] args) {
        MyThread2 myThread2=new MyThread2();
        MyThread2 myThread3=new MyThread2();
        MyThread2 myThread4=new MyThread2();
        myThread2.start();
        myThread3.start();
        myThread4.start();
    }
}
class MyThread2 extends Thread{
    private static int ticket=100;
    //private static Object object=new Object();
    @Override
    public void run() {
        while (true) {
            //类加载器是唯一的,也可以用。类加载器是class的对象
            synchronized (Window.class) {
                if (ticket > 0) {
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(getName() + ticket--);
                } else
                    break;
            }
        }
    }
}

同步方法解决实现runnable接口的线程安全问题

class MyThread1 implements Runnable{

    private int ticket=100;
    //Object obj=new Object();
    @Override
    public void run() {
        while (ticket>0){

            method();
        }
    }
    //锁默认使用的this
    private synchronized void method(){
        if (ticket > 0) {
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            System.out.println(Thread.currentThread().getName() + ":" + ticket--);
        }
    }
}
同步方法解决继承Thread的线程安全问题
class Windows2 extends Thread{
    private static int ticket=100;
    //private static Object object=new Object();
    @Override
    public void run() {
        while (true) {
            show();
        }
    }

    //这里用的锁是Windows2.class,即Windows2的字节码文件
    private static synchronized void show(){
        if (ticket > 0) {
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + ticket--);
        }
    }

}

使用lock解决线程安全问题

package t4_lockTest;

import java.util.concurrent.locks.ReentrantLock;

/**
 * 
 * TODO
 * 使用lock锁解决线程安全问题
 *
 * @Author: xiazhui
 * version 1.0
 * @Date: 2022/07/31/16:42
 */
public class LockTest {
    public static void main(String[] args) {
        Window window = new Window();

        Thread thread1 = new Thread(window);
        Thread thread2 = new Thread(window);
        Thread thread3 = new Thread(window);

        thread1.start();
        thread2.start();
        thread3.start();

    }
}
class Window implements Runnable{

    private static int ticket=100;

    //参数为true时线程分配保证先进先出,无参默认为false,线程抢占
    private ReentrantLock lock=new ReentrantLock(true);

    @Override
    public void run() {
        while (ticket>0){
            try {
                lock.lock();
                if (ticket>0){
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName()+ticket--);

                }else
                    break;
            //如果出现异常也要把锁解开,不然线程会锁住    
            }finally {
                lock.unlock();
            }

        }
    }
}

lock和synchronized的异同:

​ 同:都是为了解决线程安全问题

​ 异:synchronized机制在执行完相应的同步代码以后,自动的释放同步监视器

​ lock需要手动的启动同步lock(),同时结束同步也需要手动的实现unlock()

​ Lock只有代码块锁,synchronized有方法锁

​ Lock锁性能过更好,JVM将花费较少的时间来调度线程,并且具有更好的扩展性(提供了更多的子类)

优先使用顺序:lock,synchronized同步代码块,synchronized同步方法

死锁

不同的线程分别占用对方需要的同步资源不放弃,都在等待对方放弃自己需要的同步资源,就形成了线程的死锁

说明:

​ 出现死锁后,不会出现异常和提示,只是涉及的线程都处于阻塞状态

​ 使用同步时,要避免出现死锁

解决方法:

​ 专门的算法,原则

​ 尽量减少同步资源的定义

​ 尽量避免嵌套同步

package t3_deadlockTest;

/**
 * @Author: xiazhui
 * version 1.0
 * @Date: 2022/07/31/16:31
 */
public class ThreadTest {
    public static void main(String[] args) {
        StringBuffer stringBuffer1 = new StringBuffer();
        StringBuffer stringBuffer2 = new StringBuffer();

        new Thread(){
            @Override
            public void run() {
                synchronized (stringBuffer2){
                    stringBuffer1.append(1);
                    stringBuffer2.append('a');
                    try {
                        Thread.sleep(2000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }

                synchronized (stringBuffer1){
                    stringBuffer1.append(2);
                    stringBuffer2.append('b');
                }
                System.out.println(stringBuffer1);
                System.out.println(stringBuffer2);
            }
        }.start();

        new Thread(){
            @Override
            public void run() {
                synchronized (stringBuffer1){
                    stringBuffer1.append(3);
                    stringBuffer2.append('c');
                    try {
                        Thread.sleep(2000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }

                synchronized (stringBuffer2){
                    stringBuffer1.append(4);
                    stringBuffer2.append('d');
                }
                System.out.println(stringBuffer1);
                System.out.println(stringBuffer2);
            }
        }.start();
    }
}

线程通信

package com.xiazhui.java;

/**
 * TODO
 * 线程通信:交替打印1-100
 *
 * 涉及到的方法
 * 1 wait():一旦执行此方法,线程就进入阻塞,并且释放同步监视器
 * 2 notify():一旦执行此方法,就会唤醒被wait的一个线程。如果有多个
 *      线程wait,就唤醒优先级最高的一个
 * 3 notifyAll():一旦执行此方法,就会唤醒所有被wait的线程
 *
 * 说明:
 * 1 wait(),notify(),notifyAll()三个方法必须
 *      使用在同步代码块或同步方法中
 * 2 wait(),notify(),notifyAll()三个方法的调用
 *      者必须是同步代码块或同步方法中的同步检测器,
 *      否则会出现IllegalMonitorStateException异常
 * 3 wait(),notify(),notifyAlll()都是定义在java.lang.Object下的
 * @Author: xiazhui
 * version 1.0
 * @Date: 2022/08/01/6:51
 */
class Communication implements Runnable{
    private int number=1;
    @Override
    public void run() {
        while (number<=100){
            synchronized (this) {
                notify();
                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName()+":"+number++);
                try {
                    wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}
public class CommunicationTest {
    public static void main(String[] args) {
        Communication communication = new Communication();
        Thread thread1 = new Thread(communication);
        Thread thread2 = new Thread(communication);

        thread1.start();
        thread2.start();
    }
}

sleep()和wait()的异同

1 相同点:一旦执行方法,都可以使当前线程进入阻塞状态

2 不同点:两个方法的声明位置不同:Thread类中生命滚sleep,wait在java.lang.Objec中

​ 调用不同:sleep()可以在任何需要的场景下使用

​ wait()必须使用在同步方法活着同步代码块之中

​ 关于是否释放同步监视器:sleep()不会释放同步监视器

​ wait()会释放同步监视器

会释放锁的操作

​ 1 同步代码块或者同步方法执行完毕

​ 2 同步代码块或者同步方法中遇到break或者return导致结束

​ 3 同步代码块中遇到没有处理的Exception或者Error异常

​ 4 在同步代码块或者同步方法中执行了线程对象的wait()方法之后,线程暂停,并释放锁

不会释放锁的操作

​ 1 线程执行同步代码块或同步方法时,程序调用Thread.sleep()方法或者Thread.yield()方法暂停当前线程

​ 2 线程执行同步代码块时,其他线程调用了该线程的suspend()方法将该线程挂起,该线程不会释放锁

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

下坠丷

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值