https://blog.csdn.net/beidaol/article/details/89135277
调用run()方法后,主线程去执行完run()方法后再执行主线程的方法;
调用start()方法后,会新建一个子线程去执行run()方法,主线程和子线程交替执行。
每个对象都有一个锁,sleep()不会释放锁
Thread.yield();
@Override public void run() { System.out.println(Thread.currentThread().getName()+"线程开始执行"); Thread.yield(); System.out.println(Thread.currentThread().getName()+"线程开始执行"); }
如果礼让成功,会让出cpu给b执行
否则cpu继续执行a
插队执行线程
Thread.join()
Thread.State state = thread.getState();
thread.setPriority(4);
thread.setDaemon(true);//默认是false表示用户线程
并发:同一个对象被多个线程同时操作,每个对象都有锁
由于同一进程的多个线程共享一块存储空间,在带来方便的同时,也带来了访问冲突问题,为了保证数据在方法中被访问时的正确性,在访问时加入锁机制
synchronized,当一个线程获得对象的排它锁,独占资源,其他线程必须等待,使用后释放锁即可,存在以下问题:
一个线程持有锁会导致其他所有需要此锁的线程挂起
在多线程竞争下,加锁,释放锁会导致比较多的上下文切换和调度延时,引起性能问题
如果一个优先级高的线程等待一个优先级低的线程释放锁会导致优先级倒置,引起性能问题
public static void main(String[] args) { List<String> list = new ArrayList<>(); for (int i = 0; i < 10000; i++) { new Thread(() -> { list.add(Thread.currentThread().getName()); }).start(); } System.out.println(list.size()); }
输出结果小于10000,因为ArrayList不安全,两个线程同一时间给同一位置赋值时覆盖掉了,所以小于10000
缺陷:若将一个大的方法申明为synchronized将会影响效率
class BuyTicket implements Runnable { //票 private int ticketNums = 10; // 外部停止 boolean flag = true; @Override public synchronized void run() { // 买票 while (flag) { try { buy(); } catch (InterruptedException e) { e.printStackTrace(); } } } private void buy() throws InterruptedException { if (ticketNums <=0) { flag = false; return; } Thread.sleep(100); System.out.println(Thread.currentThread().getName()+"拿到"+ticketNums--); } }
以下代码同步块锁的是account
class Account{ int money; String name; public Account(int money, String name) { this.money = money; this.name = name; } } class Drawing extends Thread{ Account account; int drawingMoney; int nowMoney; public Drawing(Account account, int drawingMoney, String name) { super(name); this.account = account; this.drawingMoney = drawingMoney; } @Override public void run() { //所得对象是变化的量,需要增删改的 synchronized (account) { if (account.money-drawingMoney<0) { System.out.println(Thread.currentThread().getName()+"钱不够了,取不了"); return; } } account.money = account.money - drawingMoney; nowMoney = nowMoney + drawingMoney; System.out.println(account+"余额为:"+account.money); } }
产生死锁的四个必要条件:
上面列出的四个必要条件,只要想办法破其中一个或多个条件就可以避免死锁发生
@Override public void run() { while (true) { try { // 加锁 lock.lock(); if (ticketNum > 0) { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(ticketNum--); } else { break; } } finally { //解锁 lock.unlock(); } } }
执行wait()方法时,this(当前对像)线程会停这里,直到notify()唤醒才往下执行
(1)降低系统资源消耗,通过重用已存在的线程,降低线程创建和销毁造成的消耗;
(2)提高系统响应速度,当有任务到达时,通过复用已存在的线程,无需等待新线程的创建便能立即执行;
(3)方便线程并发数的管控。因为线程若是无限制的创建,可能会导致内存占用过多而产生OOM,并且会造成cpu过度切换(cpu切换线程是有时间成本的(需要保持当前执行线程的现场,并恢复要执行线程的现场))。
(4)提供更强大的功能,延时定时线程池。
(1)corePoolSize(线程池基本大小):当向线程池提交一个任务时,若线程池已创建的线程数小于corePoolSize,即便此时存在空闲线程,也会通过创建一个新线程来执行该任务,直到已创建的线程数大于或等于corePoolSize时,(除了利用提交新任务来创建和启动线程(按需构造),也可以通过 prestartCoreThread() 或 prestartAllCoreThreads() 方法来提前启动线程池中的基本线程。)
(2)maximumPoolSize(线程池最大大小):线程池所允许的最大线程个数。当队列满了,且已创建的线程数小于maximumPoolSize,则线程池会创建新的线程来执行任务。另外,对于无界队列,可忽略该参数。
(3)keepAliveTime(线程存活保持时间)当线程池中线程数大于核心线程数时,线程的空闲时间如果超过线程存活时间,那么这个线程就会被销毁,直到线程池中的线程数小于等于核心线程数。
(4)workQueue(任务队列):用于传输和保存等待执行任务的阻塞队列。
(5)threadFactory(线程工厂):用于创建新线程。threadFactory创建的线程也是采用new Thread()方式,threadFactory创建的线程名都具有统一的风格:pool-m-thread-n(m为线程池的编号,n为线程池内的线程编号)。
(6)handler(线程饱和策略):当线程池和队列都满了,再加入线程会执行此策略。
1、判断核心线程池是否已满,没满则创建一个新的工作线程来执行任务。已满则。
2、判断任务队列是否已满,没满则将新提交的任务添加在工作队列,已满则。
3、判断整个线程池是否已满,没满则创建一个新的工作线程来执行任务,已满则执行饱和策略。