java多线程总结

Posted yinchh

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了java多线程总结相关的知识,希望对你有一定的参考价值。

线程状态

  • Runnable
  • Blocked
    sleep、join、wait、synchronized 四个关键字都会使线程进入Blocked状态
  • Running
  • Dead

synchronized关键字

  • Object类有两个监视器相关的方法wait、notify
  • synchronized和wait、notify同时使用,wait、notify必须在synchronized中
  • synchronized(obj)同步代码块使用obj对象的监视器实现同步
  • 实例方法使用synchronized锁定的是当前实例
  • 静态方法使用synchronized锁定的是当前类的所有实例

synchronized关键字实现多线程同步

public class TestSynchronized {
    public static void main(String[] args) throws InterruptedException {
        Test test = new Test();
        List<Thread> listThread = new ArrayList<>();
        for (int i = 0; i < 100; i++) {
            Thread thread = new Thread(new Runnable() {
                @Override
                public void run() {
                    for (int j = 0; j < 100; j++) {
                        test.Increase();
                    }
                }
            });
            listThread.add(thread);
            thread.start();
        }
        // 主线程等待所有子线程执行完成
        for (Thread t : listThread) {
            t.join();
        }
        test.print();
    }
}
class Test {
    int i = 0;

    public synchronized void Increase() {
        i++;
    }
    public void print() {
        System.out.println(i);
    }
}

synchronized配合wait、notify实现生产者消费者

public class ProducerConsumer {

    public static void main(String[] args) {
        ProCon proCon = new ProCon();
        
        //生产者
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    for (int i = 0; i < 20; i++) {
                        proCon.pro(i);
                        Thread.sleep(1000);
                    }
                } catch (InterruptedException e) {
                }
            }
        }).start();
        
        //消费者1
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    while (true) {
                        proCon.con();
                        Thread.sleep(3000);
                    }
                } catch (InterruptedException e) {
                }
            }
        }).start();

        //消费者2
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    while (true) {
                        proCon.con();
                        Thread.sleep(3000);
                    }
                } catch (InterruptedException e) {
                }
            }
        }).start();
    }

}

class ProCon {
    List<Integer> listProduct = new ArrayList<Integer>();

    public synchronized void pro(int pro) throws InterruptedException {
        if (listProduct.size() > 100) {
            System.out.println(getPrefix() + "库存已满,请稍候再生产");
            wait();
        }

        listProduct.add(pro);
        System.out.println(getPrefix() + "生产了:" + pro);
        notifyAll();// 通知消费者获取产品
    }

    public synchronized void con() throws InterruptedException {
        if (listProduct.size() <= 0) {
            System.out.println(getPrefix() + "库存不足,请及时补货");
            wait();
        }

        Integer cur = listProduct.get(0);
        listProduct.remove(cur);
        System.out.println(getPrefix() + "取走:" + cur + " 剩余库存:" + listProduct.toString());
        notifyAll();// 通知生产者生产产品
    }

    private String getPrefix() {
        return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss ").format(new Date()) + Thread.currentThread().getName() + " ";
    }
}

volatile关键字

多线程的内存模型:main memory(主存)、working memory(线程栈),线程处理数据时,先把主存中的数据load到本地栈,操作完成后会save回主存,volatile关键词的作用:每次针对该变量的操作都激发一次load and save。
针对多线程使用的变量如果不是volatile或者final修饰的,很有可能产生不可预知的结果(另一个线程修改了这个值,但是之后在某线程看到的是修改之前的值)。其实道理上讲同一实例的同一属性本身只有一个副本。但是多线程是会缓存值的,本质上,volatile就是不去缓存,直接取值。在线程安全的情况下加volatile会牺牲性能。

多线程实现

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

public class TestThread {
    public static void main(String[] args) throws InterruptedException, ExecutionException {
        // 1、继承Thread类,资源和Thread绑定,资源无法共享
        new Thread1().start();
        new Thread1().start();

        // 2、实现Runnable接口,资源和Runnable接口绑定,资源可以共享
        Thread2 thread2 = new Thread2();
        new Thread(thread2).start();
        new Thread(thread2).start();

        // 3、实现Callable<>接口
        ExecutorService e = Executors.newFixedThreadPool(3);
        Future future = e.submit(new Thread3());
        // future.isDone(); // return true,false 无阻塞
        String str = (String) future.get(); // return 返回值,阻塞直到该线程运行结束
        System.out.println("主线程结束");
        System.out.println(str);
    }
}

// 1、继承Thread类
class Thread1 extends Thread {
    int i = 0;

    @Override
    public void run() {
        i++;
        System.out.println(i);
    }
}

// 2、实现Runnable接口
class Thread2 implements Runnable {
    int i = 0;

    @Override
    public void run() {
        i++;
        System.out.println(i);
    }
}

// 3、实现Callable<>泛型接口
// 可以在任务结束后提供一个返回值,Runnable不行
// call方法可以抛出异常,Runnable的run方法不行
// 可以通过运行Callable得到的Fulture对象监听目标线程调用call方法的结果,得到返回值,(fulture.get(),调用后会阻塞,直到获取到返回值)
class Thread3 implements Callable<String> {

    @Override
    public String call() throws Exception {
        Thread.sleep(3000);
        System.out.println("子线程结束");
        return "test";
    }
}

多线程控制类

  1. ThreadLocal
    保存线程的独立变量。对一个线程类(继承自Thread)
    当使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的线程提供独立的变量副本,所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。常用于用户登录控制,如记录session信息。
    实现:每个Thread都持有一个TreadLocalMap类型的变量(该类是一个轻量级的Map,功能与map一样,区别是桶里放的是entry而不是entry的链表。功能还是一个map。)以本身为key,以目标为value。
    主要方法是get()和set(T a),set之后在map里维护一个threadLocal -> a,get时将a返回。ThreadLocal是一个特殊的容器。

未完待续。。。

以上是关于java多线程总结的主要内容,如果未能解决你的问题,请参考以下文章

经验总结:Java高级工程师面试题-字节跳动,成功跳槽阿里!

学习java第19天个人总结

号称史上最全Java多线程与并发面试题总结—基础篇

java多线程总结

Java多线程-线程池的使用与线程总结(狂神说含代码)

第十周java学习总结