Java高级--多线程

1、进程

1.进程是正在运行的程序,也就是执行程序的一次执行过程,是系统进行资源分配的基本单位
2.目前操作系统都是支持多进程,可以进行执行多个进程,通过进程ID区分
3.单核cpu在同一时刻,只能有一个进程,宏观并行,微观串行。

2、线程

线程,又称轻量级进程,进程中的一条执行路径,也是cpu的基本调度单位。一个进程由一个或多个线程组成,彼此间完成不同的工作,同时执行,称为多线程。
例如:迅雷是一个进程,当中的多个下载任务即为线程。
Java虚拟机是一个继承,当中默认包含主线程(main),可通过代码创建多个独立线程,与main并发执行。

3、进程与线程的区别

1.进程是操作系统资源分配的基本单位,而线程是cpu的基本调度单位。
2.一个程序运行后至少有一个进程。
3.一个进程可以包含多个线程,但是至少需要有一个线程,否则这个进程是没有意义。
4.进程间不能共享数据段地址,但是同进程的线程之间可以。

4、创建线程的三种方法

4.1 继承Thread类 重写run方法

例子:使用线程Thread类实现4个窗口各卖100张票

//继承线程
public class Test01 extends Thread{
    int ticket = 20;
    //重写run方法
    @Override
    public void run() {
        while (true){
            if(ticket > 0){
                System.out.println(Thread.currentThread().getName()+"正在卖第"+ticket+"张票");
                ticket--;
            }
        }
    }
}
public class Test02 {
    public static void main(String[] args) {
        //创建线程
        Test01 test01 = new Test01();
        Test01 test02 = new Test01();
        Test01 test03 = new Test01();
        Test01 test04 = new Test01();
        test01.setName("窗口1:");
        test02.setName("窗口2:");
        test03.setName("窗口3:");
        test04.setName("窗口4:");
        //启动线程
        test01.start();
        test02.start();
        test03.start();
        test04.start();
    }
}

4.2 实现Runnable接口

 例子:四个窗口共卖100张票

class Test01{
    public static void main(String[] args) {
        RunnableTicket r1 = new RunnableTicket();
        Thread t1 = new Thread(r1,"窗口A:");
        Thread t2 = new Thread(r1,"窗口B:");
        Thread t3 = new Thread(r1,"窗口C:");
        Thread t4 = new Thread(r1,"窗口D:");
        t1.start();
        t2.start();
        t3.start();
        t4.start();
    }
}

public class RunnableTicket implements Runnable{
    private int ticket = 100;
    @Override
    public void run() {
        while (true) {
            synchronized (this) {
                if (ticket > 0) {
                    ticket--;
                    System.out.println(Thread.currentThread().getName() + "卖了一张,剩余:" + ticket + "张");
                    /*ticket = ticket - 1;*/
                } else {
                    return;
                }
            }
        }
    }
}

4.3 实现Callable接口

例子:求和

public class Test03 {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        My task = new My();
        My2 task2 = new My2();
        //自建创建线程对象并提交Callable类型的任务是比较麻烦的,需要封装到一个FutureTask类种, 建议使用线程池来提交任务
        /*FutureTask futureTask = new FutureTask(task);
        Thread t1 = new Thread(futureTask);
        t1.start();
        System.out.println(futureTask.get());*/

        ExecutorService executorService = Executors.newFixedThreadPool(5);
        Future<Integer> submit = executorService.submit(task);
        Future<Integer> submit2 = executorService.submit(task2);
        Integer sum = submit.get();
        Integer sum2 = submit2.get();
        System.out.println(sum+sum2);


    }
}
class My implements Callable<Integer>{

    @Override
    public Integer call() throws Exception {
        int sum = 0;
        for (int i = 0; i <= 50; i++) {
            sum+=i;
        }
        return sum;
    }
}

class My2 implements Callable<Integer>{

    @Override
    public Integer call() throws Exception {
        int sum = 0;
        for (int i = 51; i <= 100; i++) {
            sum+=i;
        }
        return sum;
    }
}

5、线程常用的方法

5.1 休眠sleep

public static void sleep(long millis) 当前线程主动休眠millis毫秒。

/*线程休眠*/
public class TestSleep {
    public static void main(String[] args) {
        T t = new T();
        t.start();

        for (int i = 0; i < 20; i++) {
            System.out.println("main:================"+i);
        }
    }
}
class T extends Thread{
    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            try {
                /*线程休眠*/
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("*************"+i);
        }
    }
}

5.2 yield

 public static void yield()
 当前线程主动放弃时间片,回到就绪状态,竞争下一次时间片    
 yield()只让有相同执行权的线程获得cup时间片,
 但是yield()不能控制cup交出的时间,
 yeild()只是让线程恢复到就绪状态,
 那么可能在执行yeild()后进入就绪状态,然后马上又进入运行状态。

public class TestYield {
    public static void main(String[] args) {
        T2 t1 = new T2();
        T2 t2 = new T2();
        t1.start();
        t2.start();
    }
}
class T2 extends Thread{
    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            /*两个线程交替频率提高*/
            Thread.yield();
            System.out.println(Thread.currentThread().getName()+"********"+i);
        }
    }
}

 

5.3 加入join

public final void join() 允许其他线程加入到当前线程中 在main函数线程中调用线程tj.join()方法,此时main函数线程就进入阻塞状态, 直到线程tj完全执行完以后,线程main才结束阻塞状态。

public class TestJoin {
    public static void main(String[] args) throws InterruptedException {
        T3 t1 = new T3();
        t1.start();

        t1.join();//把t1线程加入到当前mian线程中,只有t1线程结束后,main线程才会执行
        /*主线程*/
        for (int i = 0; i < 20; i++) {
            System.out.println(Thread.currentThread().getName()+"主线程:************"+i);
        }
    }
}
class T3 extends Thread{
    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            System.out.println(Thread.currentThread().getName()+"------------"+i);
        }
    }
}

 

5.4 守护线程Daemon

线程对象.setDaemon(true);设置为守护线程。 线程有两类:用户线程(前台线程)和守护线程(后台线程) 如果程序中所有前台线程都执行完毕了,后台线程也会自动结束。 垃圾回收线程属于守护线程。

public class TestDaemon {
    public static void main(String[] args) {
        T4 t1 = new T4();
        t1.setDaemon(true);//设置t1线程为守护线程,当前台线程(main线程)结束时,守护线程也会自动结束
        t1.start();


        /*主线程*/
        for (int i = 0; i <20; i++) {
            System.out.println(Thread.currentThread().getName()+"main:***********"+i);
        }
    }
}
class T4 extends Thread{
    @Override
    public void run() {
        for (int i = 0; i < 30; i++) {
            System.out.println(Thread.currentThread().getName()+"--------"+i);
        }
    }
}

6、线程的安全问题

 A线程要把“Hello”存入数组中;B线程要把“Word”存入数组中。

 

/*线程安全问题*/
public class Test01 {
    private static String[] arr = new String[5];
    private static int index = 0;
    public static void main(String[] args) throws InterruptedException {
        //匿名对象
        Runnable hello = new Runnable() {
            @Override
            public void run() {
                synchronized (arr) {
                    if (arr[index] == null) {
                        arr[index] = "hello";
                        index++;
                    }
                }
            }
        };
        //匿名对象
        Runnable word = new Runnable() {
            @Override
            public void run() {
                synchronized (arr) {
                    if (arr[index] == null) {
                        arr[index] = "word";
                        index++;
                    }
                }
            }
        };

        Thread t1 = new Thread(hello);
        Thread t2 = new Thread(word);
        t1.start();
        t2.start();

        t1.join();
        t2.join();
        System.out.println(Arrays.asList(arr));

    }
}

7、线程死锁

当A线程拥有锁资源a时,这时A线程需要锁资源b, 而B线程拥有锁资源b,这时B线程需要锁资源a, 这样会导致A等待B线程释放资源b, B线程等待A线程释放锁资源a。 从而二个处于永久等待。从而操作死锁。

例子:两个人打游戏,小明有键盘,小刚有鼠标 。要打游戏,小明要等小刚的鼠标,而小刚要等小明的键盘,这俩个人都在循环等待,就会陷入死锁状态。

public class LockObject {
    public static Object a = new Object();
    public static Object b = new Object();

}
public class BoyOne extends Thread{
    @Override
    public void run() {
        synchronized (LockObject.a){
            System.out.println(Thread.currentThread().getName()+"获取键盘");
            synchronized (LockObject.b){
                System.out.println(Thread.currentThread().getName()+"获取鼠标");
                System.out.println("可以打游戏了");
            }
        }
    }
}
public class BoyTwo extends Thread{
    @Override
    public void run() {
        /*try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }*/
        synchronized (LockObject.b){
            System.out.println(Thread.currentThread().getName()+"获取鼠标 ");
            synchronized (LockObject.a){
                System.out.println(Thread.currentThread().getName()+"获取键盘");
                System.out.println("可以打游戏了");
            }
        }
    }
}
//测试
public class DeadLock {
    public static void main(String[] args) {
        BoyOne b1 = new BoyOne();
        BoyTwo b2 = new BoyTwo();

        b1.setName("小明");
        b2.setName("小刚");

        b1.start();
        b2.start();
    }
}

8、什么是线程池

线程池,--其实就是一个 容纳多个线程的容器 ,其中的线程可以反复使用,省去了频繁创建线程对象的操作 ,--无需反复创建线程而消耗过多资源。

使用线程池的好处:

第一:降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。
第二:提高响应速度。当任务到达时,任务可以不需要等到线程创建就能立即执行
第三:提高线程的可管理性。线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一 的分配,调优和监控。

9、创建线程池的方式

所有的线程池---封装了一个父接口---java.util.concurrent.Executor.

​ 它的实现接口: ExecutorService.

有一个工具类。Executors可以帮你创建相应的线程池。

        //创建单一线程池
        ExecutorService executorService = Executors.newSingleThreadExecutor();

        //创建定长线程池
        ExecutorService executorService = Executors.newFixedThreadPool(3);

        //创建可变线程池
        ExecutorService executorService = Executors.newCachedThreadPool();

        //创建延迟线程池
        ScheduledExecutorService executorService = Executors.newScheduledThreadPool(3);

线程池不允许使用 Executors 去创建,而是通过 ThreadPoolExecutor 的方式,这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。  

public static void main(String[] args) {
        /**
         *int corePoolSize:核心线程数
         *int maximumPoolSize:最大线程数
         *long keepAliveTime:空闲时间
         *TimeUnit unit:时间单位
         *BlockingQueue<Runnable> workQueue:堵塞队列
         */
        //LinkedBlockingDeque:可以设置堵塞等待的个数,如果不设置个数,则默认值为Integer的最大值
        BlockingDeque blockingDeque =new LinkedBlockingDeque(3);
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(1,5,5, TimeUnit.SECONDS,blockingDeque);

        for (int i = 0; i < 5; i++) {
            threadPoolExecutor.submit(new Runnable() {
                @Override
                public void run() {
                    System.out.println(Thread.currentThread().getName()+":---------");
                }
            });
        }
        //等待线程执行结束时,关闭线程
        threadPoolExecutor.shutdown();
    }

使用Callable接口,创建线程

package demo06;

import java.util.concurrent.*;

public class Test03 {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        My task = new My();
        My2 task2 = new My2();
        //自建创建线程对象并提交Callable类型的任务是比较麻烦的,需要封装到一个FutureTask类种, 建议使用线程池来提交任务
        /*FutureTask futureTask = new FutureTask(task);
        Thread t1 = new Thread(futureTask);
        t1.start();
        System.out.println(futureTask.get());*/

        ExecutorService executorService = Executors.newFixedThreadPool(5);
        Future<Integer> submit = executorService.submit(task);
        Future<Integer> submit2 = executorService.submit(task2);
        Integer sum = submit.get();
        Integer sum2 = submit2.get();
        System.out.println(sum+sum2);


    }
}
class My implements Callable<Integer>{

    @Override
    public Integer call() throws Exception {
        int sum = 0;
        for (int i = 0; i <= 50; i++) {
            sum+=i;
        }
        return sum;
    }
}

class My2 implements Callable<Integer>{

    @Override
    public Integer call() throws Exception {
        int sum = 0;
        for (int i = 51; i <= 100; i++) {
            sum+=i;
        }
        return sum;
    }
}

10、手动锁-Lock

Lock它是手动锁的父接口,它下面有很多实现类。

lock()方法。

unlock()释放锁资源,放在finally

package demo07;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class Test01 {
    public static void main(String[] args) {
        Ticket task = new Ticket();

        Thread t1 = new Thread(task,"窗口A");
        Thread t2 = new Thread(task,"窗口B");
        Thread t3 = new Thread(task,"窗口C");
        Thread t4 = new Thread(task,"窗口D");

        t1.start();
        t2.start();
        t3.start();
        t4.start();
    }
}
class Ticket implements Runnable{
    private int ticket = 100;
    Lock lock = new ReentrantLock();
    @Override
    public void run() {
        while (true) {
            try {
                lock.lock();//加锁
                if (ticket > 0) {
                    --ticket;
                    System.out.println(Thread.currentThread().getName() + "卖了一张,剩余:" + ticket + "张");
                } else {
                    break;
                }
            }finally {
                lock.unlock();//释放锁
            }
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值