Java——多线程
一、进程/线程:
1.进程:是一个正在执行的程序;进程是资源管理的最小单位;
//每一个进程执行都有一个执行顺序;该顺序是一个执行路径,或者叫一个执行单元;
2.线程:是进程中一个独立的控制单元;线程不能管理资源;
//线程在控制着进程的执行;
PS:一个进程中至少有一个线程;
二、创建线程的两种方式:
1、继承Thread类:
(1)定义类继承Thread;
(2)复写Thread中的run方法;
(3)创建类继承Thread的类对象;
(4)调用线程的start()方法:1)启动线程;2)调用run方法;
PS:1)如果不使用start(),而使用run(),则为正常调用函数,不开启线程;
2)start()仅仅是通知线程开始,只有争取到时间片后才开始运行;
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
public class ThreadTest { public static void main(String args[]) { MyThread mt1 = new MyThread(); MyThread mt2 = new MyThread(); mt1.start(); mt2.start(); for (int i = 0; i < 100; i++) { System.out.println("主线程:"+i); } System.out.println("执行完成"); } } class MyThread extends Thread { @Override public void run() { for (int i = 0; i < 100; i++) { System.out.println(this.getName() + ":" + i); } } }
2、实现runnable接口:
(1)定义类实现(implements)Runnable接口;
(2)覆盖Runnable中的run方法;
(3)通过Thread类建立线程对象;
(4)将Runnable接口中的子类对象作为实际参数传递给Thread类的构造函数;
//传入对象是为了让Thread执行指定的run方法;
(5)调用Thread类的start方法开启线程并调用Runnable接口子类的run方法;
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
public class RunnableTest { public static void main(String args[]) { Runnable myThread = new MyThread(); Thread thread1 = new Thread(myThread); Thread thread2 = new Thread(myThread); thread1.start(); thread2.start(); System.out.println("执行结束"); } } class MyThread implements Runnable { @Override public void run() { for (int i = 0; i < 100; i++) { System.out.println(Thread.currentThread().getName() + ":" + i); } } }
3、实现和继承两种方式的区别:
(1)java为单继承,继承Thread后,则无法再继承其他类;
(2)继承的线程代码存放在Thread的子类中;实现的线程代码存放在接口的子类中;
4、两种方式的匿名实现:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
public class Test { public static void main(String[] args) { //匿名继承Thread类 demo1(); //匿名实现Runnable接口 demo2(); } private static void demo1() { // new一个类(){},继承Thread这个类 new Thread() { @Override public void run() { for (int i = 0; i < 100; i++) { System.out.println(this.getName() + ":" + i); } } }.start(); } private static void demo2() { //将Runnable的子类对象当作参数传递给Thread的构造方法;new Runnable(){}:实现Runnable接口; new Thread(new Runnable() { @Override public void run() { for (int i = 0; i < 100; i++) { System.out.println(Thread.currentThread().getName() + ":" + i); } } }).start(); } }
三、线程方法:
(1).getName();获得线程的名称;(用于继承时获取线程名)//this.getName()
(2)Thread.currentThread();获取创建当前线程的对象;==this;(用于实现时获取线程名)//Thread.currentThread().getName();
(3)设置线程的名称:1)构造函数传入name;2)setName;
(4)obj.join();让指定线程加入执行,当前线程进入就绪状态不再执行,但是其他进程还是会抢时间片;加入的指定线程执行完后,当前线程开始执行;
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
public class RunnableTest { public static void main(String args[]) throws InterruptedException { Runnable myThread = new MyThread(); Thread thread1 = new Thread(myThread); Thread thread2 = new Thread(myThread); thread1.start(); thread2.start(); for (int i = 0; i < 100; i++) { thread1.join(); System.out.println("主线程:"+i); } System.out.println("执行结束"); } } class MyThread implements Runnable { @Override public void run() { for (int i = 0; i < 100; i++) { System.out.println(Thread.currentThread().getName() + ":" + i); } } }
(5)obj.sleep(毫秒[,纳秒]);休眠;休眠状态结束后进入就绪状态;
(6)obj.yield();让出时间片;线程进入到就绪状态;
//yield进入到就绪状态,还会争抢时间片,所有yield之后可能还是它第一个运行;
四、多线程状态:
五、多线程安全问题
(1)问题原因:多条语句在操作同一个线程共享数据时,一个线程对多条语句只执行了一部分,还没有执行完,另一个线程参与进来执行,导致共享数据错误;
(2)解决办法:对多条操作共享数据的语句,只能让一个线程都执行完。再让其它线程进入;
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
public class Test { public static void main(String args[]) { Runnable myThread = new MyThread(); Thread thread1 = new Thread(myThread); Thread thread2 = new Thread(myThread); thread1.start(); thread2.start(); } } class MyThread implements Runnable { private int num = 100; @Override public void run() { while (true) { if (num > 0) { try { Thread.sleep(10); } catch (Exception e) { } num--; System.out.println(Thread.currentThread().getName() + ":" + num); } } } }
六、同步代码块synchronized;
(1)同步代码块:synchronized(对象){需要被同步的代码};
(2)同步方法:访问修饰符 synchronized 返回值类型 方法名(){};
//区别:同步代码块可以指定锁;同步方法的锁只能是this;
//同步前提:1)两个或以上多线程;2)同一个对象(锁);
(3)缺点:
1)死锁:请求互斥 请求保持 不可剥夺 形成环路;
2)性能低;
3)无法灵活的加锁,解锁;
1)同步方法:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
public class Test { public static void main(String args[]) { Runnable myThread = new MyThread(); Thread thread1 = new Thread(myThread); Thread thread2 = new Thread(myThread); thread1.start(); thread2.start(); } } class MyThread implements Runnable { private int num = 100; @Override public synchronized void run() { while (true) { if (num > 0) { try { Thread.sleep(10); } catch (Exception e) { } num--; System.out.println(Thread.currentThread().getName() + ":" + num); } } } }
2)同步代码块:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
public class Test { public static void main(String args[]) { Runnable myThread = new MyThread(); Thread thread1 = new Thread(myThread); Thread thread2 = new Thread(myThread); thread1.start(); thread2.start(); } } class MyThread implements Runnable { private int num = 100; @Override public void run() { while (true) { synchronized (MyThread.class) { if (num > 0) { try { Thread.sleep(10); } catch (Exception e) { } num--; System.out.println(Thread.currentThread().getName() + ":" + num); } } } } }
3)死锁:简单的说就是两个线程拿了对方需要的锁且互不想让,导致两边都无法结束线程;
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
class MyLock { public static final Object locka = new Object(); public static final Object lockb = new Object(); } class MyThread implements Runnable { private boolean flag; MyThread(boolean flag) { this.flag = flag; } @Override public void run() { if (flag) { while (true) { synchronized (MyLock.locka) { System.out.println(Thread.currentThread().getName() + "locka...."); synchronized (MyLock.lockb) { System.out.println(Thread.currentThread().getName() + "lockb...."); } } } } else { while (true) { synchronized (MyLock.lockb) { System.out.println(Thread.currentThread().getName() + "lockb...."); synchronized (MyLock.locka) { System.out.println(Thread.currentThread().getName() + "locka...."); } } } } } } public class Test { public static void main(String[] args) { MyThread myThread1 = new MyThread(true); MyThread myThread2 = new MyThread(false); Thread t1 = new Thread(myThread1); Thread t2 = new Thread(myThread2); t1.start(); t2.start(); } }
(4)synchronized线程间通讯方法:
1.obj.wait();让当前线程处于等待状态;释放obj锁;
2.obj.notify();唤醒此锁等待线程中的一个;
3.obj.notifyAll();唤醒此锁所有等待线程;
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
public class Test { // 1、承装元素的集合 private LinkedList<Object> list = new LinkedList<>(); // 2、计数器 private AtomicInteger count = new AtomicInteger(); // 3、指定上限和下限 private final int minSize = 0; private final int maxSize; // 4、构造方法,传入maxSize的最大值 public Test(int size) { this.maxSize = size; } // 5、初始化一个对象,用于加锁 private final Object lock = new Object(); // 6、向容器增加元素,当到达最大值等待 public void put(Object obj) { synchronized (lock) { while (count.get() == this.maxSize) { try { lock.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } // 6.1、加入元素 list.add(obj); // 6.2、计数器累加 count.incrementAndGet(); System.out.println("加入:" + obj); // 6.3、唤醒另外一个线程 lock.notify(); } } // 7、从容器中取走元素,当到达0时等待 public Object take() { Object obj; synchronized (lock) { while (count.get() == this.minSize) { try { lock.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } // 7.1、移除元素 obj = list.removeFirst(); System.out.println("移除:" + obj); // 7.2、计数器递减 count.decrementAndGet(); //7.3 唤醒另外一个线程 lock.notify(); } return obj; } public static void main(String[] args) { Test t = new Test(5); new Thread("t1") { @Override public void run() { for (int i = 0; i < 50; i++) { t.put(i); Thread.yield(); } } }.start(); new Thread("t2") { @Override public void run() { for (int i = 0; i < 50; i++) { t.take(); } } }.start(); } }
七、重入锁:
//重入锁是指,加锁的一段代码内的方法,其中的方法本身已经使用了锁;
void methodA(){ lock.lock(); // 获取锁 methodB(); lock.unlock() // 释放锁 } void methodB(){ lock.lock(); // 获取锁 // 其他业务 lock.unlock();// 释放锁 }
1、Lock接口:
//不可创建实例对象,需要用它的子类ReentrantLock创建对象来调用方法;
(1)void lock();获取锁;
(2)boolean TryLock();尝试获取锁,获取成功返回true,失败返回false;jdk1.8版本后被多次获取;
(3)void unlock();释放锁; //在finally代码块中释放锁,保证100%释放成功;
//java中只有unlock和substring不符合小驼峰命名规范;据说开发java的写错了!!
(4)Condition newCondition();获得Condition对象;
2、ReentrantLock:Lock实现类;
Lock reentrantLock = new ReentrantLock();
3、Condition类:
(1)void await();等待;
(2)void signal();唤醒;
(3)void signalAll();唤醒所有;
4、 lock()/unlock()代码示例:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
public class Test implements Runnable { private Lock lock = new ReentrantLock(); private int tickets = 100; @Override public void run() { while (true) { // 获取锁 lock.lock(); try { if (tickets > 0) { Thread.sleep(100); System.out.println(Thread.currentThread().getName() + " " + tickets--); } else { break; } } catch (InterruptedException e) { e.printStackTrace(); } finally { // 释放所 lock.unlock(); } } } public static void main(String[] args) { Test test = new Test(); for (int i = 0; i < 10; i++) { Thread thread = new Thread(test, "thread" + i); thread.start(); } } }
5、线程通讯方法示例:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
public class Test { private Lock lock = new ReentrantLock(); private Condition addCondition = lock.newCondition(); private Condition removeCondition = lock.newCondition(); private LinkedList<Integer> resources = new LinkedList<>(); private int maxSize; public Test(int maxSize) { this.maxSize = maxSize; } public class Producer implements Runnable { private int proSize; private Producer(int proSize) { this.proSize = proSize; } @Override public void run() { lock.lock(); try { for (int i = 1; i < proSize; i++) { while (resources.size() >= maxSize) { System.out.println("当前仓库已满,等待消费..."); try { addCondition.await(); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println("已经生产产品数: " + i + "\t现仓储量总量:" + resources.size()); resources.add(i); removeCondition.signal(); } } finally { lock.unlock(); } } } public class Consumer implements Runnable { @Override public void run() { String threadName = Thread.currentThread().getName(); while (true) { lock.lock(); try { while (resources.size() <= 0) { System.out.println(threadName + " 当前仓库没有产品,请稍等..."); try { // 进入阻塞状态 removeCondition.await(); } catch (InterruptedException e) { e.printStackTrace(); } } // 消费数据 int size = resources.size(); for (int i = 0; i < size; i++) { Integer remove = resources.remove(); System.out.println(threadName + " 当前消费产品编号为:" + remove); } // 唤醒生产者 addCondition.signal(); } finally { lock.unlock(); } } } } public static void main(String[] args) throws InterruptedException { Test Test = new Test(10); Producer producer = Test.new Producer(100); Consumer consumer = Test.new Consumer(); final Thread producerThread = new Thread(producer, "producer"); final Thread consumerThread = new Thread(consumer, "consumer"); producerThread.start(); TimeUnit.SECONDS.sleep(2); consumerThread.start(); } }
八、线程池:
1、Executor:线程池顶级接口;//java.util. concurrent;
2、ExecutorService:Executor子接口;
方法:Future<?>.submit(Runnable task);提交任务代码;//返回Future接口类型的值;
3、Executors工厂类:通过此类获得线程池;//四种方式都会造成内存溢出;
(1)Executors.newSingleThreadExecutor(); 单实例线程池,保证提交任务的执行顺序;
(2)Executors.newFixedThreadPool(int num);获得固定数量的线程池;
(3)Executors.newCachedThreadPool();获得动态数量的线程池,如果不够创建新的;
(4)Executors.newScheduledThreadPool(); 定时任务相关线程池;
4、Callable<V>接口:具有泛型返回值,可以声明异常;与Runable接口类似;
//new Callable<Long>()
//public Long call() throws Exception { return null; }
5、Future接口:是接口池调用submit后的返回值类型;
方法:.get();返回Future接口的值,值类型为Callable中泛型指定的值;
6、ThreadPoolExecutor类:Executor接口实现类;
Executors.newScheduledThreadPool();cpu核数
ExecutorService pool = new ThreadPoolExecutor(100, 200, 100, TimeUnit.MILLISECONDS, new LinkedBlockingDeque<Runable>(100));
参数:100初始化线程数;200最大线程数;100呆滞时间;单位时间;队列;
呆滞时间:当线程池处于空闲状态时,进行多余线程的回收参照时间;
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
public class Test { public static void main(String[] args) throws InterruptedException, ExecutionException { ExecutorService pool = Executors.newFixedThreadPool(5); Future<Long> future1 = pool.submit(new Callable<Long>() { @Override public Long call() throws Exception { Long res = 0L; for (int i = 0; i <= 50; i++) { res += i; } return res; } }); Future<Long> future2 = pool.submit(new Callable<Long>() { public Long call() throws Exception { Long res = 0L; for (int i = 51; i <= 100; i++) { res += i; } return res; } }); Long res1 = future1.get(); Long res2 = future2.get(); System.out.println(res1); System.out.println(res2); } }
//创建100个线程 public class Test { public static void main(String[] args) throws InterruptedException, ExecutionException { ExecutorService pool = new ThreadPoolExecutor(100, 200, 100, TimeUnit.MILLISECONDS, new LinkedBlockingDeque<>(100)); for (int i = 0; i < 100; i++) { pool.submit(new Runnable() { @Override public void run() { System.out.println(Thread.currentThread().getName() + "..."); } }); } } }