多线程学习笔记
一、线程 | 进程 | 多线程
线程:操作系统能够进行运算调度的最小单位、线程是独立调度和分派的基本单位、同一进程中的多条线程将共享该进程中的全部系统资源。一个进程可以有很多线程,每条线程并行执行不同的任务
进程:系统进行资源分配和调度的基本单位(进程是执行程序的一次过程。程序:是指令和数据的有序集合,静态)
多线程:从软件或者硬件上实现多个线程并发执行的技术。(并发:时间片轮回,每隔一段时间处理不同的程序,时间短。并行:同时运行)
二、创建方式
1、继承Thread类
- 继承Thread类
- 重写run方法
- 开启线程
package com.zheng.demo1;
public class MyThread1 extends Thread {
public static void main(String[] args) {
MyThread1 myThread1 = new MyThread1();
myThread1.start();
for (int i = 0; i < 100; i++) {
System.out.println("======我是主线程====" + i);
}
}
public void run() {
for (int i = 0; i < 50; i++) {
System.out.println("我是子线程====" + i);
}
}
}
测试结果:
线程开启后,主线程和子线程交替执行
2、实现runnable接口
- 实现Runnable接口
- 重写run()方法,编写线程执行体
- 创建线程对象,开启线程
package com.zheng.demo1;
public class MyRunnable implements Runnable {
public static void main(String[] args) {
MyRunnable myRunnable = new MyRunnable();
// Thread thread = new Thread(myRunnable);
// thread.start();
new Thread(myRunnable).start();
for (int i = 0; i < 200; i++) {
System.out.println("-------我是主线程------" + i);
}
}
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("我是子线程------" + i);
}
}
}
测试结果
当创建多个对象,都开启线程。每一个对象都会独享一份重写run()里的资源
package com.zheng.demo1;
public class MyRunnable implements Runnable {
public static void main(String[] args) {
MyRunnable myRunnable = new MyRunnable();
MyRunnable myRunnable1 = new MyRunnable();
MyRunnable myRunnable2 = new MyRunnable();
MyRunnable myRunnable3 = new MyRunnable();
new Thread(myRunnable).start();
new Thread(myRunnable1).start();
new Thread(myRunnable2).start();
new Thread(myRunnable3).start();
for (int i = 0; i < 200; i++) {
System.out.println("-------我是主线程------" + i);
}
}
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("我是子线程------" + i);
}
}
}
测试结果
不安全购票行为
package com.zheng.demo1;
public class MyRunnable2 implements Runnable {
private int ticket = 9;
public static void main(String[] args) {
MyRunnable2 station = new MyRunnable2();
new Thread(station, "小明").start();
new Thread(station, "老师").start();
new Thread(station, "商贩").start();
}
public void run() {
while (true) {
if (ticket <= 0) {
break;//跳出循环
}
try {
Thread.sleep(1000);//模拟延时
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "买了第" + ticket-- + "张票");
}
}
}
测试结果
龟兔赛跑问题
package com.zheng.demo1;
public class Game implements Runnable {
//只能有一个胜利者
private String winner;
public void run() {
for (int i = 0; i <= 100; i++) {
boolean flag = gameOver(i);
if (Thread.currentThread().getName() == "兔子" && i % 10 == 0) {
try {
Thread.sleep(5);//模拟兔子休眠
} catch (InterruptedException e) {
e.printStackTrace();
}
}
if (!flag) {
System.out.println(Thread.currentThread().getName() + "跑了" + i + "米");
} else {
break;
}
}
}
//判断比赛是否结束
public boolean gameOver(int step) {
if (winner != null) {
return true;
} else {
//继续跑
if (step >= 100) {
winner = Thread.currentThread().getName();//得到当前线程对象名
System.out.println("胜利者是:" + winner);
return true;
}
}
return false;
}
public static void main(String[] args) {
Game animal = new Game();
new Thread(animal, "兔子").start();
new Thread(animal, "乌龟").start();
}
}
测试结果
3、Callable接口
4、lambda
三、静态代理
真实对象和代理对象都要实现同一个接口,代理对象要代理真实对象,真实对象关注自己主要的事情
package com.zheng.demo2;
public interface Wedding {
public void wedding();
}
//真实角色
class You implements Wedding {
public You() {
}
public void wedding() {
System.out.println("结婚了好开心");
}
}
//代理角色
class WeddingCompang implements Wedding {
private Wedding target;
public WeddingCompang(Wedding target) {
this.target = target;//要结婚的对象
}
public void wedding() {
before();
target.wedding();
after();
}
public void before() {
System.out.println("进行婚礼布置");
}
public void after() {
System.out.println("进行婚后的处理");
}
}
package com.zheng.demo2;
public class Mytest {
public static void main(String[] args) {
Wedding you = new You();
WeddingCompang wc = new WeddingCompang(you);
wc.wedding();
}
}
测试结果
四、Lamda表达式
1、函数式接口(Function interface)
- 定义:只包含一个抽象方法
public interface Runnable{
public abstract void run();
}
【1】
package com.zheng.demo3;
//定义函数式接口
interface A {
public void say();
}
class B implements A {
public void say() {
System.out.println("我是函数式接口");
}
}
public class Lamda {
public static void main(String[] args) {
B b = new B();
b.say();
}
}
静态内部类:静态内部类定义在类中,任何方法外,用static定义。
package com.zheng.demo3;
//定义函数式接口
interface A {
public void say();
}
public class Lamda {
//静态内部类
static class B1 implements A {
public void say() {
System.out.println("我是函数式接口1");
}
}
public static void main(String[] args) {
B1 b = new B1();
b.say();
}
}
局部内部类:写在方法里边的类
package com.zheng.demo3;
//定义函数式接口
interface A {
public void say();
}
public class Lamda {
public static void main(String[] args) {
//局部内部类
class B2 implements A {
public void say() {
System.out.println("我是函数式接口2");
}
}
B2 b = new B2();
b.say();
}
}
匿名内部类:匿名类是不能有名字的类,它们不能被引用,只能在创建时用New语句来声明它们
package com.zheng.demo3;
//定义函数式接口
interface A {
public void say();
}
public class Lamda {
public static void main(String[] args) {
//匿名内部类,没有类的名称,必须借助父类或者接口
A a=new A() {
public void say() {
System.out.println("我是匿名内部类");
}
};
a.say();
}
}
lamda表达式
package com.zheng.demo3;
//定义函数式接口
interface A {
public void say();
}
public class Lamda {
public static void main(String[] args) {
//使用lamda表达式
A a1 = () -> {
System.out.println("我是lamda表达式");
};
a1.say();
}
}
五、线程状态
1、新建状态
- 2、就绪状态
- 3、运行状态
- 4、阻塞状态
- 5、死亡状态
线程停止:(使用标志位)
package com.zheng.demo4;
public class ThreadStop implements Runnable {
boolean flag = true;
public void run() {
int i = 0;
while (flag) {
System.out.println("我是子线程======" + 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 < 50; i++) {
System.out.println("======我是主线程=====" + i);
if (i == 25) {
threadStop.stop();
System.out.println("子线程停止了");
}
}
}
}
测试结果
1、线程休眠:模拟网络延时,放大问题发生的可能性,找到问题
try {
Thread.sleep(1000);//模拟延时
} catch (InterruptedException e) {
e.printStackTrace();
}
打印当前的年月日时间
package com.zheng.demo4;
import java.text.SimpleDateFormat;
import java.util.Date;
public class DateSleep {
public static void main(String[] args) {
//获取系统当前时间
Date date = new Date(System.currentTimeMillis());
while (true){
try {
Thread.sleep(1000);//休眠一秒钟
String date1=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(date);
System.out.println(date1);
date = new Date(System.currentTimeMillis());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
2、线程礼让,让当前执行的线程先暂停转化为就绪状态
package com.zheng.demo4;
public class MyYield {
public static void main(String[] args) {
YieldMy my = new YieldMy();
new Thread(my,"小明").start();
new Thread(my,"老师").start();
}
}
class YieldMy implements Runnable{
public void run() {
System.out.println(Thread.currentThread().getName()+"开始执行");
Thread.yield();//线程礼让
System.out.println(Thread.currentThread().getName()+"结束执行");
}
}
3、线程的加入(join):加入的线程执行结束后,在执行其他的线程,其他线程阻塞
package com.zheng.demo4;
public class TestJoin implements Runnable {
@Override
public void run() {
for (int i = 0; i < 50; i++) {
System.out.println("我是加入的线程" + i);
}
}
public static void main(String[] args) throws InterruptedException {
TestJoin testJoin = new TestJoin();
Thread thread = new Thread(testJoin);
thread.start();//开启线程
for (int i = 0; i < 20; i++) {
System.out.println("我是主线程" + i);
if (i == 15) {
thread.join();
}
}
}
}
4、线程优先级
package com.zheng.demo4;
public class TestPriority {
public static void main(String[] args) {
Priority priority = new Priority();
Thread t1 = new Thread(priority);
Thread t2 = new Thread(priority);
Thread t3 = new Thread(priority);
Thread t4 = new Thread(priority);
Thread t6 = new Thread(priority);
Thread t7 = new Thread(priority);
Thread t8 = new Thread(priority);
Thread t9 = new Thread(priority);
Thread t10 = new Thread(priority);
System.out.println("当前线程名称:" + Thread.currentThread().getName() + " 优先级:" + Thread.currentThread().getPriority());
//设置线程优先级
t1.setPriority(Thread.MIN_PRIORITY);
t10.setPriority(Thread.MAX_PRIORITY);
t2.setPriority(2);
t3.setPriority(3);
t4.setPriority(4);
t6.setPriority(6);
t7.setPriority(7);
t8.setPriority(8);
t9.setPriority(9);
//开启线程
t1.start();
t2.start();
t3.start();
t4.start();
t6.start();
t7.start();
t8.start();
t9.start();
t10.start();
}
}
class Priority implements Runnable {
public void run() {
//打印当前线程名称+优先级
System.out.println("当前线程名称:" + Thread.currentThread().getName() + " 优先级:" + Thread.currentThread().getPriority());
}
}
测试结果
优先级高代表CPU调度到的概率大。优先级低代表CPU调度到的概率低
5、守护线程
用户线程和守护线程,虚拟机必须保证用户线程执行完毕。不需要等待守护线程执行结束
package com.zheng.demo4;
public class Test {
public static void main(String[] args) {
People people = new People();
Animal animal = new Animal();
Thread thread = new Thread(animal);
thread.setDaemon(true);//默认是false为用户线程,true改为守护线程
thread.start();
new Thread(people).start();
}
}
class People implements Runnable {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("开心的生活");
}
}
}
class Animal implements Runnable {
@Override
public void run() {
while (true) {
System.out.println("动物守护着你");
}
}
}
测试结果
守护线程不会一直执行下去,当用户线程结束后,虚拟机关闭
六、线程同步不安全实例
package com.zheng.demo5;
public class BuyTicket {
public static void main(String[] args) {
Ticket ticket = new Ticket();
new Thread(ticket, "小明").start();
new Thread(ticket, "老师").start();
new Thread(ticket, "商贩").start();
}
}
class Ticket implements Runnable {
private int ticket = 15; //票数
boolean flag = true;//设置标志位
public void buyTicket() {
if (ticket > 0) {//有票可以继续买
System.out.println(Thread.currentThread().getName() + "买了第" + ticket-- + "张票");
} else {
//无票
flag = false;//改变标志位状态,结束线程
}
}
@Override
public void run() {
while (flag) {
try {
Thread.sleep(50);//模拟买票延时
} catch (InterruptedException e) {
e.printStackTrace();
}
buyTicket();//调用买票的方法
}
}
}
2、存钱取钱问题
package com.zheng.demo5;
public class Bank {
public static void main(String[] args) {
Account account = new Account(100, "买房");
withdrawMoney thread = new withdrawMoney(account,50,"小明");
withdrawMoney thread1 = new withdrawMoney(account,100,"小黑");
thread.start();
thread1.start();
}
}
class Account{
int money;
String name;
public Account(int money,String name){
this.money=money;
this.name=name;
}
}
class withdrawMoney extends Thread{
Account bank;
int nowMoney;
//取款金额
int takeMoney;
withdrawMoney(Account bank,int takeMoney,String name){
super(name);
this.bank=bank;
this.takeMoney=takeMoney;
}
@Override
public void run() {
//判断是否能取钱
if(bank.money-takeMoney<0){
System.out.println("余额不够");
return;
}else {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
bank.money=bank.money-takeMoney;
nowMoney=nowMoney+takeMoney;
System.out.println("账户还剩下"+bank.money);
System.out.println(Thread.currentThread().getName()+"手里的钱"+nowMoney);
}
}
}
同步锁
买票
package com.zheng.demo5;
public class BuyTicket {
public static void main(String[] args) {
Ticket ticket = new Ticket();
new Thread(ticket, "小明").start();
new Thread(ticket, "老师").start();
new Thread(ticket, "商贩").start();
}
}
class Ticket implements Runnable {
private int ticket = 15; //票数
boolean flag = true;//设置标志位
//同步方法
public synchronized void buyTicket() {
if (ticket > 0) {//有票可以继续买
System.out.println(Thread.currentThread().getName() + "买了第" + ticket-- + "张票");
} else {
//无票
flag = false;//改变标志位状态,结束线程
}
}
@Override
public void run() {
while (flag) {
try {
Thread.sleep(50);//模拟买票延时
} catch (InterruptedException e) {
e.printStackTrace();
}
buyTicket();//调用买票的方法
}
}
}
取钱
同步块:synchronized(obj){}
obj:共享资源作为同步监视器
- 第一个线程访问,锁定,访问结束,第二个线程访问,锁定。。。
package com.zheng.demo5;
public class Bank {
public static void main(String[] args) {
Account account = new Account(100, "买房");
withdrawMoney thread = new withdrawMoney(account,50,"小明");
withdrawMoney thread1 = new withdrawMoney(account,100,"小黑");
thread.start();
thread1.start();
}
}
class Account{
int money;
String name;
public Account(int money,String name){
this.money=money;
this.name=name;
}
}
class withdrawMoney extends Thread{
Account bank;
int nowMoney;
//取款金额
int takeMoney;
withdrawMoney(Account bank,int takeMoney,String name){
super(name);
this.bank=bank;
this.takeMoney=takeMoney;
}
@Override
public void run() {
synchronized (bank){
//判断是否能取钱
if(bank.money-takeMoney<0){
System.out.println("余额不够");
return;
}else {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
bank.money=bank.money-takeMoney;
nowMoney=nowMoney+takeMoney;
System.out.println("账户还剩下"+bank.money);
System.out.println(Thread.currentThread().getName()+"手里的钱"+nowMoney);
}
}
}
}
七、死锁
多个线程各自占有一些共享资源,并且只有等待其他线程释放资源才能运行。A:我需要你给我钱,我才能给你货。B:我需要你给我货,我才能给你钱。
八、生产者和消费者问题
生产者将商品放入仓库,消费者从仓库中取走商品。
- 仓库没有商品,生产者将商品放入仓库。有商品,则停止生产并且等待。直到消费者全部拿走
- 仓库有商品,消费者取走,无商品,停止消费并且等待,生产者生产商品放入仓库
package com.zheng.demo6;
public class CustomerAndProducer {
public static void main(String[] args) {
Warehouse warehouse = new Warehouse();
new Producer(warehouse).start();
new Customer(warehouse).start();
}
}
//生产者
class Producer extends Thread {
Warehouse warehouse;
public Producer(Warehouse warehouse) {
this.warehouse = warehouse;
}
//生产
public void run() {
for (int i = 0; i < 50; i++) {
System.out.println("生产了" + i + "个商品");
warehouse.push(new Food(i));
}
}
}
//消费者
class Customer extends Thread {
Warehouse warehouse;
public Customer(Warehouse warehouse) {
this.warehouse = warehouse;
}
//消费
public void run() {
for (int i = 0; i < 50; i++) {
System.out.println("消费了" + warehouse.pop().id + "个商品");
}
}
}
//产品
class Food {
int id;
public Food(int id) {
this.id = id;
}
}
//缓冲区
class Warehouse {
//生产者仓库大小
Food[] foods = new Food[10];
//计数器
int count = 0;
//生产者放入商品
public synchronized void push(Food food) {
//先判断能否放入
if (count == foods.length) {
//已放满,通知消费者消费
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
} else {
//没有放满,继续放入产品
foods[count] = food;
count++;
this.notify();//通知消费者消费
}
}
//消费者消费商品
public synchronized Food pop() {
//先判断能否消费
if (count == 0) {
//等待生产者生产
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//可以消费
count--;
Food food = foods[count];
//通知生产者生产
this.notifyAll();
return food;
}
}