Java多线程学习记录

多线程

很多内容都在代码的注释中,要注意看代码的注释

优点

​ 对单核CPU来说,多线程比单线程完成相同的任务所需时间要多(因为要进行cpu的切换)

​ 提高程序的响应,使图形化界面更有意义(因为图形化界面要求同时完成多个任务),

​ 提高CPU利用率

​ 改善程序结构。将复杂的程序分成多个线程独立运行

用途

​ 程序需要同时执行多个任务

​ 程序需要实现一些需要等待的任务,如用户输入、文件的读写、网络操作等

​ 需要运行后台程序

创建方式

方式一、继承Thread类

package javaSE.Threads;

/**
 * 多线程的创建  方式一:继承Thread类
 * 1。 创建一个继承于Thread类的子类
 * 2。 重写thread类的run() --> 将此线程要做的操作写在run()中
 * 3。 创建Thread类的子类对象
 * 4. 通过此队形调用start()
 *
 * 例子:遍历100以内的偶数
 */

//创建一个Thread的子类
class MyThread extends Thread{
    //重写run()

    @Override
    public void run() {
        for (int i = 0; i <100 ; i++) {
            if (i % 2 == 0){
                System.out.println(i);
            }
        }

    }
}
public class ThreadsTest {
    public static void main(String[] args) {
        //新建Thread子类对象
        MyThread t1 = new MyThread();

        //通过此对象调用start() *启动当前线程  *调用当前线程的run()
        t1.start();
        //t1.run();问题一 :如果直接调用run(),那么线程就根本没有被启用,run仍在主线程中执行
        //t1.start();问题二:不能启动两次线程,会报IllegalThreadException  解决办法新建一个对象去start
        //如下操作仍然在主线程汇总执行
        for (int i = 0; i < 100; i++) {
            System.out.println(i + "***********main********");
        }
    }
}

方法二、创建一个实现Runnable接口的类

package javaSE.Threads;

/**
 * 创建线程的方法二
 * 1,创建一个实现Runnable接口的类
 * 2,实现接口中的run方法
 * 3,创建一个实现类的对象
 * 4,将此对象作为参数传递到thread类的对象,创建thread对象
 * 5,通过thread类放入对象调用start方法
 *
 */

class Mythread implements Runnable{
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            if (i % 2 ==0){
                System.out.println(i);
            }
        }
    }
}

public class MyThread2 {
    public static void main(String[] args) {
        Mythread mythread = new Mythread();
        Thread thread = new Thread(mythread);
        thread.start();
        //此处start启用的run是是当前线程的run方法(即thread类中的run)-->
        //thread类中的run方法
        /* What will be run. */
//        private Runnable target;
//
//        public void run() {
//            if (target != null) {
//                target.run();
//            }
//        }
    }
}



jdk5.0新增线程的创建方式

方式三 实现Callable接口

​ 实现Callable接口创建多线程比实现runable接口实现多线程方式强

​ 1.可以有返回值

​ 2.方法可以抛出异常

​ 3.支持泛型的返回值

​ 4.需借助FutureTask类,比如获取返回结果但需要Future接口

​ Future接口

​ 可以对具体的Runnable、callable任务的执行结果进行取消、查询是否完成、获取结果等

​ FutureTask是Future接口的唯一实现类

​ FutureTask同时实现了Runnable、Future接口,它既可以作为Runnable被线程执行,又可以作为Future得到Callable的返回值

package javaSE.Threads;

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

/**
 * 创建线程的方式三 实现Callable接口 ---- JDK 5.0新增
 * 步骤
 * 1, 创建一个callable实现类
 * 2, 实现call()方法,将此线程需执行的操作声明在call()方法中
 * 3, 创建Callable接口实现类的对象
 * 4, 将Callable接口实现类的对象作为参数传递到FutureTask构造器中创建FutureTask类的对象
 * 5, 将FutureTask对象作为参数传递到thread类的构造器中,创建Thread类的对象
 * 6, 获取call方法的返回值
 *
 *
 */
class NumThread implements Callable{

    @Override
    public Object call() throws Exception {

        int sum = 0;
        for (int i = 1; i < 100; i++) {
            if (i % 2 == 0){
                System.out.println(i);
                sum += i;
            }
        }
        return sum;
    }
}

public class ThreadNew {
    public static void main(String[] args) {
        NumThread numThread = new NumThread();
        //创建一个FutureT类的对象
        FutureTask futureTask = new FutureTask(numThread);

        new Thread(futureTask).start();//如果没有这一步就不能调用重写的call方法

        try {
            //get()方法的返回值为FutureTask构造器参数Callable实现类重写的call()的返回值
            Object sum = futureTask.get();
            System.out.println(sum);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }
}

方式四 线程池

[在下面]

比较

实现runnable接口比较常用,因为第一种方法采用继承类,有类的单继承局限性,而且更适合实现多线程共享数据的形式

相同点,都需要实现run方法

常用方法

package javaSE.Threads;

/**
 * 测试Thread类的常用方法
 * 1. start();启动当前线程,调用当前线程的run()
 * 2. rnu();通常需要重写Thread类中的run()方法,将要执行的操作声明在此方法中
 * 3. currentThread();静态方法,返回执行当前代码的线程
 * 4. getname();获取当前线程名字
 * 5. setname();设置当前线程的名字
 * 6. yield();释放当前CPU的执行权
 * 7. join();在线程A中调用B的join方法,此时A进入阻塞状态,直到join来的子线程执行结束后在执行A原线程
 * 8. sleep(long millitime);阻塞当前线程指定时间(单位毫秒)
 * 9.isAlive();判断当前线程是否存活
 * 10.notify();唤醒有优先级最高的进程;notifyall();唤醒所有进程
 */ 
publc class ThrradsMethod {
    public static void main(String[] args) {
        MyThread1 mt = new MyThread1();
        mt.setName("子线程");
        mt.start();
        Thread.currentThread().setName("主线程");
        for (int i = 0; i < 100; i++) {
            if(i % 2 != 0){
                System.out.println(Thread.currentThread().getName() + i);
            }
        }


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

设置线程的优先级

同优先级采用先来先服务,不同优先级采用抢占式优先级

//线程的优先级等级
MAX_PRIORITY:10
MIN_PRIORITY:1
NORM_PRIORITY:5//默认

//涉及的方法
    
    getPriority();返回线程优先级
    setPriority(int newPriority);改变线程优先级

线程的创建时继承父线程的优先级

低优先级只是获得较低的调度概率,并不一定在搞高优先级线程之后被调用

线程的同步

package javaSE.Threads;

/**
 * 线程安全:例如下面,卖票系统出现错票重票问题
 * 解决办法,给线程加锁实现线程的同步与互斥
 *方法一: 同步代码块
 * synchronized(同步监视器){
 *     //需要被同步的代码,即操作共享数据的代码
 *
 * }
 *说明1.同步监视器;锁,任何一个类的对象都可以充当锁
 *      要求:多个线程公用一把锁
 *
 *同步的好处,解决了线程的安全问题
 * 局限性:操作同步代码的时候,只能有一个线程参与,效率较低
 *
 * 方法二;同步方法
 *   如果对共享代码的操作在同一个方法中,可将该方法同步
 *   同步监视器为this
 */

class Mythread implements Runnable {
    private int tickets = 100;
    Object object = new Object();

    @Override
    //public synchronized void run() {//该方式即为同步方法
    public void run() {
        while (true) {
            synchronized (object) {//objest同步监视器,所有线程公用此监视器
                if (tickets > 0) {
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName() + ":卖票,票号是" + tickets);
                    tickets--;
                } else {
                    break;
                }
            }
        }
    }
}

public class MyThread2 {
    public static void main(String[] args) {
        Mythread mythread = new Mythread();
        Thread thread1 = new Thread(mythread);
        Thread thread2 = new Thread(mythread);
        Thread thread3 = new Thread(mythread);

        thread1.setName("窗口1");
        thread2.setName("窗口2");
        thread3.setName("窗口3");

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

    }
}


//*******************************************8

package javaSE.Threads;

/**
 * 同步方法实现继承类的同步
 */
class Mythread3 implements Runnable {
    private static int tickets = 100;
    Object object = new Object();

    @Override
    //public synchronized void run() {//该方式即为同步方法
    public void run() {
        while (true) {
            show();
        }
    }
    private static synchronized void show (){//同步监视器是当前的类
    //private synchronized void show(){//t1,t2,t3的同步监视器是不同的,故此种方法是错误的
        if (tickets > 0) {
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + ":卖票,票号是" + tickets);
            tickets--;
        }
    }
}


public class WindowSTest1 {
    public static void main(String[] args) {
        Mythread3 mythread = new Mythread3();
        Thread thread1 = new Thread(mythread);
        Thread thread2 = new Thread(mythread);
        Thread thread3 = new Thread(mythread);

        thread1.setName("窗口1");
        thread2.setName("窗口2");
        thread3.setName("窗口3");

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

    }
}


LOCK锁

​ 从jdk 5.0开始,java提供了更强大的线程同步机制-----通过显示定义同步锁对象来实现同步,同步锁使用lock对象充当。

​ ReentrantLock类实现了Lock,比较茬昂用的是ReentrantLock,因为他可以显示的加锁和释放锁

package javaSE.Threads;

import java.util.concurrent.locks.ReentrantLock;

/**
 * Lock锁解决线程安全问题-----jdk 5.0之后新增
 *。面试题,synchronized 和 lock的异同
 *     相同:二者都可以解决线程安全问题
 *     不同:synchronized机制在执行完相应的代码后自动释放同步监视而Lock方法需要手动
 *     启动同步(lock()方法),同时结束同步也需要手动实现(unlock()方法)
 *
 *   使用顺序
 *   lock -> 同步代码块(已经进入了方法体,分配了相应资源) ->  同步方法(在方法体之外)
 *
 *   
 *
 */
class Windowss implements Runnable{
    private static int tickets = 100;
    //实例化ReentrantLock类的实例化对象
    private ReentrantLock lock = new ReentrantLock();

    @Override
    public void run() {
       while (true){
           try {
               //调用Lock方法
               lock.lock();

               if (tickets > 0){
                   try {
                       Thread.sleep(100);
                   } catch (InterruptedException e) {
                       e.printStackTrace();
                   }

                   System.out.println(Thread.currentThread().getName() + "售票,票号是" + tickets);
                   tickets--;
               }else {
                   break;
               }
           }finally {
               //显示解锁
               lock.unlock();
           }

       }
    }
}
public class LockTest {
    public static void main(String[] args) {
        Windowss w = new Windowss();

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

        thread1.setName("窗口一");
        thread2.setName("窗口二");
        thread3.setName("窗口三");

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

}

线程通信

package javaSE.Threads;

import java.awt.print.PrinterAbortException;

/**
 * 线程通信的例子,两个线程交替打印 1 - 100
 * 所需方法
 * wait();阻塞当前线程
 * notify();唤醒一个线程,如果是多个线程就唤醒优先级锡膏的
 * notifyall();唤醒全部进程
 *
 * 说明:
 * 1. wait();notify(); notifyall();三个方法必须使用在同步方法和同步代码块中
 * 2. wait();notify(); notifyall();三个方法必调用者必须是同步方法和同步代码块的
 * 监视器否则会出现IllegalMonitorStateException异常
 * 3. wait();notify(); notifyall();三个方法定义在object类中,以确保所有 的监视器
 * 都可以调用三种方法
 *
 *
 */

class Number implements Runnable{
    private int number = 1;

    @Override
    public void run() {
        while (true){

            synchronized (this){
                notify();

                if (number <= 100){
                    System.out.println(Thread.currentThread().getName() + ":" + number);
                    number++;
                }else {
                    break;
                }

                try {
                    wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}


public class CommunicationTest {
    public static void main(String[] args) {
        Number num = new Number();

        Thread t1 = new Thread(num);
        Thread t2 = new Thread(num);

        t1.setName("甲");
        t2.setName("乙");

        t1.start();
        t2.start();

    }
}

线程池

​ 背景:为了避免频繁的创建、销毁、使用量巨大的资源对性能的影响,提出线程池的概念

​ 思路:提前创建好多个线程,使用时直接获取,使用完后放回池中,这样可以频繁的创建销毁,以实现重复利用

​ 好处:

​ 1. 提高了创建速度(减少了创建线程的时间)

  1. 降低了资源消耗(重复利用线程池中的线程,不需要每次都创建)

  2. 便于线程管理

    ​ corePoolSize 核心池的大小

    ​ maximumPoolSize: 最大线程数

    ​ keepAliveTime: 线程没有任务时最多保持多少时间终止
    在这里插入图片描述

package javaSE.Threads;
/**
 * 实现多线程的方法四 --- 线程池
 * 方法
 *  1.创建一个线程数为10的线程池
 *  2.调用一个线程,需要提供一个实现Runnable接口或Callable接口实现类的对象
 *  3.调用另一个线程
 *  
 */

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

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 != 0){
                System.out.println(Thread.currentThread().getName() + ":" + i);
            }
        }
    }
}


public class ThreadPool {
    public static void main(String[] args) {


        //1.创建一个线程数为10的线程池
        ExecutorService service = Executors.newFixedThreadPool(10);

        //设置线程池的属性
        //      corePoolSize   核心池的大小
        //​		maximumPoolSize: 最大线程数
        //​		keepAliveTime: 线程没有任务时最多保持多少时间终止
        
       // System.out.println(service.getClass());//getclass():获取创造该对象的类型

        //2.调用一个线程,需要提供一个实现Runnable接口或Callable接口实现类的对象
        service.execute(new NumberThread());//适用于Runnable
//        service.submit();//适用于Callable
        //3.调用另一个线程
        service.execute(new NumberThread1());
        //每一个线程都需要一个实现Runnable接口的实现类 列如 NumberThread 和  NumberThread1 
        
        
        service.shutdown();//关线程池
    }
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值