首页 > 编程语言 > 详细

04多线程

时间:2020-10-14 22:48:13      阅读:32      评论:0      收藏:0      [点我收藏+]

toc

一、基础入门

1.1 线程、程序、进程

程序:针对特定功能的代码集合。是死的

进程:正在执行的程序,是活的,是资源分配的最小单位

线程:一个进程可以有多个线程,是CPU调度的最小单位,线程+ 资源=进程

1.2 并行和并发

并行:多个CPU同时处理多件事情,强调的是同一时刻

并发:一个CPU同时处理多件事情,强调的是一个时间段

二、如何创建多线程

2.1 继承Thread类

public class ThreadTest {
    static class MyThread extends Thread{
        @Override
        public void run() {
            for (int i = 0; i < 10; i++) {
                System.out.println(Thread.currentThread().getName()+":"+i);
            }
        }
    }

    public static void main(String[] args) {
        MyThread myThread = new MyThread();
        myThread.start();
        for (int i = 0; i < 10; i++) {
            System.out.println(Thread.currentThread().getName()+":"+i);
        }
    }
}
/*
main:0
Thread-0:0
main:1
Thread-0:1
main:2
Thread-0:2
main:3
*/

总结:可以看出两个线程是同时执行的,(Thread.currentThread().getName()用来获取线程名,获取的是不同的线程

2.2 实现Runable接口

package com.lsm;
class MyThread4 implements Runnable{

    @Override
    public void run() {
        for (int i = 0; i < 5; i++) {
            System.out.println(Thread.currentThread().getName()+":"+i);
        }
    }
}

public class ThreadMethod3 {
    public static void main(String[] args) {
        Thread thread = new Thread(new MyThread4());
        thread.start();

        for (int i = 0; i < 5; i++) {
            System.out.println(Thread.currentThread().getName()+":"+i);
        }
    }
}
/*
main:0
Thread-0:0
main:1
main:2
main:3
main:4
Thread-0:1
Thread-0:2
Thread-0:3
Thread-0:4
*/

总结:创建一个类实现Runable接口,把这个类的实例传递给Thread构造方法的位置

2.3 实现Runable接口

public class MyCallable implements Callable<Integer> {
    @Override
    public Integer call() throws Exception {
        Integer sum = 0;
        for (int i = 1; i <= 100; i++) {
            if (i % 2 == 0) {
                System.out.println(i);
                sum += i;
            }
        }
        return sum;
    }

    public static void main(String[] args) {
        MyCallable myCallable = new MyCallable();
        FutureTask<Integer> task = new FutureTask<Integer>(myCallable);
        new Thread(task).start();
        try {
            Integer result = task.get();
            System.out.println("result="+result);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }
}

总结:Callable可以有返回值,Callable支持泛型传递返回值,可以抛出异常,使用的时候需要借助FutureTask

2.4 线程池方式

package com.lsm.java1.MyJava;

import java.util.concurrent.*;

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);
            }
        }
    }
}

class MyCallable1 implements Callable<Integer>{
    @Override
    public Integer call() throws Exception {
        int sum = 0;
        for (int i = 0; i < 10; i++) {
            System.out.println(Thread.currentThread().getName()+":"+i);
            sum+=i;
        }
        return sum;
    }
}

public class ThreadPool {
    public static void main(String[] args) {
        ExecutorService service = Executors.newFixedThreadPool(10);
        service.execute(new NumberThread()); //适合Runable
        service.execute(new NumberThread1());
        FutureTask<Integer> task = new FutureTask<>(new MyCallable1());
        Thread thread = new Thread(task);
        service.submit(thread);
        Integer result = null;
        try {
            result = task.get();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
        System.out.println("=============="+result);
        service.shutdown();
    }
}

三、线程常用的方法

3.1 run方法和strat方法的区别

package com.lsm;

public class ThreadTest {
    static class MyThread extends Thread{
        @Override
        public void run() {
            for (int i = 0; i < 2; i++) {
                System.out.println(Thread.currentThread().getName()+":"+i);
            }
        }
    }

    public static void main(String[] args) {
        MyThread myThread = new MyThread();
//        myThread.start();
        myThread.run();
        for (int i = 0; i < 2; i++) {
            System.out.println(Thread.currentThread().getName()+":"+i);
        }
    }
}
/*
main:0
main:1
main:0
main:1
*/

总结:可以看出都是main线程在执行,run方法在API中是这样介绍的:

  • start() 导致此线程开始执行; Java虚拟机调用此线程的run方法。

3.2 jion方法()

class MyThread extends Thread{
    @Override
    public void run() {
        for (int j = 0; j < 5; j++) {
            System.out.println(Thread.currentThread().getName()+":"+j);
        }

    }
}
public class ThreadMethod {

    public static void main(String[] args) {
       Thread t = new MyThread();
       t.start();
        for (int i = 0; i < 5; i++) {
            System.out.println(Thread.currentThread().getName()+":"+i);
            if (i==2){
                try {
                    t.join();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}
/*
main:0
Thread-0:0
main:1
main:2
Thread-0:1
Thread-0:2
Thread-0:3
Thread-0:4
main:3
main:4
*/

3.3 yield方法

class MyThread1 extends Thread{
    @Override
    public void run() {
        for (int j = 0; j < 50; j++) {

            System.out.println(Thread.currentThread().getName()+":"+j);
        }

    }
}

public class ThreadMethod1 {

    public static void main(String[] args) {
       Thread t = new MyThread1();
       t.start();
        for (int i = 0; i < 50; i++) {
            System.out.println(Thread.currentThread().getName()+":"+i);
            if (i%2 ==0){
              Thread.currentThread().yield();
            }
        }
    }
}
/*
Thread-0:9
main:0
Thread-0:10
Thread-0:11
Thread-0:12
Thread-0:13
main:1
main:2
Thread-0:14
main:3
main:4
Thread-0:15
main:5
Thread-0:16
main:6
Thread-0:17
main:7
Thread-0:18
main:8
Thread-0:19
main:9
Thread-0:20
Thread-0:21
Thread-0:22
main:10
Thread-0:23
Thread-0:24
Thread-0:25
Thread-0:26
main:11
main:12
main:13
*/

总结:可以看到main线程调用yield方法后会让出cpu的使用权,让出来自己还会抢占。最后一行main线程执行到12后如何让出条件,让出cpu后main线程再次抢占执行到13

3.4 sleep方法

package com.lsm;

class MyThread2 extends Thread{
    @Override
    public void run() {
        for (int j = 0; j < 5; j++) {

            System.out.println(Thread.currentThread().getName()+":"+j);
        }

    }
}

public class ThreadMethod2 {

    public static void main(String[] args) {
       Thread t = new MyThread2();
       t.start();
        for (int i = 0; i < 5; i++) {
            System.out.println(Thread.currentThread().getName()+":"+i);
            if (i%2 ==0){
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}
/*
main:0
Thread-0:0
Thread-0:1
Thread-0:2
Thread-0:3
Thread-0:4
main:1
main:2
main:3
main:4
*/

3.5 常用方法总结

方法注释作用
public static native void yield(); A hint to the scheduler that the current thread is willing to yield its current use of a processor. The scheduler is free to ignore this hint. 当前线程让出CPU,有可能下次还会碰到此线程
public final synchronized void setName(String name) Changes the name of this thread to be equal to the argument 给线程设置名字
public final void join() Waits for this thread to die. 插入一个线程,直到插入的线程执行完本线程才会执行
public static native void sleep(long millis) Causes the currently executing thread to sleep (temporarily cease * execution) for the specified number of milliseconds, subject to * the precision and accuracy of system timers and schedulers. The thread * does not lose ownership of any monitors 让当前线程睡眠指定的毫秒,睡眠期间会放弃cpu,睡醒后等待cpu分配时间片
public final void setPriority(int newPriority) Changes the priority of this thread. 设置当前线程的优先级,并不完全可靠。优先级1-10默认5

一个类可以调用多次start方法吗

Thread类中

public synchronized void start() {
    if (threadStatus != 0)
        throw new IllegalThreadStateException();
              group.add(this);

        boolean started = false;
        try {
            start0();
            started = true;

可以看出一个线程多次调用start方法会抛出IllegalThreadStateException

四、线程的生命周期

技术分享图片

五、线程安全问题

什么时候会发生线程安全问题?

多个线程中有有共享的变量

模拟三个窗口卖票

5.1 同步代码块方式

package com.lsm;
public class Ticket {
    public static void main(String[] args) {
        Thread t1 = new Thread(new MyWindow(), "1窗口");
        Thread t2 = new Thread(new MyWindow(), "2窗口");
        Thread t3 = new Thread(new MyWindow(), "3窗口");
        t1.start();
        t2.start();
        t3.start();
    }
}
class MyWindow implements Runnable {
    private static int ticket = 100;  //这里的static代表该类共享变量
    private Object obj = new Object(); 
    @Override
    public void run() {
        synchronized (obj) {
            while (true) {
                if (ticket > 0) {
                    System.out.println(Thread.currentThread().getName() + "卖出了" + ticket);
                    ticket--;
                } else {
                    break;
                }
            }
        }
    }
}
synchronized (锁对象) {
         对共享变量的操作
}

5.2 同步方法方式

public class Ticket2 {
    public static void main(String[] args) {
        MyWindow2 windows2 = new MyWindow2();
        Thread t1 = new Thread(windows2, "1窗口");
        Thread t2 = new Thread(windows2, "2窗口");
        Thread t3 = new Thread(windows2, "3窗口");
        t1.start();
        t2.start();
        t3.start();
    }
}

class MyWindow2 implements Runnable {
    static int ticket = 100;

    @Override
    public void run() {
        show();
    }

    private synchronized void show() {
        while (true) {
            if (ticket > 0) {
                System.out.println(Thread.currentThread().getName() + "卖出了" + ticket);
                ticket--;
            } else {
                break;
            }
        }

    }
}

总结:同步方法private synchronized void show()中的锁对象是this也就是MyWindow2的实例,如果是public static synchronized void show()锁的是当前类的.class

5.3 lock方式

public class Ticket3 {
    public static void main(String[] args) {
        MyWindow3 windows3 = new MyWindow3();
        Thread t1 = new Thread(windows3, "1窗口");
        Thread t2 = new Thread(windows3, "2窗口");
        Thread t3 = new Thread(windows3, "3窗口");
        t1.start();
        t2.start();
        t3.start();
    }
}

class MyWindow3 implements Runnable {
    static int ticket = 100;
    private ReentrantLock lock = new ReentrantLock();

    @Override
    public void run() {
        while (true) {
            try {
                lock.lock();
                if (ticket > 0) {
                    System.out.println(Thread.currentThread().getName() + "卖出了" + ticket);
                    Thread.sleep(10);
                    ticket--;
                } else {
                    break;
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }
    }
}

5.4 线程安全的懒汉式

public class Bank {
    private Bank(){};  //私有构造方法,不能再外部创建
    private static Bank instance = null;
    public static Bank getInstance(){
        if (instance == null){   //外层判断提高Synchronize的效率
            synchronized (Bank.class){
                if (instance == null){   //内层判断用于创建对象
                    instance = new Bank();
                }
            }
        }
        return instance;
    }
}

5.5 死锁

public class DeadLock {
    public static void main(String[] args) {
        Object o1 = new Object();
        Object o2 = new Object();
        new Thread(()->{
            synchronized (o1){
                System.out.println("111a");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (o2){
                    System.out.println("111b");
                }
            }
        }).start();
        new Thread(()->{
            synchronized (o2){
                System.out.println("222a");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (o1){
                    System.out.println("222b");
                }
            }
        }).start();
    }
}

运行结果

技术分享图片

发现程序停不了了。

死锁产生的原因:

不同的线程占用了对方需要的锁不放弃,在等待对方释放自己需要的锁。

解决方法:

避免锁嵌套

六、线程通信

6.1 三个线程交替打印1-100

class MyThread extends Thread {
    private static int num = 1;
    @Override
    public void run() {
        while (true) {
            synchronized (this) {
                 notify();
                if (num <= 100) {
                    int i = Integer.parseInt(Thread.currentThread().getName()); //获取线程名
                    if (num % 3 == i) {
                        System.out.println(Thread.currentThread().getName() + ":" + num);
                        num++;
                    }else {
                        try {
                            wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }else {
                    break;
                }
            }
        }
    }
}

public class ThreeLock {
    public static void main(String[] args) {
        MyThread myThread = new MyThread();
        Thread thread = new Thread(myThread, "0");
        Thread thread1 = new Thread(myThread, "1");
        Thread thread2 = new Thread(myThread, "2");
        thread.start();
        thread1.start();
        thread2.start();
    }
}

总结:进入run方法就随机唤醒一个线程。通过让1-100和3取模,模到哪个就用哪个线程进行直接运行,否则就让当前线程wait

6.2 sleep方法和wait方法异同

sleppwait
相同点 让线程阻塞
声明位置 Thread类
调用位置 任何场景
不会释放锁

6.3 生产者消费者问题

package com.lsm.java1.MyJava;

public class BoxTest {
    public static void main(String[] args) {
        Box box = new Box();
        Producer1 producer = new Producer1(box);
        Consumer1 consumer = new Consumer1(box);
        producer.setName("生产者");
        consumer.setName("消费者");
        producer.start();
        consumer.start();
    }

}

class Box {
    private int productCount = 0;
    //生产
    public synchronized void produceProduct() {
        if (productCount < 20) {
            productCount++;

            System.out.println(Thread.currentThread().getName() + "生产了,盒子里有" + productCount + "个");
            notify();
        } else {
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    //消费
    public synchronized void consumerProduct() {
        if (productCount > 0) {

            System.out.println(Thread.currentThread().getName() + "消费了,盒子里有" + (productCount - 1) + "个");
            productCount--;
            notify();
        } else {
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

class Producer1 extends Thread {
    private Box box;

    public Producer1(Box box) {
        this.box = box;
    }

    @Override
    public void run() {

        while (true) {
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            box.produceProduct();
        }

    }
}

class Consumer1 extends Thread {
    private Box box;

    public Consumer1(Box box) {
        this.box = box;
    }

    @Override
    public void run() {
        while (true) {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            box.consumerProduct();
        }
    }
}

 





04多线程

原文:https://www.cnblogs.com/adaobl/p/13817414.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!