多线程相关

多线程

一、进程与线程

  • 进程:执行程序的一次执行过程,是系统资源分配的单位。

  • 线程:一个进程可以包含若干个线程,线程是CPU调度和执行的单位。

注意:在一个CPU的情况下,在同一时间点,CPU只能执行一个代码,因为切换的很快,所以就有了同时执行的错觉,只有在多CPU(即多核)的前提下才能实现真正的多线程。


二、线程的创建

线程创建的三种方式

  • 继承Thread类
  • 实现Runnable接口
  • 实现Callable接口

1. 继承Thread类

package zander.example.thread;

/**
 * 通过集成继承Thread类的方式创建线程
 * 1.继承Thread类
 * 2.重写run方法
 * 3.调用start()方法开启线程
 */
public class TestThread1 extends Thread{

    @Override
    public void run() {//run方法线程体
        for(int i = 0; i < 20; i++){
            System.out.println("我的第"+(i+1)+"run");
        }
    }

    public static void main(String[] args) {//main主线程

        //创建一个线程对象
        TestThread1 testThread = new TestThread1();
        //调用start方法开启线程
        testThread.start();

        for(int i = 0 ; i <500; i++) {
            System.out.println("main"+(i+1));
        }

    }
}

package zander.example.thread;

import org.apache.commons.io.FileUtils;

import java.io.File;
import java.io.IOException;
import java.net.URL;

/**
 * 通过线程下载图片
 */
public class TestThread2 extends Thread{

    private String url ;
    private String name;

    public TestThread2(String url , String name) {
        this.url = url;
        this.name = name;
    }

    @Override
    public void run() {
        FileDownload fileDownload = new FileDownload();
        fileDownload.download(url, name);
        System.out.println(name);
    }

    public static void main(String[] args) {
        TestThread2 test1 
            = new TestThread2("https://img2.baidu.com/it/u=3599223828,1689461421&fm=26&fmt=auto&gp=0.jpg","1.jpg");
        TestThread2 test2 
            = new TestThread2("https://img2.baidu.com/it/u=3599223828,1689461421&fm=26&fmt=auto&gp=0.jpg", "2.jpg");
        TestThread2 test3 
            = new TestThread2("https://img2.baidu.com/it/u=3599223828,1689461421&fm=26&fmt=auto&gp=0.jpg", "3.jgp");

        test1.start();
        test2.start();
        test3.start();
    }

}

class FileDownload {
    public void download(String url , String name) {
        try {
            //commons-io包提供的FileUtils工具类,根据链接下载
            FileUtils.copyURLToFile(new URL(url), new File(name));
        } catch (IOException e) {
            e.printStackTrace();
            System.out.println("下载文件FileDownload类的down方法出错!");
        }
    }
}

2. 实现Runnable接口

package zander.example.runnable;

/**
 * 通过实现Runnable接口的方式创建线程
 * 1.实现Runnable接口
 * 2.重写run方法
 * 3.以参数形式丢入runnable接口的实现类,调用start方法
 */
public class TestRunnable1 implements Runnable{

    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            System.out.println("我的第"+(i+1)+"个run");
        }
    }

    public static void main(String[] args) {

        //创建Runnable接口的实现类对象
        TestRunnable1 testRunnable1 = new TestRunnable1();
        //创建线程对象,通过线程对象开启线程(代理)
        new Thread(testRunnable1).start();

        for(int i = 0 ; i <500; i++) {
            System.out.println("main"+(i+1));
        }
    }

}

package zander.example.runnable;

import org.apache.commons.io.FileUtils;
import zander.example.thread.TestThread2;

import java.io.File;
import java.io.IOException;
import java.net.URL;

/**
 * 通过线程下载图片
 */
public class TestRunnable2 implements Runnable{

    private String url;
    private String name;

    public TestRunnable2(String url, String name) {
        this.url = url;
        this.name = name;
    }

    @Override
    public void run() {
        FileDownload fileDownload = new FileDownload();
        fileDownload.download(url, name);
        System.out.println("下载了"+name+"文件");

    }

    public static void main(String[] args) {

        TestRunnable2 runnable1
                = new TestRunnable2("https://img2.baidu.com/it/u=3599223828,1689461421&fm=26&fmt=auto&gp=0.jpg", "1.jpg");
        TestRunnable2 runnable2
                = new TestRunnable2("https://img2.baidu.com/it/u=3599223828,1689461421&fm=26&fmt=auto&gp=0.jpg", "2.jpg");
        TestRunnable2 runnable3
                = new TestRunnable2("https://img2.baidu.com/it/u=3599223828,1689461421&fm=26&fmt=auto&gp=0.jpg", "3.jpg");

        new Thread(runnable1).start();
        new Thread(runnable2).start();
        new Thread(runnable3).start();

    }
}

class FileDownload {
    public void download(String url , String name) {
        try {
            //commons-io包提供的FileUtils工具类,根据链接下载
            FileUtils.copyURLToFile(new URL(url), new File(name));
        } catch (IOException e) {
            e.printStackTrace();
            System.out.println("下载文件FileDownload类的down方法出错!");
        }
    }
}

3.实现Callable接口

package zander.example.callable;

import java.util.concurrent.*;

/**
 * 通过实现Callable接口的方式创建线程
 * 1.实现Callable接口
 * 2.重写call方法,带返回值
 * 3.创建执行服务  ExecutorService executorService = Executors.newFixedThreadPool(3);
 * 4.提交执行 Future<Boolean> future = executorService.submit(testCallable1);
 * 5.获取执行结果 boolean result = future.get();
 * 6.关闭服务 executorService.shutdownNow();
 */
public class TestCallable1 implements Callable<Boolean> {
    @Override
    public Boolean call() throws Exception {
        for (int i = 0; i < 20; i++) {
            System.out.println("我的第" + (i + 1) + "个call");
        }
        return true;
    }

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        TestCallable1 testCallable1 = new TestCallable1();

        //创建执行服务
        ExecutorService executorService = Executors.newFixedThreadPool(3);

        //提交执行
        Future<Boolean> future = executorService.submit(testCallable1);

        //获取结果
        boolean result = future.get();

        //关闭服务
        executorService.shutdownNow();

    }
}

线程状态

线程状态

三、线程停止

package zander.example.runnable;

/**
 * 线程停止
 * 尽量不要使用jdk已经不建议使用的方式,如stop()、destroy()等
 * 可以使用标志位的方式,让线程自己停止
 */
public class TestStop implements Runnable {

    private static boolean flag = true;

    @Override
    public void run() {
        while (flag) {
            System.out.println("线程" + Thread.currentThread().getName() + "正在运行");
        }
    }

    //设置一个停止线程的方法,转换标志位
    public void stop() {
        this.flag = false;
    }

    public static void main(String[] args) {

        TestStop testStop = new TestStop();
        new Thread(testStop).start();

        for (int i = 0; i < 1000; i++) {
            System.out.println("main方法正在执行--" + i);
            if (i == 900) {
                testStop.stop();
                System.out.println("停止线程" + i);
            }
        }
    }

}


四、线程休眠

package zander.example.runnable;

import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * 线程休眠
 */
public class TestSleep{

    public static void main(String[] args) {
        Date start = new Date(System.currentTimeMillis()); //获取当前时间
        while (true) {
            try {
                System.out.println(new SimpleDateFormat("HH:mm:ss").format(start));
                Thread.sleep(1000);
                start = new Date(System.currentTimeMillis());
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(); 
        }
    }
}


五、线程礼让

package zander.example.runnable;

/**
 * 线程礼让
 * 线程礼让后,重新由CPU调度,所以礼让不一定成功
 */
public class TestYield {

    public static void main(String[] args) {
        Yield yield = new Yield();

        new Thread(yield, "线程1").start();
        new Thread(yield, "线程2").start();
    }

}

class Yield implements Runnable{

    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName()+"开始执行");
        Thread.yield();
        System.out.println(Thread.currentThread().getName()+"结束执行");
    }
}

六、线程强制执行

package zander.example.runnable;

import jdk.nashorn.internal.scripts.JO;

/**
 * 线程强制执行
 */
public class TestJoin {

    public static void main(String[] args) throws InterruptedException {

        Join join = new Join();
        Thread thread = new Thread(join);
        thread.start();

        for (int i = 0; i < 1000; i++) {
            if (i == 50) {
                thread.join();
            }
            System.out.println("main"+i);
        }

    }

}

class Join implements Runnable {

    @Override
    public void run() {

        for (int i = 0; i < 100; i++) {
            System.out.println("join" + i);
        }

    }
}

七、观察线程状态

package zander.example.runnable;

/**
 * 观察线程状态
 */
public class WatchThreadState {

    public static void main(String[] args) throws InterruptedException {

        Thread thread = new Thread(()->{
            try {
                Thread.sleep(1000); //休眠的时候 TIMED_WAITING状态
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("线程结束"); // 结束后TERMINATED状态
        });
        Thread.State state = thread.getState();
        System.out.println(state);  //NEW 状态

        thread.start(); //RUNNABLE 状态
        state = thread.getState();
        System.out.println(state);

        while (state != Thread.State.TERMINATED){
            Thread.sleep(100);
            state = thread.getState();
            System.out.println(state);
        }
    }

}


八、线程优先级

package zander.example.runnable;

/**
 * 设置线程优先级
 * 优先级高的只是权重高,但是不一定先执行,最终还是需要看CPU调度
 * setPriority 方法  优先级 1~10
 */
public class TestPriority {

    public static void main(String[] args) {

        Priority priority = new Priority();
        Thread thread1 = new Thread(priority, "线程1");
        Thread thread2 = new Thread(priority, "线程2");
        Thread thread3 = new Thread(priority, "线程3");
        Thread thread4 = new Thread(priority, "线程4");
        Thread thread5 = new Thread(priority, "线程5");
        Thread thread6 = new Thread(priority, "线程6");
        Thread thread7 = new Thread(priority, "线程7");
        Thread thread8 = new Thread(priority, "线程8");

        thread1.setPriority(3);
        thread1.start();

        thread2.setPriority(8);
        thread2.start();

        thread3.setPriority(2);
        thread3.start();

        thread4.setPriority(10);
        thread4.start();

        thread5.setPriority(6);
        thread5.start();

        thread6.setPriority(7);
        thread6.start();

        thread7.setPriority(1);
        thread7.start();

        thread8.setPriority(9);
        thread8.start();

    }
}

class Priority implements Runnable{

    @Override
    public void run() {

        System.out.println(Thread.currentThread().getName()+"正在执行,它的优先级是"+Thread.currentThread().getPriority());

    }

}

九、守护线程

package zander.example.runnable;

/**
 * 守护线程
 * 1.线程分为用户线程和守护线程
 * 2.虚拟机必须保证用户线程执行完毕,但不用等待守护线程执行完毕
 */
public class TestDaemon {
    public static void main(String[] args) {
        UserThread userThread = new UserThread();
        Daemon daemon = new Daemon();

        Thread uThread = new Thread(userThread);
        Thread dThread = new Thread(daemon);

        dThread.setDaemon(true); //将daemon设置为守护线程

        uThread.start();
        dThread.start();

    }

}

class UserThread implements Runnable{

    @Override
    public void run() {
        for (int i = 0; i < 365; i++) {
            System.out.println(i+1);
        }
        System.out.println("end");
    }
}

class Daemon implements Runnable{
    @Override
    public void run() {
        int i = 0;
        while (true) {
            System.out.println("daemon"+(++i));
        }

    }
}

十、线程同步

  1. 多线程操作同一个对象(并发),线程不安全
package zander.example.runnable;

/**
 * 模拟多个线程同时操作一个对象
 *
 * 注意:多个线程操作同一个资源的情况下,线程不安全
 */
public class TestRunnable3 implements Runnable {

    private int num = 10;

    @Override
    public void run() {
        while (true) {
            if (num <= 0) {
                break;
            }
            //模拟延时
            try {
                Thread.sleep(200);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + "买了第" + (num--) + "张票");
        }
    }

    public static void main(String[] args) {
        TestRunnable3 testRunnable3 = new TestRunnable3();

        new Thread(testRunnable3,"张三").start();
        new Thread(testRunnable3,"李四").start();
        new Thread(testRunnable3,"王五").start();

    }
}

输出结果如下,可以发现线程不安全

王五获得了第9张票
王五获得了第8张票
王五获得了第7张票
王五获得了第6张票
王五获得了第5张票
王五获得了第4张票
王五获得了第3张票
张三获得了第10张票
张三获得了第2张票
张三获得了第1张票
李四获得了第10张票

  1. ArrayList线程不安全, CopyOnWriteArrayList线程安全
package zander.example.runnable;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;

/**
 * 测试线程不安全List
 * 发现实际list的长度不足10000,因为并发导致往同一个位置插数的时候有覆盖
 * 通过synchronized修饰方法,或者通过 synchronized (obj) {}代码块实现同步
 */
public class TestUnsafeList {
    public static void main(String[] args) throws InterruptedException {
        List unsafeList = new ArrayList<String>();
        for (int i = 0; i < 10000; i++) {
            new Thread(()->{
                unsafeList.add(Thread.currentThread().getName());
            }).start();
        }
        Thread.sleep(3000);//防止线程没有执行完成就打印
        System.out.println(unsafeList.size()); //ArrayList线程不安全

         /**
         *  ArrayList线程不安全,通过synchronized (unsafeList1)变为线程安全
         */
        List unsafeList1 = new ArrayList<String>();
        for (int i = 0; i < 10000; i++) {
            new Thread(()->{
                synchronized (unsafeList1){
                    unsafeList1.add(Thread.currentThread().getName());
                }
            }).start();
        }
        Thread.sleep(3000);
        System.out.println(unsafeList1.size());
        
        
        List safeList =  new  CopyOnWriteArrayList();
        for (int i = 0; i < 10000; i++) {
            new Thread(()->{
                safeList.add(Thread.currentThread().getName());
            }).start();
        }
        Thread.sleep(3000);//防止线程没有执行完成就打印
        System.out.println(safeList.size());//CopyOnWriteArrayList线程安全
    }
}

十一、死锁

​ 产生死锁的四个必要条件:

  1. 互斥条件:一个资源每次只能被一个进程使用
  2. 请求和保持条件
package zander.example.runnable;

/**
 * 多个对象相互持有对方的锁
 */
public class TesteDeadLock {
    public static void main(String[] args) {
        C c1 = new C(0, "test1");
        C c2 = new C(1, "test2");

        new Thread(c1).start();
        new Thread(c2).start();
    }
}

class A {
}

class B {
}

class C implements Runnable{

    static A a =  new A();
    static B b = new B();

    int m;
    String name;

    public C(int m, String name) {
        this.m = m;
        this.name = name;
    }

    @Override
    public void run() {
        testDeadLock(m, name);
    }

    void testDeadLock(int m, String name){
        if(m == 0 ){
            synchronized (a){
                System.out.println("AA");
                synchronized (b){
                    System.out.println("BB");
                }
            }
        }else{
            synchronized (b){
                System.out.println("BB");
                synchronized (a) {
                    System.out.println("AA");
                }
            }
        }
    }
}

十二、Lock锁

package zander.example.runnable;

import java.util.concurrent.locks.ReentrantLock;

/**
 * Lock锁
 * ReentrantLock 可重入锁
 * 使用lock.lock()加锁, lock.unlock()解锁。
 */
public class TestLock implements Runnable {

    private int num = 10;

    private final ReentrantLock  lock = new ReentrantLock();

    @Override
    public void run() {
        while (true) {
            //模拟延时
            try {
                if (num <= 0) {
                    break;
                }
                lock.lock();  //加锁
                Thread.sleep(200);
                System.out.println(Thread.currentThread().getName() + "获得了第" + (num--) + "张票");
            } catch (InterruptedException e) {
                lock.unlock();   //解锁
            }


        }
    }

    public static void main(String[] args) {
        TestLock testRunnable3 = new TestLock();

        new Thread(testRunnable3,"张三").start();
        new Thread(testRunnable3,"李四").start();
        new Thread(testRunnable3,"王五").start();

    }
}


十三、线程协作

package zander.example.runnable;

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

/**
 * 线程池的创建
 */
public class TestPool {
    public static void main(String[] args) {

        Pool pool = new Pool();

        //创建线程池
        //newFixedThreadPool参数为线程池大小
        ExecutorService executorService = Executors.newFixedThreadPool(10);

        executorService.execute(pool);
        executorService.execute(pool);
        executorService.execute(pool);
        executorService.execute(pool);

        executorService.shutdownNow();
    }
}

class Pool implements Runnable{
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName());
    }
}

posted @ 2021-07-31 22:06  zander_zx  阅读(57)  评论(0)    收藏  举报