多线程
Thread
现在的Thread中的run方法,已经被实现了,所以已经不需要实现了
操作
-
继承 extends Thread方法
-
重写run方法
-
start()
案例
public class ThreadTest extends Thread{
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("多线程" + i);
}
}
public static void main(String[] args) {
ThreadTest threadTest = new ThreadTest();
threadTest.start();
for (int i = 0; i < 100; i++) {
System.out.println("主线程" + i);
}
}
}
- 存在两个线程,一个是主线程,一个是其他的线程
- 当start时,并不是线程启动了,而是线程进入了就绪状态,当所有线程都进入了就绪状态,由CPU来分配资源
- 这个输出是,上面线程与下面线程同时进行的
Runnable
其实Thread也是实现了Runnable这个接口,同时这个接口中只有一个方法,,就是run方法
实现了Runnable接口只是为了实现这个方法,并创建一个实例,而不是创建一个线程,还需要创建一个线程,多个线程可以同时调用这个实例
操作
- 实现Runnable接口
- 重写run方法
- 创建Thread时将 实现Runnable接口类放进去
- start
案例
public class RunableTest implements Runnable{
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("多线程"+ i);
}
}
}
public class Main {
public static void main(String[] args) {
Runnable runnable = new RunableTest();
//Thread thread = new Thread(runnable);
//thread.start();
//与上面代码等效
new Thread(runnable).start();
for (int i = 0; i < 100; i++) {
System.out.println("主线程"+i);
}
}
}
- 得到的结果跟上述的是一样的
总结
注意:
- 线程start(),并不是启动了,而是将加入到就绪队列,是与主线程一起的呢,再有cpu分配资源
- 实现多线程的方式存在两个
extends Thread
类 ,实现Runnable接口
,同时重写run方法 - Thread类也是实现了 Runnable接口
Lambda表达式
概念
函数式接口:
一个接口,里面只有一个方法
案例
interface Study {
public void studyChinese();
}
class Student implements Study{
@Override
public void studyChinese() {
System.out.println("学习中文");
}
}
public class Main {
public static void main(String[] args) {
Study study = new Student();
study.studyChinese();
}
}
- 这里存在一个接口里面只有一个接口,里面只有 一个方法
- 这是一个常规的实现
进阶------->静态内部类
public class Main {
static class Student implements Study{
@Override
public void studyChinese() {
System.out.println("学习中文");
}
}
public static void main(String[] args) {
Study study = new Student();
study.studyChinese();
}
}
进阶----->局部内部类
public class Main {
public static void main(String[] args) {
class Student implements Study{
@Override
public void studyChinese() {
System.out.println("学习中文");
}
}
Study study = new Student();
study.studyChinese();
}
}
进阶---->匿名内部类
这个就是实例化接口
public class Main {
public static void main(String[] args) {
Study study = new Study(){
@Override
public void studyChinese() {
System.out.println("学习中文");
}
};
study.studyChinese();
}
}
进阶----->lambda表达式
public class Main {
public static void main(String[] args) {
Study study = ()->{
System.out.println("学习中文");
};
study.studyChinese();
}
}
- 删除了函数的实例化,里面的函数名字都删了 -> 增加了箭头
线程的操作
线程的停止【stop】
- Thread原本使用的是Thread.stop(),但是却并不被采用了
- 采用定义一个标志位来使得线程停止
public class ThreadStop implements Runnable{
private boolean flag = true;
@Override
public void run() {
int i = 0;
while(flag){
System.out.println("线程停止" + Thread.currentThread().getName() + i++);
}
}
public void stop(){
this.flag = false;
}
public static void main(String[] args) {
ThreadStop threadStop = new ThreadStop();
new Thread(threadStop).start();
for (int i = 0; i < 1000; i++) {
System.out.println(i);
if(i == 900){
threadStop.stop();
}
}
}
}
- 主线程到了900的时候,调用停止方法,将标志位flag修改位false,使得线程停止
线程睡觉【sleep】
- 休眠一定的时间
public class ThreadSleep implements Runnable{
@Override
public void run() {
for (int i = 0; i < 10; i++) {
if(Thread.currentThread().getName().equals("小明") && i == 8){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
System.out.println(Thread.currentThread().getName() + "票数"+i);
}
}
public static void main(String[] args) {
ThreadSleep threadSleep = new ThreadSleep();
Thread thread1 = new Thread(threadSleep,"小明");
Thread thread2 = new Thread(threadSleep,"丽丽");
Thread thread3 = new Thread(threadSleep,"图图");
thread1.start();
thread2.start();
thread3.start();
}
}
线程的礼让【yield】
-
让线程重新处于就绪状态,大家齐头并进的那种
-
但是礼让后,CPU去调度谁,还是得CPU说的算
-
所以说礼让不一定成功
public class TreadYield implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName() +"开始");
if(Thread.currentThread().getName().equals("pp")){
Thread.yield();
}
System.out.println(Thread.currentThread().getName() +"结束");
}
public static void main(String[] args) {
TreadYield treadYield = new TreadYield();
new Thread(treadYield,"pp").start();
new Thread(treadYield,"tutu").start();
new Thread(treadYield,"qq").start();
new Thread(treadYield,"yuyu").start();
}
}
线程的强制执行【join】
- 插队的方式
- 就是当一个线程执行到一半,你可以插队,而且是等插队的人执行完了,后面的人才能执行
public class ThreadJoin implements Runnable{
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("插队" + i);
}
}
public static void main(String[] args) throws InterruptedException {
ThreadJoin threadJoin = new ThreadJoin();
Thread thread = new Thread(threadJoin);
thread.start();
for (int i = 0; i < 100; i++) {
System.out.println("我是爹" + i);
if(i == 10){
thread.join();
}
}
}
}
线程的状态【status】
- NEW
- RUNNABLE
- BOLCOKED
- WAITING
- TIMED_WAITING
- TERMINATED
public class ThreadState implements Runnable{
@Override
public void run() {
//try {
// Thread.sleep(1000);
//} catch (InterruptedException e) {
// throw new RuntimeException(e);
//}
for (int i = 0; i < 100; i++) {
System.out.println(i);
}
//Thread.yield();
}
public static void main(String[] args) throws InterruptedException {
ThreadState threadState = new ThreadState();
Thread thread = new Thread(threadState,"线程1");
System.out.println(thread.getName() + ":" +thread.getState());
thread.start();
System.out.println(thread.getName() + ":" +thread.getState());
Thread thread2 = new Thread(threadState,"线程2");
//可以看出是共享一个线程的
System.out.println(thread2.getName() + ":" +thread.getState());
thread2.start();
System.out.println(thread2.getName() + ":" +thread.getState());
while(thread.getState() != Thread.State.TERMINATED){
System.out.println(thread.getName()+thread.getState());
//Thread.sleep(100);
}
thread2.join();
}
}
- 我们可以看出,当多个线程操作一个实例的时候,第一个线程会有新生状态,但是其他的线程就没有新生
线程的优先级【priority】
- 可以设置线程优先级,谁先进行
- 但是设置了不一定成功
public class ThreadPriority implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + ":" + Thread.currentThread().getPriority());
}
}
class Main{
public static void main(String[] args) {
ThreadPriority threadPriority = new ThreadPriority();
Thread thread = new Thread(threadPriority,"first");
Thread thread1 = new Thread(threadPriority,"second");
Thread thread2 = new Thread(threadPriority,"third");
Thread thread3 = new Thread(threadPriority,"forth");
thread.setPriority(1);
thread.start();
thread1.setPriority(4);
thread1.start();
thread2.setPriority(9);
thread2.start();
thread3.setPriority(6);
thread3.start();
}
}
线程的守护【Deamon】
- 守护进程会一直伴随着需要守护的进程,直到进程的死亡
public class ThreadDeamon {
public static void main(String[] args) {
YouYongci youYongci = new YouYongci();
ZhouXiaoqi zhouXiaoqi = new ZhouXiaoqi();
Thread threadYou = new Thread(youYongci);
Thread threadZhou = new Thread(zhouXiaoqi);
//设置为守护进程
threadZhou.setDaemon(true);
threadYou.start();
threadZhou.start();
}
}
/**
* 尤永慈
*/
class YouYongci implements Runnable{
@Override
public void run() {
int days = 365 * 3;
for (int i = 0; i < days; i++) {
System.out.println("今天是第"+ i +"天,是开心的一天");
}
}
}
/**
* 周萧齐
*/
class ZhouXiaoqi implements Runnable{
@Override
public void run() {
while (true) {
System.out.println("尤永慈 开心我就开心");
}
}
}
线程同步
定义
线程同步:
1、即当有一个线程在对内存进行操作时,
2、其他线程都不可以对这个内存地址进行操作,
3、直到该线程完成操作 其他线程才能对该内存地址进行操作,
4、而其他线程又处于等待状态
- java允许多个线程操作同一个对象【资源变量】
- 当多个线程同时操作同一个数据时,就会造成数据的不一致性
- 12306买票,只剩下一张票,但是却被很多人看到,如果线程不同步,那么很多人都会买到这张票,而这个数据将会变成负数
多线程操作一个对象,造成数据不一致的案例
案例一 【操作的是一个数据】
public class ThreadSync {
public static void main(String[] args) {
BuyTickets buyTickets = new BuyTickets();
Thread thread1 = new Thread(buyTickets,"皮夹子来买票");
Thread thread2 = new Thread(buyTickets,"黄牛买票");
Thread thread3 = new Thread(buyTickets,"大家都来买票");
thread1.start();
thread2.start();
thread3.start();
}
}
class BuyTickets implements Runnable{
int number = 10;
boolean flag = true;
@Override
public void run() {
while (flag) {
System.out.println(Thread.currentThread().getName() + "买到了第" +number + "张票");
number--;
try {
Thread.sleep(100);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
stop();
}
}
public void stop(){
if(number <= 0){
this.flag = false;
}
}
}
- 创建了一个实现runnable接口的实例
- 多个线程操作一个实例,一个实例中的同一个数据,会造成数据的不一致性
案例二【操作一个对象】
public class UnsafeBank {
public static void main(String[] args) {
GetMoney getMoney = new GetMoney();
getMoney.setGetMoney(0);
new Thread(getMoney,"皮夹子").start();
new Thread(getMoney,"皮夹子女朋友").start();
}
}
class GetMoney implements Runnable{
private Account account = new Account(100);
private int getMoney;
public void setGetMoney(int getMoney) {
this.getMoney = getMoney;
}
@Override
public void run() {
if(account.getAllMoney() < getMoney){
System.out.println(Thread.currentThread().getName() + "取钱不够了");
return;
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
int endMoney = account.getAllMoney() - getMoney;
account.setAllMoney(endMoney);
account.setGetMoney(getMoney);
account.setEndMoney(endMoney);
System.out.println(Thread.currentThread().getName() + "取了:" + account.getGetMoney());
System.out.println(Thread.currentThread().getName() + "剩下:" + account.getEndMoney());
}
}
class Account{
private int allMoney;
private int getMoney;
private int endMoney;
public Account(int allMoney) {
this.allMoney = allMoney;
}
public int getAllMoney() {
return allMoney;
}
public void setAllMoney(int allMoney) {
this.allMoney = allMoney;
}
public int getGetMoney() {
return getMoney;
}
public void setGetMoney(int getMoney) {
this.getMoney = getMoney;
}
public int getEndMoney() {
return endMoney;
}
public void setEndMoney(int endMoney) {
this.endMoney = endMoney;
}
}
- 这个是创建一个账户的对象
- 皮夹子和皮夹子的女朋友同时对其中的钱进行修改
- 造成了Account对象的不一致性
案例三【多线程下的ArrayList】
public class UnsafeList {
public static void main(String[] args) {
List<Integer> list = new ArrayList<>();
for (int i = 0; i < 10000 ;i++) {
int finalI = i;
Runnable runnable = new Runnable() {
@Override
public void run() {
list.add(finalI);
}
};
new Thread(runnable).start();
}
System.out.println(list.size());
}
}
- ArrayList是不安全,容易造成数据的不一致性,也就是使得多个数据被存放到同一个位置了
线程同步的方式
同步方法
public synchronized void run(){
}
-
由于java的每个对象都有一个内置锁,当用此关键字修饰方法时,内置锁会保护整个方法。
-
在调用该方法前,需要获得内置锁,否则就处于阻塞状态
同步代码块
synchronized(object){
}
- object是一个对象类型
- 当需要对某个对象进行操作时,就可以使用先将对象锁住,等操作完成后,在开锁
使用特殊域变量(volatile)
- volatile为关键字提供一个免锁机制,这样就可以直接访问
- volatile会告诉虚拟机该数据可能会被其他线程更新
- 使用后,每次线程操作该数据,都需要重新计算,而不是使用寄存器重点值
- 不可以修饰常量(final类型)
private volatile int account = 100
重入锁(ReentrantLock)
ReentrantLock lock = new ReetrantLock();
try{
lock.lock();
}catch {
}finally{
lock.unlock();
}
- 创建一个对象
- 一般lock.lock()方法放在try里
- lock.unlock()放在finally里
生产者消费者问题
- 也就是解决通信问题,当需要当生产者线程生产了商品,如何让消费者知道这个问题,然后去消费他
管程法
- 定义一个缓存区域
- 当缓存区满了,告诉消费者消费
- 当缓存区空了,告诉生产者生产
- 问题来了???如何告知
- 使用wati() 和 notify方法
- 当缓存区满了,可以使用wati(),告诉生产者等待生产
- 同时通知消费者,不要消费,notify()
public class ThreadContainer {
public static void main(String[] args) {
Container container = new Container();
Producer producer = new Producer(container);
Consumer consumer = new Consumer(container);
new Thread(producer).start();
new Thread(consumer).start();
}
}
class Chicken {
}
class Producer implements Runnable{
private Container container;
public Producer(Container container) {
this.container = container;
}
@Override
public void run() {
productChicken(container);
}
/**
* 生产鸡
* 将鸡放在缓冲区
*/
int num = 0;
public void productChicken(Container container){
while (num <= 100) {
Chicken chicken = new Chicken();
num++;
System.out.println("生产了" +num +"鸡");
container.push(chicken);
}
}
}
class Consumer implements Runnable{
private Container container;
public Consumer(Container container) {
this.container = container;
}
@Override
public void run() {
consumeChicken(container);
}
/**
* 消费鸡
*
*/
int num = 0;
public synchronized void consumeChicken(Container container){
while (num <= 100) {
num ++;
System.out.println("消费了" +num +"鸡");
container.pop();
}
}
}
class Container{
//定义chicken数组,作为缓冲区
Chicken[] chickens = new Chicken[10];
int count = 0;
public synchronized void push(Chicken chicken){
//判断是否满了
if(count == chickens.length){
try {
this.wait();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
chickens[count++] = chicken;
//通知消费者消费
this.notifyAll();
}
public synchronized void pop(){
//如果没有了,通知生产者生产
if(count == 0){
try {
this.wait();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
count--;
this.notifyAll();
}
}
- 生产者Producer
- 生产鸡,并将鸡放到容器里Container.push
- 消费者Consumer
- 消费鸡,并将鸡从容器里移动出来Container.pop
- 缓存区Container
- 定义缓存区,缓存大小
- 定义入缓存方法和出缓存方法
信号灯法
- 就是在容器内定义个变量,告诉别人,下一步该干啥了