关于线程的简单总结
线程基本使用
- 进程:一个程序的运行就可以说是一个进程;
- 线程:一个进程可以包含多个线程,它可以所说是进程的“小弟”,负责干活的;
- 线程三种实现方式:
extends Thread
implements Runnable
implements Callable
- Thread类中的start方法和run方法:start方法是启动线程,它只是使线程处于就行状态;run方法被叫做线程体,它是线程要执行的内容;
- 线程的五个状态:
NEW //新建线程
RUNNABLE //执行start方法,启动线程,就绪状态
RUNNING //获得cpu,执行状态
BLOCKED //阻塞状态,执行sleep.wait等方法
TERMINATED //线程死亡,不可逆转
- 线程休眠:线程暂停执行;
Thread.sleep(1000);//还有一个更好的方法
- 线程停止:一般建议执行完,自然停止;废弃的方法例如stop不建议使用;如果想要停止线程建议设置“标志位”;
- 线程礼让:注意并不一定能够礼让成功!
Thread.yield();//让先执行的线程停止,把资源让给其他线程,等待其他线程执行结束后再执行
-
线程优先级:范围0-10,优先级越高,能够被执行的概率越大;优先级具有继承性,例如a线程启动b线程,那么a,b线程优先级相等;
-
守护线程:虚拟机不要等待其是否执行完毕的线程,例如后台的垃圾回收线程。注意:daemon线程(守护线程)finally代码块里面的不一定执行;
-
线程“插队”:join方法
//测试Join:想象成“插队” public class TestJoin implements Runnable{ @Override public void run() { for (int i = 0; i < 1000; i++) { System.out.println("join:线程vip来了"+i); } } public static void main(String[] args) throws InterruptedException { TestJoin join = new TestJoin(); Thread thread = new Thread(join); //这种写法存在问题: // 主线程运行到200之前两个线程谁抢到CPU谁执行 ,主线程执行到200之后,“插队”线程一直执行到结束,主线程才继续执行 thread.start(); //主线程 for (int i = 0; i < 200; i++) { if (i==100){ //thread.start();这种写法没有问题 thread.join(); } System.out.println("main主线程"+i); } } }
死锁
synchronized关键字
-
作用:保证线程安全,例如当多个线程访问统一资源时,会发生数据紊乱问题;
-
用法:用来修饰代码块和方法
-
实例:
package 线程同步;
//不安全的线程import sun.security.krb5.internal.Ticket; /* 可以买到负数的票的原因: 当只剩下一张票时,三个线程都认为是有票; 因此它们都把“1”拿到自己的内存; 第一次拿后,对象内存剩余0; 第二次拿后,对象内存剩余-1; */ public class UnsafeTicket { public static void main(String[] args) { BuyTicket ticket = new BuyTicket(); new Thread(ticket,"倒霉的我").start(); new Thread(ticket,"幸运的你们").start(); new Thread(ticket,"可恶的黄牛党").start(); } } class BuyTicket implements Runnable{ private int tickets = 10; private boolean flag = true; @Override public void run() { while (flag){ try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } buy(); } } //在方法名前加上"synchronized",那么该方法就变成了同步方法了 //!!!锁的是"this"!!! //'synchronized'默认锁的是this private synchronized void buy(){ if (tickets<=0){ flag=false; //这个return放在这里,如果符合if条件,那么返回出去,不执行下面的sout语句 return; } System.out.println(Thread.currentThread().getName()+"买到了第"+tickets--+"张票"); } }
Lock锁
-
特性:显示定义同步锁
-
应用实例:
package 死锁;
//Lock锁import java.util.concurrent.locks.ReentrantLock; public class TestLock { public static void main(String[] args) { Lock lock = new Lock(); new Thread(lock).start(); new Thread(lock).start(); new Thread(lock).start(); } } class Lock implements Runnable{ private int ticketNum = 10; private final ReentrantLock lock1 = new ReentrantLock(); @Override public void run() { while (true){ try{ //加锁 lock1.lock(); if (ticketNum>0){ try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(ticketNum--); }else { break; } }finally { //解锁 lock1.unlock(); } } } }
synchronized、Lock二者的区别?
- 这里暂时没有总结,先欠着!
线程通信(协作)
上面讲到的都是单个线程或者是线程与线程之间互不影响,互不作用;那如果线程之间有前后的联系或者影响,那该怎么表达?
-
wait():是当前正在执行的线程进入等待状态,在wait所在的代码处停止执行,直到接到通知;
-
notify()/notifyAll():将处于等待状态的进程移动到同步队列;
package 线程协作;
//生产者消费者模型:通过缓冲区来解决-管程法
//生产者、消费者、产品、缓冲区
public class TestPC {public static void main(String[] args) { SynContainer synContainer = new SynContainer(); new Productor(synContainer).start(); new Consumer(synContainer).start(); } } //生产者 class Productor extends Thread{ SynContainer synContainer; public Productor(SynContainer synContainer) { this.synContainer = synContainer; } @Override public void run() { for (int i = 0; i < 100; i++) { try { synContainer.push(new Chicken(i)); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("生产了第"+i+"只鸡"); } } } //消费者 class Consumer extends Thread{ SynContainer synContainer; public Consumer(SynContainer synContainer) { this.synContainer = synContainer; } @Override public void run() { for (int i = 0; i < 100; i++) { try { System.out.println("消费了第"+synContainer.pop().id+"只鸡"); } catch (InterruptedException e) { e.printStackTrace(); } } } } //产品 class Chicken{ int id; public Chicken(int id) { this.id = id; } } //缓冲区 class SynContainer{ //掌握数组定义方式 Chicken[] chickens = new Chicken[10]; //容器计数器 int count = 0; //1.生产者生产产品 public synchronized void push(Chicken chicken) throws InterruptedException { //1.1判断容器是否已满,如果已满则生产者等待 if (chickens.length==count){ //缓冲区已满,通知消费者消费 this.wait(); } //1.2如果容器没有满的话,则生产者继续生产产品 chickens[count] = chicken; count++; //消费者消费 this.notifyAll(); } //2.消费者消费产品 public synchronized Chicken pop() throws InterruptedException { //2.1判断容器是否有产品,如果没有则通知生产者进行生产 if (count==0){ //消费者等待 this.wait(); } //2.2容器有产品,则消费者进行消费 count--; Chicken chicken = chickens[count]; //吃完了通知生产者进行生产 this.notifyAll(); return chicken; } }
-
ThreadLocal类:给某个线程绑定一个值。存放线程的私有数据;
public class XKThreadLocal {public static ThreadLocal threadLocal = new ThreadLocal(); public static void main(String[] args) { if (threadLocal.get() == null) { System.out.println("未设置过值"); threadLocal.set("Mtf"); } System.out.println(threadLocal.get()); } }
-
解决get()返回null问题:通过继承重写initialValue()方法;
public class ThreadLocalExt extends ThreadLocal{static ThreadLocalExt threadLocalExt = new ThreadLocalExt(); @Override protected Object initialValue() { return "Mtf"; } public static void main(String[] args) { System.out.println(threadLocalExt.get()); } }
-
线程池
-
概念:线程池产生的目的就是为了提高性能而来;
-
应用实例:
package 线程池;import java.util.concurrent.Executor; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; //创建线程池 public class Test { public static void main(String[] args) { MyThread myThread = new MyThread(); //创建服务,创建线程池 ExecutorService ser = Executors.newFixedThreadPool(10); //执行 ser.execute(myThread); ser.execute(myThread); ser.execute(myThread); ser.execute(myThread); ser.shutdown(); } } class MyThread implements Runnable{ @Override public void run() { System.out.println(Thread.currentThread().getName()); } }