17 Java多线程(线程创建+线程状态+线程安全+死锁+线程池+Lock接口+线程安全集合)(上):https://developer.aliyun.com/article/1580253
17.3线程状态
17.3.1 线程状态
线程状态:新建、就绪、运行、终止。
17.3.2 常见方法
方法名 | 说明 |
public static void sleep(long millis) | 当前线程主动休眠 millis 毫秒。 |
public static void yield() | 当前线程主动放弃时间片,回到就绪状态,竞争下一次时间片。 |
public final void join() | 允许其他线程加入到当前线程中。 |
public void setPriority(int) | 线程优先级为1-10,默认为5,优先级越高,表示获取CPU机会越多。 |
public void setDaemon(boolean) | 设置为守护线程线程有两类:用户线程(前台线程)、守护线程(后台线程) |
17.3.3 线程状态(等待)
线程状态:新建、就绪、运行、等待、终止。
17.4 线程安全
为什么会出现线程安全问题?
- 需求:A线程将“Hello”存入数组;B线程将“World”存入数组。
- 线程不安全:
- 当多线程并发访问临界资源时,如果破坏原子操作,可能会造成数据不一致。
- 临界资源:共享资源(同一对象),一次仅允许一个线程使用,才可保证其正确性。
- 原子操作:不可分割的多步操作,被视作一个整体,其顺序和步骤不可打乱或缺省。
案例演示:
public class ThreadSafe { private static int index=0; public static void main(String[] args) throws Exception{ //创建数组 String[] s=new String[5]; //创建两个操作 Runnable runnableA=new Runnable() { @Override public void run() { //同步代码块 synchronized (s) { s[index]="hello"; index++; } } }; Runnable runnableB=new Runnable() { @Override public void run() { synchronized (s) { s[index]="world"; index++; } } }; //创建两个线程对象 Thread a=new Thread(runnableA,"A"); Thread b=new Thread(runnableB,"B"); a.start(); b.start(); a.join();//加入线程 b.join();//加入线程 System.out.println(Arrays.toString(s)); } }
17.4.1 同步代码块
语法:
synchronized(临界资源对象){ //对临界资源对象加锁
//代码(原子操作)
}
演示案例:
Ticket类:
public class Ticket implements Runnable{ private int ticket=100; //创建锁 //private Object obj=new Object(); @Override public void run() { while(true) { synchronized (this) {//this ---当前对象 if(ticket<=0) { break; } System.out.println(Thread.currentThread().getName()+"卖了第"+ticket+"票"); ticket--; } } } }
17.4.2 线程状态(阻塞)
线程状态:新建、就绪、运行、阻塞、终止。
17.4.3 同步方法
语法:
synchronized 返回值类型 方法名称(形参列表){ //对当前对象(this)加锁
// 代码(原子操作)
}
17.4.4 同步规则
- 只有在调用包含同步代码块的方法,或者同步方法时,才需要对象的锁标记。
- 如调用不包含同步代码块的方法,或普通方法时,则不需要锁标记,可直接调用。
JDK中线程安全的类:
- StringBuffer
- Vector
- Hashtable
以上类中的公开方法,均为synchonized修饰的同步方法。
17.5 死锁
17.5.1 什么是死锁?
- 当第一个线程拥有A对象锁标记,并等待B对象锁标记,同时第二个线程拥有B对象锁标记,并等待A对象锁标记时,产生死锁。
- 一个线程可以同时拥有多个对象的锁标记,当线程阻塞时,不会释放已经拥有的锁标记,由此可能造成死锁。
17.5.2 死锁案例
MyLock类:
public class MyLock { //两个锁(两个筷子) public static Object a=new Object(); public static Object b=new Object(); }
BoyThread类:
public class Boy extends Thread{ @Override public void run() { synchronized (MyLock.a) { System.out.println("男孩拿到了a"); synchronized (MyLock.b) { System.out.println("男孩拿到了b"); System.out.println("男孩可以吃东西了..."); } } } }
GirlThread类:
public class Girl extends Thread { @Override public void run() { synchronized (MyLock.b) { System.out.println("女孩拿到了b"); synchronized (MyLock.a) { System.out.println("女孩拿到了a"); System.out.println("女孩可以吃东西了..."); } } } }
TestDeadLock类:
public class TestDeadLock { public static void main(String[] args) { Boy boy=new Boy(); Girl girl=new Girl(); girl.start(); try { Thread.sleep(100); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } boy.start(); } }
17.6 线程通信
17.6.1 线程通信方法
方法 | 说明 |
public final void wait() | 释放锁,进入等待队列 |
public final void wait(long timeout) | 在超过指定的时间前,释放锁,进入等待队列 |
public final void notify() | 随机唤醒、通知一个线程 |
public final void notifyAll() | 唤醒、通知所有线程 |
17.6.2 生产者消费者
若干个生产者在生产产品,这些产品将提供给若干个消费者去消费,为了使生产者和消费者能并发执行,在两者之间设置一个能存储多个产品的缓冲区,生产者将生产的产品放入缓冲区中,消费者从缓冲区中取走产品进行消费,显然生产者和消费者之间必须保持同步,即不允许消费者到一个空的缓冲区中取产品,也不允许生产者向一个满的缓冲区中放入产品。
Bread类:
pupublic class Bread { private int id; private String productName; public Bread() { // TODO Auto-generated constructor stub } public Bread(int id, String productName) { super(); this.id = id; this.productName = productName; } public int getId() { return id; } public void setId(int id) { this.id = id; } public String getProductName() { return productName; } public void setProductName(String productName) { this.productName = productName; } @Override public String toString() { return "Bread [id=" + id + ", productName=" + productName + "]"; } }
BreadCon类:
public class BreadCon { //存放面包的数组 private Bread[] cons=new Bread[6]; //存放面包的位置 private int index=0; //存放面包 public synchronized void input(Bread b) { //锁this //判断容器有没有满 while(index>=6) { try { this.wait(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } cons[index]=b; System.out.println(Thread.currentThread().getName()+"生产了"+b.getId()+""); index++; //唤醒 this.notifyAll(); } //取出面包 public synchronized void output() {//锁this while(index<=0) { try { this.wait(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } index--; Bread b=cons[index]; System.out.println(Thread.currentThread().getName()+"消费了"+b.getId()+" 生产者:"+b.getProductName()); cons[index]=null; //唤醒生产者 this.notifyAll(); } }
Consume类:
public class Consume implements Runnable{ private BreadCon con; public Consume(BreadCon con) { super(); this.con = con; } @Override public void run() { for(int i=0;i<30;i++) { con.output(); } } }
Produce类:
public class Prodcut implements Runnable { private BreadCon con; public Prodcut(BreadCon con) { super(); this.con = con; } @Override public void run() { for(int i=0;i<30;i++) { con.input(new Bread(i, Thread.currentThread().getName())); } } }
Test类:
public class Test { public static void main(String[] args) { //容器 BreadCon con=new BreadCon(); //生产和消费 Prodcut prodcut=new Prodcut(con); Consume consume=new Consume(con); //创建线程对象 Thread chenchen=new Thread(prodcut, "晨晨"); Thread bingbing=new Thread(consume, "消费"); Thread mingming=new Thread(prodcut, "明明"); Thread lili=new Thread(consume, "莉莉"); //启动线程 chenchen.start(); bingbing.start(); mingming.start(); lili.start(); } }
17 Java多线程(线程创建+线程状态+线程安全+死锁+线程池+Lock接口+线程安全集合)(下):https://developer.aliyun.com/article/1580256