多线程Thread二

3.4 线程礼让 yeild

  • 暂停当前正在执行的线程,但不阻塞
  • 将线程从运行状态转为就绪状态
  • 让cpu重新调度,重新竞争,礼让不易i的那个成功,看CPU心情
    使用场景:执行某项复杂的任务时,如果担心占用资源过
package thread;

import java.text.SimpleDateFormat;

public class TestYield {

	public static void main(String[] args) {
		MyYield myYield = new MyYield();
		new Thread(myYield,"a").start();
		new Thread(myYield,"b").start();
	}
	
	
}
class MyYield implements Runnable{

	public void run() {
		System.out.println(Thread.currentThread().getName()+" start to run");
		Thread.yield();//yield
		System.out.println(Thread.currentThread().getName()+" stop to run");
	}
	
	
}

3.5 Join

*原来主线程和子线程是并行关系,一旦使用join方法后就会变成串行关系
*主线程调用子线程的join()方法时,子线程插队,此时执行子线程阻塞主线程,待子线程完成后,再执行主线程

package Thread;
//测试join插队
public class TestJoin implements Runnable {
    @Override
    public void run() {
        for (int i = 0; i < 50; i++) {
            System.out.println("线程vip来了 "+i);
        }

    }

    public static void main(String[] args) throws InterruptedException {
        TestJoin testJoin =  new TestJoin();
        Thread subThread = new Thread(testJoin);
        subThread.start();

        for (int i = 0; i < 100; i++) {
            if(i == 10){
                subThread.join();
            }
            System.out.println("main " + i );
        }
    }
}

3.7 线程状态观测

  • Thread.state
    • NEW
      尚未启动的线程处于此状态
    • RUNNABLE
      在Java虚拟机中执行的线程处于此状态
    • BLOCKED
      被阻塞等待监视器锁定的线程处于此状态
    • WAITTING
      正在等待另一个线程执行特定动作的线程处于此状态
    • TIMED_WAITTING
      正在等待另一个线程执行动作达到指定等待时间的线程处于此状态
    • TERMINATED
      已退出的线程处于此状态
      一个线程可以在给定时间点处于一个状态。这些状态是不反映任何操作系统线程状态的虚拟机状态.
package Thread;

public class TestState {
    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(()->{
            for (int i = 0; i < 5; i++) {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("//////");
            }
        });
        //观察新生状态
        Thread.State state = thread.getState();
        System.out.println(state);

        //观察启动后
        thread.start();
        state = thread.getState();
        System.out.println(state);

        //只要线程不终止,就一直输出状态
        while(state != Thread.State.TERMINATED){
            Thread.sleep(100);
            state = thread.getState();
            System.out.println(state);
        }
        //死亡之后的线程就不能再启动了
        //thread.start();
    }
}

对应的输出结果,在sleep是对应 TIMED_WATTING状态,线程死亡后不能再次启动

NEW
RUNNABLE
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
//////
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
//////
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
//////
RUNNABLE
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
//////
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
//////
TERMINATED

Process finished with exit code 0

3.8 线程优先级

  • 所有线程启动后进入就绪状态,此时java线程调度器按照优先级决定应该调用哪个线程来执行
  • 优先级范围1~10, 优先级越高中奖率越高,被CPU选中概率越大。但并不意味着低优先级的线程不能先执行,主要看CPU心情
    • Thread.MIN_PRIORITY = 1 (最小)
    • Thread.MAX_PRIORITY = 10(最大)
    • Thread.NORM_PRIORITY = 5 (正常默认)
  • 改变或获取优先级方法
    getPriority().setPriority(int xxx)
  • 优先级设置应该在start之前
package Thread;
public class TestPriority {
    public static void main(String[] args) {
        //打印主线程默认优先级,改不了的
        System.out.println(Thread.currentThread().getName()+"-->"+Thread.currentThread().getPriority());

      MyPriority myPriority  = new MyPriority();
       Thread t1 = new Thread(myPriority,"t1");
       Thread t2 = new Thread(myPriority,"t2");
       Thread t3 = new Thread(myPriority,"t3");
       Thread t4 = new Thread(myPriority,"t4");
       Thread t5 = new Thread(myPriority,"t5");
       Thread t6 = new Thread(myPriority,"t6");

       //先设置一下优先级再start
        t1.start();

        t2.setPriority(1);
        t2.start();
        t3.setPriority(4);
        t3.start();
        t4.setPriority(Thread.MAX_PRIORITY);
        t4.start();
  /*      t5.setPriority(-1);
        t5.start();
        t6.setPriority(13);
        t6.start();*/
    }
}
class MyPriority implements Runnable{
    public void run() {
        System.out.println(Thread.currentThread().getName()+"-->"+Thread.currentThread().getPriority());
    }
}

3.9 守护线程

  • 线程分为用户线程守护线程
  • 虚拟机必须确保拥核线程执行完毕
  • 虚拟机不用确保守护线程执行完毕
  • 应用举例如:后台记录操作日志,监控内存,垃圾回收gc等...
  • 默认创建的都是用户线程,只有手动设置了true才是守护线程
    threadName.setDaemon(true);
    例 人生不过300天
package Thread;
//测试守护线程,
public class TestDaemon {
    public static void main(String[] args) {
        God god = new God();
        You you = new You();

        Thread thread = new Thread(god);
        //默认创建的都是用户线程,只有手动设置了true才是守护线程
        thread.setDaemon(true);

        thread.start();

        new Thread(you).start();
    }
}

//God
class God implements Runnable{
    @Override
    public void run() {
        while(true){
            System.out.println("god monitor you");
        }
    }
}

//你
class You implements Runnable{
    @Override
    public void run() {
        for (int i = 0; i < 36500; i++) {
            System.out.println("开心每一天");
        }
        System.out.println("goodbye world");
    }
}

4 线程同步

并发 多个线程操作同一个资源,如买票,取钱
解决 队列+锁 保证线程同步的安全性

4.1 线程同步

锁机制 synchronized,当一个线程获得对象的锁,独占资源,其他线程必须等待,使用后释放锁,缺点:

  • 其他线程挂起---一个线程持有锁会导致其他所有需要此锁的线程挂起
  • 锁处理耗时---在多线程竞争下,加锁,释放锁会导致比较多的上下文切换和调度延时
  • 优先级倒置---如果一个优先级高的线程等待一个优先级低的线程释放锁,会导致优先级倒置,引起性能问题
    三大不安全案例
    例1 买票
    每个线程在自己的工作内存交互,两线程都把余票数据复制到自己内存中,剩余一张票的时候,连续自减两次就出现了负票
package thread;

public class UnsafeBuyTicket {
	public static void main(String argvs[]){
		BuyTicket buyTicket = new BuyTicket();
		new Thread(buyTicket,"xiaoming").start();
		new Thread(buyTicket,"xiaohong").start();
		new Thread(buyTicket,"xiaolan").start();
	}

}

class BuyTicket implements Runnable{
	private int ticketNums = 10;
	boolean flag = true;

	@Override
	public void run() {
		while(flag){
			buy();
		}
	}
	
	private void buy(){
		
		if(ticketNums<=0){
			flag = false;
			return;
		}
		try {
			Thread.sleep(100);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		System.out.println(Thread.currentThread().getName()+"tack the "+ ticketNums--+" ticket");
	}
	
}

result:

xiaohongtack the 10 ticket
xiaolantack the 8 ticket
xiaomingtack the 9 ticket
xiaohongtack the 7 ticket
xiaolantack the 6 ticket
xiaomingtack the 5 ticket
xiaohongtack the 4 ticket
xiaomingtack the 3 ticket
xiaolantack the 2 ticket
xiaohongtack the 1 ticket
xiaomingtack the 0 ticket
xiaolantack the -1 ticket

例2. 两人同时取一个账号里的钱

package thread;

public class UnsafeBank {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Account account = new Account("fund", 100);
		Drawing you = new Drawing(account, 50,"you");
		Drawing wife = new Drawing(account, 100,"wife");
		
		you.start();
		wife.start();
	}

}

class Account {
	String name;
	int money;

	public Account(String name, int money) {
		this.name = name;
		this.money = money;

	}

}

class Drawing extends Thread {
	Account account;
	int drawingMoney;

	public Drawing(Account account, int drawingMoney, String name) {
		super(name);
		this.account = account;
		this.drawingMoney = drawingMoney;
		
	}

	public void run(){
		if (account.money-drawingMoney<0){
			System.out.println(Thread.currentThread().getName()+"not enough, can't withdraw");
			return;
		}
		try {
			//写在判断条件后面,放大问题发生可能性
			Thread.sleep(1000);
		} catch (InterruptedException e) {
			
			e.printStackTrace();
		}
		account.money -= drawingMoney;
		
		System.out.println(account.name+ " balance is "+ account.money);
		//Thread.currentThread().getName() ==  this.getName(), current class extend Thread
		System.out.println(this.getName() +" money in hand is "+ drawingMoney);
	}
}

result:

fund balance is -50
you money in hand is 50
fund balance is -50
wife money in hand is 100

例3 ArrayList是线程不安全的

package thread;

import java.util.ArrayList;
import java.util.List;

public class UnsafeList {

	public static void main(String[] args) {
		List<String> list = new ArrayList<String>();
		for(int i =0; i<10000;i++){
//java8后写lamda表达式
/*			new Thread(()->{
				list.add(Thread.currentThread().getName());
			}).start();*/
                        
                        //多个线程名字添加到list的同一位置,覆盖掉了
			new Thread(new MyThread(list)).start();
		}
               //等10s所有子线程都结束再统计存进去的线程总数
               try {
			Thread.sleep(10000);
		  } catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		System.out.print(list.size());
	}

}
class MyThread implements Runnable{
	List<String> list = null;
	public MyThread(List<String> list){
		this.list = list;
	}
	public void run(){
		list.add(Thread.currentThread().getName());
	}
}

result: 9978

4.2 同步方法

  • 类似提出private关键字,可以保证数据只能在类内访问
    同步使用的synchronized也是关键字,利用锁和队列保证线程安全。其分为 synchronized方法和synchronized块
    同步方法: public synchronized void method(int args){}
  • 每个对象有一把锁,synchronized方法必须获取调用该方法的对象的锁才能执行,否则线程会阻塞
  • synchronized方法一旦执行就独占该锁,直到方法返回才释放锁,后面被阻塞的线程才能获得这个锁,继续执行
  • 缺点: 一个方法声明位synchronized大大影响效率

    例 1 买票方法加上synchronized
package thread;

public class UnsafeBuyTicket {
	public static void main(String argvs[]){
		BuyTicket buyTicket = new BuyTicket();
		new Thread(buyTicket,"xiaoming").start();
		new Thread(buyTicket,"xiaohong").start();
		new Thread(buyTicket,"xiaolan").start();
	}

}

class BuyTicket implements Runnable{
	private int ticketNums = 10;
	boolean flag = true;

	@Override
	public void run() {
		while(flag){
			buy();
		}
	}
	
	// lock this object
	private synchronized void buy(){
		
		if(ticketNums<=0){
			flag = false;
			return;
		}
		try {
			Thread.sleep(100);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		System.out.println(Thread.currentThread().getName()+" take the "+ ticketNums--+" ticket");
	}
	
}

4.3 同步代码块

  • 语法 synchronized(Obj){}
  • Obj 同步监视器
    • Obj可以是任何对象,但是推荐使用共享资源对象
    • 同步方法中无需指定同步监视器,其监视器是this或者class(反射中讲解)
      例 银行取钱,不是锁取钱的对象,而是锁账户
package thread;

public class UnsafeBank {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Account account = new Account("fund", 100);
		Drawing you = new Drawing(account, 50,"you");
		Drawing wife = new Drawing(account, 100,"wife");
		
		you.start();
		wife.start();
	}

}

class Account {
	String name;
	int money;

	public Account(String name, int money) {
		this.name = name;
		this.money = money;

	}

}

class Drawing extends Thread {
	Account account;
	int drawingMoney;

	public Drawing(Account account, int drawingMoney, String name) {
		super(name);
		this.account = account;
		this.drawingMoney = drawingMoney;
		
	}
	
	//It's no value to synchronized run method. it locks Drawing object, not account object
	public void run(){
                //锁的变化的量,针对增删改操作,不针对查
		synchronized(account){
			if (account.money-drawingMoney<0){
				System.out.println(Thread.currentThread().getName()+"not enough, can't withdraw");
				return;
			}
			try {
				//magnify issue
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				
				e.printStackTrace();
			}
			account.money -= drawingMoney;
			
			System.out.println(account.name+ " balance is "+ account.money);
			//Thread.currentThread().getName() ==  this.getName(), current class extend Thread
			System.out.println(this.getName() +" money in hand is "+ drawingMoney);
			
		}
		
	}
}

例3 list 锁

package thread;

import java.util.ArrayList;
import java.util.List;

public class UnsafeList {

	public static void main(String[] args) {
		List<String> list = new ArrayList<String>();
		for (int i = 0; i < 10000; i++) {
			/*
			 * new Thread(()->{ synchronized(list){
			 * list.add(Thread.currentThread().getName()); } }).start();
			 */

			new Thread(new MyThread(list)).start();

		}
		try {
			Thread.sleep(3000);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		System.out.print(list.size());
	}

}

class MyThread implements Runnable {
	List<String> list = null;

	public MyThread(List<String> list) {
		this.list = list;
	}

	public void run() {
                //add的步骤锁住
		synchronized (list) {
			list.add(Thread.currentThread().getName());
		}
	}
}

result: 10000

JUC

package thread;

import java.util.concurrent.CopyOnWriteArrayList;
public class TestJUC {

	public static void main(String[] args) {

		CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<String>();
		for(int i=0;i<10000;i++){
			new Thread(new MyThread(list))
		}
	}

}

class MyThread implements Runnable {
	List<String> list = null;

	public MyThread(List<String> list) {
		this.list = list;
	}

	public void run() {
		synchronized (list) {
			list.add(Thread.currentThread().getName());
		}
	}
}

扩展 java.util.concurrent并发包下的类使用
例 CopOnWriteArrayList

package thread;

        import java.util.concurrent.CopyOnWriteArrayList;
public class TestJUC {

    public static void main(String[] args) {

        CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<String>();
        for(int i=0;i<10000;i++){
            new Thread(()->{
                list.add(Thread.currentThread().getName());
            }).start();
        }
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(list.size());
    }

}

4.4 死锁

多个线程各自占有一些共享资源,并相互等待其他线程占有的资源才能运行结束,而导致停止执行的情形。
如 小孩A拥有车希望小孩B给自己船后才不要车,小孩B拥有船也希望小孩A先给他车才能让出船。
例 口红和镜子两个资源

package Thread;
//死锁:多个线程互相抱着对方需要的资源,然后形成僵持
public class DeadLock {
    public static void main(String[] args) {

        new Makeup(0,"xiaohong").start();
        new Makeup(1,"xiaolan").start();
    }
}

class Lipstick{

}
class Mirror{

}
class Makeup extends Thread{
    //static 共享的
    static Lipstick lipstick = new Lipstick();
    static Mirror mirror = new Mirror();

    int choice;
    String girlName;;

    Makeup(int choice,String girlName){
        this.choice = choice;
        this.girlName = girlName;

    }
    @Override
    public void run() {
        try {
            makeup();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    //化妆,互相持有对方的需要的资源的锁
    private void makeup() throws InterruptedException {
        if(choice == 0){
            synchronized (lipstick){
                System.out.println(this.girlName + " 获得口红的锁");
                    Thread.sleep(1000);
                synchronized (mirror){
                    System.out.println(this.girlName+" 获得镜子的锁");
                }
            }
        }else{
            synchronized (mirror){
                System.out.println(this.girlName + " 获得镜子的锁");
                Thread.sleep(1000);
                synchronized (lipstick){
                    System.out.println(this.girlName+ " 获得口哦那个的锁");
                }

            }

        }

    }
}

结果程序运行卡住,不能停止

  • 产生死锁的四个必要条件
  1. 互斥条件:一个资源每次只能被一个线程是使用
  2. 请求与保持条件: 一个线程因请求资源而阻塞时,对已获得的资源保持不妨
  3. 不剥夺条件: 进程已获得资源,在为使用完之前,不能强行剥夺
  4. 循环等待条件: 若干线程之间形成头尾相接的循环等待资源关系
    破坏任意一个或多个条件即可避免死锁

4.5 Lock (锁)

  • 从JDK5开始 java提供显示定义同步锁LOCK(synchronized)
  • java.util.concurrent.locks.Lock接口,每次只能有一个线程对Lock对象加锁,线程访问共享资源之前应先获得Lock对象
  • ReentrantLock类(可重入锁)实现了Lock, 常用来显示加锁,释放锁
    语法
    class A{
    private final ReentrantLock lock = new ReenTrantLock();
    public void m(){
    lock.lock(); //加锁
    try{
    //保证线程安全的代码;
    }
    finally{
    lock.unlock(); //解锁
    //如果同步代码有异常,要将unlock()写入finally语句块
    }
    例:
package Lock;

import java.util.concurrent.locks.ReentrantLock;

public class TestLock {

	public static void main(String[] args) {
		TestLock2 testLock2 = new TestLock2();
		new Thread(testLock2, "xiaoming").start();
		new Thread(testLock2, "xiaohong").start();
		new Thread(testLock2, "xiaolan").start();
	}
}

class TestLock2 implements Runnable {

	int ticketNums = 10;

	// define lock
	private final ReentrantLock lock = new ReentrantLock();

	public void run() {
		while (true) {

			try {
				Thread.sleep(100);
				lock.lock();// lock
				
				if (ticketNums > 0) {
					System.out.println(Thread.currentThread().getName()
							+ " get the " + ticketNums-- + " ticket");
				} else {
					break;
				}
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} finally {
				lock.unlock(); // unlock
			}
		}
	}
}

**synchronized 与 lock的对比

  • Lock是显示锁,手动开启锁关闭锁,synchronized是隐式锁,出了作用域自动释放
  • Lock只有代码块锁,synchronized 代码块—+方法锁
  • 使用Lock锁,JVM将花费较少的时间来调度线程,性能更好。并且具有更好的扩展性(提供更多子类)
  • 优先使用顺序
    *Lock->同步代码块(已经进入了方法体,分配了相应资源)->同步方法(在方法体之外)

4.6 线程协作

生产者消费者模式

4.6.1 线程通信

  • 应用场景:生产者和消费者问题
    • 对于仓库:假设仓库中只能放一件产品,生产者将生产出来的产品放入仓库,消费者将仓库中产品取走消费
    • 对于生产者:如果仓库中没有产品,则生产者将产品放入仓库,否则停止生产并等待,直到仓库中的产品被消费者取走为止
    • 对于消费者:如果仓库中放有产品,则消费者取走消费,否则停止消费并等待,直到仓库中再次放入产品为止。
  • 通信分析
    • 线程同步问题,生产者和消费者共享同一个资源,并且生产者和消费者之间相互依赖,互为条件
    • 生产者,没有生产产品之前,要通知消费者等待。生产产品之后,要通知消费者消费
    • 消费者,消费之后,要通知生产者消费结束,需要生产新的产品以供消费
    • 在生产者消费者问题中,仅有synchronized是不够的
      虽然可以实现同步,但是不能实现线程之间的消息传递(通信)
  • java提供的线程通信方法
    • wait() 线程一直等待,直到其他线程通知,与sleep抱着锁睡觉不同,会释放锁
    • wait(long timeout) 指定等待毫秒数
    • notify() 唤醒一个处于等待状态的线程
    • notifyAll() 唤醒 同一个对象上所有调用wait()方法的线程,优先级别高的线程优先调度
      注意:均是Object类的方法,都知道能在同步方法或者同步代码块中使用,否则会抛出异常illegalMonitorStateException
  • 解决方式1--管程法
    • 生产者:负责生产数据的模块(可能是方法,对象,线程,进程)
    • 消费者:负责处理数据的模块 (可能是方法,对象,线程,进程)
    • 缓冲区: 消费者不能直接使用生产者的数据,他们之间有个缓冲区
  • 解决方式2 --信号灯法
    设置标志位,true的时候进行,false等待

4.6.2 管程法

产品 只提供属性
生产者 线程里只循环生产数据 (缓冲区对象作为属性)
消费者 线程里只循环处理消费(缓冲区对象作为属性)
缓冲区 提供生产方法和消费方法,并设置判定条件指定wait和notifyAll 动作

package Thread;
//测试生产者消费者模型-> 利用缓冲区解决问题:管程法

//生产者,消费者,缓冲区
public class TestPC {
    public static void main(String[] args) {
        SynContainer container = new SynContainer();
        new Producer(container).start();
        new Consumer(container).start();
    }
}
//生产者 线程里只顾生产
class Producer extends Thread{
    SynContainer container;

    public Producer(SynContainer container) {
        this.container = container;
    }

    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println("生产了第"+ i +"只鸡");
            container.push(new Chicken(i));
        }
    }
}

//消费者 线程里只顾消费
class Consumer extends Thread{
    SynContainer container;

    public Consumer(SynContainer container) {
        this.container = container;
    }

    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println("消费了-->"+container.pop().id+"只鸡");
        }
    }
}

//产品 鸡
class Chicken{
    int id = 0;

    public Chicken(int id) {
        this.id = id;
    }
}

//缓冲区 提供生产和消费方法,并做出等待和通知判断
class SynContainer{
    //需要一个容器大小
    Chicken[] chickens = new Chicken[10];
    //容器计数器
    int count = 0;


    //生产者放入产品
    public synchronized void push(Chicken chicken){
        //如果容器满了,就需要等待消费者消费
        if (count == chickens.length){
            //通知消费者消费,生产者等待
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        //如果没有满,就需要丢入产品
        chickens[count++]= chicken;
        this.notifyAll();
    }

    //消费者消费产品
    public synchronized Chicken pop(){
        //判断能否消费
        if(count == 0){
            //等待生产者生产
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        //如果可以消费
        count--;
        Chicken chicken = chickens[count];

        //吃完了,通知生产者生产
        this.notifyAll();
        return chicken;
    }
}

4.6.3 信号灯法

生产者 只负责循环生产
消费者 只负责循环消费
产品 除了属性还提供生产和消费的方法,标志位flag只有true和flase两个值,取代了管程法中的缓冲区,但是只能生产一个产品
同一标志位值,生产者和消费者采取相反的两种操作

package Thread;

//测试生产者消费者问题2:信号灯法,标志位
public class TestPC2 {
    public static void main(String[] args) {
        TV tv = new TV();
        new Player(tv).start();
        new Watcher(tv).start();
    }

}

//生产者-->演员
class Player extends Thread{
    TV tv;

    public Player(TV tv) {
        this.tv = tv;
    }

    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            if(i%2==0){
                this.tv.play("综艺");
            }else{
                this.tv.play("电视剧");
            }
        }
    }
}
//消费者-->观众
class Watcher extends Thread{
    TV tv;

    public Watcher(TV tv) {
        this.tv = tv;
    }

    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            tv.watch();
        }
    }
}
// 产品-->节目
class TV{
    //演员拍戏的时候观众等待
    //观众观看的时候,这个敬业演员等待评价反馈再投入下次拍戏
    String program;

    //标志位是否该拍戏,true的时候演员拍戏,观众等待。false 演员录完了,观众观看。
    // 生产者和消费者标志位相同时,采取相反的行动
    boolean flag = true;
    
    //表演方法,提供给生成者
    public synchronized void play(String program){
        if(!flag){
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        //演员表演完,通知观众观看
        System.out.println("演员表演完了"+program);
        this.notifyAll();
        this.program = program;
        this.flag = !this.flag;

    }

    //观看,提供给消费者
    public synchronized void watch(){
        if(flag){
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        //观众看完戏,通知演员拍戏
        System.out.println("观看了"+ program);
        this.flag = !this.flag;
        this.notifyAll();
    }
}

4.7 线程池

  • 背景: 经常创建和销毁线程,消耗特别大的资源,对性能影响很大
  • 思路: 提前创建多个线程,放入线程池中,使用时直接获取,使用完放回池中。可以避免频繁创建销毁,实现重复利用。类似生活中的公共交通
  • 好处:
    • 提高响应速度(减少了创建新线程的时间)
    • 降低资源消耗 (重复利用线程池中的线程,不需要每次都创建)
    • 便于线程管理
      • corePoolSize:核心池的大小
      • maximumPoolSize: 最大线程数
      • keepAliveTime: 线程没有任务时最多保持多长时间后会终止
  • 使用线程池
    • jdk 1.5起提供ExecutorService 和 Executors
    • ExecutorService: 真正的线程池接口,常见子类 ThreadPoolExecutor
      • void execute(Runnable command):执行实现Runnable的线程,没有返回值
      • Future submit(Callabletask):执行实现Callable接口的线程任务,有返回值
      • void shutdown(): 关闭线程池
    • Executors:工具类,线程池的工厂类,用于创建并返回不同类型的线程池
      ExecutorService service = Executors.newFixedThreadPool(nThreads)创建线程池
      例 runable线程池
package Thread;

import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class MyThread implements Runnable{
    public static void main(String[] args) {
        ExecutorService service = Executors.newFixedThreadPool(10);
        MyThread mythread = new MyThread();
        service.execute(mythread);
        service.execute(mythread);
        service.execute(mythread);
    }
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println(Thread.currentThread().getName()+ " "+i);

        }
    }
}

Callable例子参前2.3

posted @ 2021-03-30 19:51  晒网达人  阅读(81)  评论(0)    收藏  举报