java多线程学习笔记-1

本文详细解析了Java线程的创建与使用,包括继承Thread类、实现Runnable接口、线程状态转换、同步与死锁避免、生产者消费者模型的两种实现方法。探讨了线程同步机制、线程间通信及如何利用容器队列和信号灯法解决线程间的协作问题。
  1. 创建线程
  • 继承Thread类
  • 实现Runnable接口
  • 实现Callable接口
  1. 推荐使用Runnable接口
  • 可以使用多个代理(静态代理模式)
public class Web12306 implements Runnable{
	//票数
	private int ticketNums = 99;
	
	@Override
	public void run() {
		while(true) {
			if(ticketNums<0) {
				break;
			}
			try {
				Thread.sleep(200);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			System.out.println(Thread.currentThread().getName()+"-->"+ticketNums--);
		}
	}
	public static void main(String[] args) {
		//一份资源
		Web12306 web =new Web12306();
		System.out.println(Thread.currentThread().getName());
		//多个代理
		new Thread(web,"码畜").start();
		new Thread(web,"码农").start();
		new Thread(web,"码蟥").start();;
	}
}

  1. Lambda表达式
  • 函数化接口
  • lambda推导必须存在类型
  1. 线程状态

  • 进入就绪状态的原因
1) start()
2) 阻塞时间解除
3) Thread.yield(),当前线程主动让出CPU(从运行态回到就绪态)——礼让。
4) jvm从本线程切换到其他线程
  • 进入阻塞状态的原因
1) Thread.sleep(),当前线程不交出cpu等待。
2) wait(),交出cpu等待。
3) 对象方法join()。等待插队进来的线程执行完——写在哪个线程中,则哪个线程被阻塞。而调用join()的对象是插队的线程。
4) 等待IO操作

注意wait()的使用:

  1. 必须在synchronized块中调用
  2. 调用wait()后线程会释放持有的对象锁并进入阻塞状态
  • join()示意
  • join()的代码
public class Thread_b {

	public static void main(String[] args) throws InterruptedException {
		Thread thread_a = new Thread(() -> {
			for (int i = 0; i < 100; i++) {
				System.out.println("thread_a要超车了..." + i);
			}
		});

		thread_a.start();

		for (int i = 0; i < 100; i++) {
			if (i == 20) {
				System.out.println("thread_a插队!!");
				thread_a.join(); // 插队,main主线程(thread_b)被阻塞
			}
			System.out.println("thread_b正常行驶..." + i);
		}
	}
}

join(milliseconds),参数为超时的时间,超过时间还没完,则不等了,让CPU自行调度。
join()可以实现线程的同步。

  1. 线程的状态
Thread t=new Thread(()->{
	System.out.println("...");
});
State state=t.getState();
...
//指定线程状态
Thread.State.TERMINATED
...
//获取活动线程数
Thread.activeCount()
  1. 守护线程
    默认都为用户线程。
Thread t=new Thread(god);
t.setDaemon(true);
  1. synchronized和同步对象
  • synchronized方法的同步对象是this
  • synchronized块可以自定义同步对象(减小锁定的粒度)
synchronized (object){ //这里的同步对象一般是要修改的对象
	...
}

同步对象必须是不变的对象——对象不变(引用的地址不变),只是属性变。对于变化的对象(引用不同的对象地址)是锁不住的。
需要尽可能锁定合理的范围(不是指代码,而是指数据的完整性)。

  • double checking检验数据的临界值
...
	public  void test5() {
		if(ticketNums<=0) {//考虑的是没有票的情况
			flag = false;
			return ;
		}
		synchronized(this) {			
			if(ticketNums<=0) {//考虑最后的1张票
				flag = false;
				return ;
			}
			//模拟延时
			try {
				Thread.sleep(200);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			System.out.println(Thread.currentThread().getName()+"-->"+ticketNums--);
		}
	}
...
  1. 死锁
    不要在一个同步块中同时持有多个对象的锁。

  2. 生产者消费者模型——线程同步(依序执行)

  • 容器队列作缓冲区——管程法
/**
 * 协作模型:生产者消费者实现方式一:管程法 借助缓冲区
 * 
 * @author beeworkshop
 *
 */
public class CoTest01 {
	public static void main(String[] args) {
		SynContainer container = new SynContainer();
		new Productor(container).start();
		new Consumer(container).start();
	}
}

//生产者
class Productor extends Thread {
	SynContainer container;

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

	public void run() {
		// 生产
		for (int i = 0; i < 100; i++) {
			System.out.println("生产-->" + i + "个馒头");
			container.push(new Steamedbun(i));
		}
	}
}

//消费者
class Consumer extends Thread {
	SynContainer container;

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

	public void run() {
		// 消费
		for (int i = 0; i < 100; i++) {
			System.out.println("消费-->" + container.pop().id + "个馒头");
		}
	}
}

//缓冲区
class SynContainer {
	Steamedbun[] buns = new Steamedbun[10]; // 存储容器
	int count = 0; // 计数器
	// 存储 生产

	public synchronized void push(Steamedbun bun) {
		// 何时能生产 容器存在空间
		// 不能生产 只有等待
		if (count == buns.length) {
			try {
				this.wait(); // 线程阻塞 消费者通知生产解除
			} catch (InterruptedException e) {
			}
		}
		// 存在空间 可以生产
		buns[count] = bun;
		count++;
		// 存在数据了,可以通知消费了
		this.notifyAll();
	}

	// 获取 消费
	public synchronized Steamedbun pop() {
		// 何时消费 容器中是否存在数据
		// 没有数据 只有等待
		if (count == 0) {
			try {
				this.wait(); // 线程阻塞 生产者通知消费解除
			} catch (InterruptedException e) {
			}
		}
		// 存在数据可以消费
		count--;
		Steamedbun bun = buns[count];
		this.notifyAll(); // 存在空间了,可以唤醒对方生产了
		return bun;
	}
}

//馒头
class Steamedbun {
	int id;

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

}

注意:wait()和notifyAll()方法都是Object类提供的。notifyAll()的使用需要:

  1. 必须在synchronized块中调用
  2. 唤醒等待队列中的所有线程(notify()唤醒一个)
  • 信号灯
/**
 * 协作模型:生产者消费者实现方式二:信号灯法 借助标志位
 * 
 * @author beeworkshop
 *
 */
public class CoTest02 {
	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;
	}

	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;
	}

	public void run() {
		for (int i = 0; i < 20; i++) {
			tv.watch();
		}
	}
}

//同一个资源 电视
class Tv {
	String voice;
	// 信号灯
	// T 表示演员表演 观众等待
	// F 表示观众观看 演员等待
	boolean flag = true;

	// 表演
	public synchronized void play(String voice) {
		// 演员等待
		if (!flag) {
			try {
				this.wait();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		// 表演
		System.out.println("表演了:" + voice);
		this.voice = voice;
		// 唤醒
		this.notifyAll();
		// 切换标志
		this.flag = !this.flag;
	}

	// 观看
	public synchronized void watch() {
		// 观众等待
		if (flag) {
			try {
				this.wait();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		// 观看
		System.out.println("听到了:" + voice);
		// 唤醒
		this.notifyAll();
		// 切换标志
		this.flag = !this.flag;
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值