Java多线程(全知识点)(上):https://developer.aliyun.com/article/1419402
线程同步
案例卖票
学习代码:
public class MyThreadDemo { public static class SellTicket implements Runnable{ // 默认有100张票 private int ticket = 100; @Override public void run() { while (true){ if (ticket > 0){ System.out.println(Thread.currentThread().getName()+"正在出售第:"+ticket+"张票"); ticket --; } } } } public static void main(String[] args){ SellTicket st = new SellTicket(); // 定义对象 Thread t1 = new Thread(st,"窗口1"); Thread t2 = new Thread(st,"窗口2"); Thread t3 = new Thread(st,"窗口3"); t1.start(); t2.start(); t3.start(); } }
运行结果:
改进之后的思考
学习代码:
public class MyThreadDemo { public static class SellTicket implements Runnable{ private int ticket = 100; @Override public void run() { while (true){ if (ticket > 0){ try { // 卖票的时候 添加休眠 Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"正在出售第:"+ticket+"张票"); ticket --; } } } } public static void main(String[] args){ SellTicket st = new SellTicket(); Thread t1 = new Thread(st,"窗口1"); Thread t2 = new Thread(st,"窗口2"); Thread t3 = new Thread(st,"窗口3"); t1.start(); t2.start(); t3.start(); } }
运行结果:
出现问题:
原因分析:
- 出现相同的票
- 出现负数的票
安全问题的解决方案
学习代码:
public class MyThreadDemo { public static class SellTicket implements Runnable{ private int ticket = 100; private Object obj = new Object(); // 在外面定义这个Object对象 @Override public void run() { while (true){ synchronized (obj){ // 不要在这里面定义这个对象 不然3个进程会产生3把锁 还是不能解决问题 if (ticket > 0){ try { // 卖票的时候 添加休眠 Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"正在出售第:"+ticket+"张票"); ticket --; } } } } } public static void main(String[] args){ SellTicket st = new SellTicket(); Thread t1 = new Thread(st,"窗口1"); Thread t2 = new Thread(st,"窗口2"); Thread t3 = new Thread(st,"窗口3"); t1.start(); t2.start(); t3.start(); } }
运行结果:
同步方法
非静态的锁
学习代码:
public class MyThreadDemo { public static class SellTicket implements Runnable{ private int ticket = 100; private int x = 0; @Override public void run() { try { // 卖票的时候 添加休眠 Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } while (true){ if (x % 2 == 0){ synchronized (this){ // 非静态用的this这个锁 if (ticket > 0){ try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"正在出售第:"+ticket+"张票"); ticket --; } } } else { sellTicket(); } } } private synchronized void sellTicket(){ if (ticket > 0){ try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"正在出售第:"+ticket+"张票"); ticket --; } } } public static void main(String[] args){ SellTicket st = new SellTicket(); Thread t1 = new Thread(st,"窗口1"); Thread t2 = new Thread(st,"窗口2"); Thread t3 = new Thread(st,"窗口3"); t1.start(); t2.start(); t3.start(); } }
运行结果:
静态方法的锁
学习代码:
public class SellTicket implements Runnable{ private static int ticket = 100; private int x = 0; @Override public void run() { try { // 卖票的时候 添加休眠 Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } while (true){ if (x % 2 == 0){ synchronized (SellTicket.class){ // 静态的用这个锁 if (ticket > 0){ try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"正在出售第:"+ticket+"张票"); ticket --; } } } else { sellTicket(); } } } private static synchronized void sellTicket(){ if (ticket > 0){ try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"正在出售第:"+ticket+"张票"); ticket --; } }
线程安全的类
案例:生产者与消费者
学习代码:
package Safe; public class MyThreadDemo { // 生产者类 public static class Producer implements Runnable{ private Box b; public Producer(Box b) { this.b = b; } @Override public void run() { for (int i = 1; i <= 5; ++ i){ // 放置牛奶 b.put(i); } } } // 消费者类 public static class Customer implements Runnable{ private Box b; public Customer(Box b) { this.b = b; } @Override public void run() { // 获取牛奶 while(true){ b.get(); } } } // 奶箱类 public static class Box{ // 定义一个成员变量 表示第x瓶牛奶 private int milk; // 定义一个成员变量,表示奶箱的状态 private boolean state = false; // 提供存储牛奶和获取牛奶的操作 // 存储牛奶 public synchronized void put(int milk){ // 如果有牛奶,等待消费 if (state){ try { wait(); } catch (InterruptedException e) { e.printStackTrace(); } } // 如果没有牛奶,就生产牛奶 this.milk = milk; System.out.println("送奶工将第"+this.milk+"瓶奶放入奶箱"); // 生产完毕之后,修改奶箱状态 state = true; // 唤醒其他等待线程 notifyAll(); } // 获取牛奶 public synchronized void get(){ // 如果没有牛奶,等待生产 if(!state){ try { wait(); } catch (InterruptedException e) { e.printStackTrace(); } } // 如果有牛奶,就消费牛奶 System.out.println("用户拿到第"+this.milk+"瓶奶"); // 消费完毕之后,修改奶箱状态 state = false; // 唤醒其他等待线程 notifyAll(); } } public static void main(String[] args){ // 创建奶箱对象,这是共享数据区域 Box b = new Box(); // 创建生产者对象,把奶箱对象作为构造方法参数传递,因为在这个类种要调用存储牛奶的操作 Producer p = new Producer(b); // 创建消费者对象,把牛奶对象作为构造方法参数传递,因为在这个类种要调用获取牛奶的操作 Customer c = new Customer(b); // 创建两个线程对象,分别把生产者对象和消费者对象作为构造方法参数传递 Thread t1 = new Thread(p); Thread t2 = new Thread(c); // 启动线程 t1.start(); t2.start(); } }
运行结果:
Lock锁
学习代码:
import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; public class Main { public static class SellTicket implements Runnable{ private int ticket = 100; private Lock lock = new ReentrantLock(); @Override public void run() { while(true){ try { lock.lock(); if (ticket > 0){ try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"正在出售第"+ticket+"张票"); ticket--; } } finally { // 为了使得这个锁怎么都可以释放 所以要用到finally关键字 lock.unlock(); } } } } public static void main(String[] args){ SellTicket st = new SellTicket(); Thread t1 = new Thread(st, "窗口一"); Thread t2 = new Thread(st, "窗口二"); Thread t3 = new Thread(st, "窗口三"); t1.start(); t2.start(); t3.start(); } }
运行结果: