多线程学习笔记
多线程学习笔记
一、多线程的实现方法
1.继承Thread
public class MyThread extends Thread{
@Override
public void run() {
}
public static void main(String[] args) {
new MyThread().start();
}
}
2.实现Runnable接口
public class MyThread implements Runnable{
@Override
public void run() {
}
public static void main(String[] args) {
new Thread(new MyThread()).start();
}
}
二、多线程的底层原理:静态代理
静态代理的条件:
1.代理类和真实类都实现同一个接口
2.代理对象要代理真实对象
好比结婚这件事,你可以选择自己办婚礼,也可以请婚庆公司办婚礼,婚庆公司就是代理对象,而你就是真实对象。婚庆公司办婚礼,但结婚的人是你
interface MarryHappy{
void MarryHappy();
}
public class You implements MarryHappy{
@Override
public void MarryHappy() {
System.out.println("人间四大喜事:洞房花烛夜!秦老师新婚快乐!");
}
}
class WeddingCompany implements MarryHappy{
private MarryHappy target;
public WeddingCompany(MarryHappy target){
this.target=target;
}
@Override
public void MarryHappy() {
before();
target.MarryHappy();
after();
}
public void before(){
System.out.println("结婚前准备婚礼现场");
}
public void after(){
System.out.println("结婚后收礼金");
}
public static void main(String[] args) {
//这就是静态代理,Thread类的底层原理
new Thread(()-> System.out.println("结婚快乐")).start();//lambda表达式
new WeddingCompany(new You()).MarryHappy();
}
}
三、线程状态
1、五个状态
线程这部分内容和操作系统的联系上了,与进程的5大状态模型很像
分别是:新建态,就绪态,运行态,阻塞态,以及终止态
2、观察线程状态
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("观察线程状态=========");
});
System.out.println(thread.getState());//new
thread.start();
System.out.println(thread.getState());//Run
while (thread.getState()!= Thread.State.TERMINATED){
Thread.sleep(500);
System.out.println(thread.getState());
}
}
}
四、线程的一些方法总结
//1、查看当前线程的名字
Thread.currentThread().getName();
//2、查看线程状态
new Thread(new RunnaleImpl()).getState();
//3、休眠sleep
Thread.sleep(1000)//参数是毫秒值
//4、线程礼让
new Thread(new RunnaleImpl()).yield();
//该方法会使得当前线程重新回到就绪队列,但不一定礼让成功,全靠cpu的调度
//5、线程强制执行join,相当于插队,强制该线程执行,其他线程阻塞,包括main线程
new Thread(new RunnaleImpl()).join();
//6、设置,获取线程的优先级priority
new Thread(new RunnaleImpl()).setPriority();
new Thread(new RunnaleImpl()).getPriority();
//优先级在1~10之间,Thread.MIN_PRIORITY=1;Thread.NROM_PRIORITY=5;Thread.MAX_PRIORITY=10;
//优先级低(调度的概率低)也有可能被调度
//7、设置守护线程Daemon
new Thread(new RunnaleImpl()).setDaemon(true);
//God,和You都是Thread的子类,其中God的Run方法是死循环,You的Run不是
五、线程同步
注:这里简单的说说多个线程访问共享资源的问题
1、多线程不安全的原因
每个线程都在自己的工作内存中交互,内存控制不当就会导致数据不一致
这个说实话我也不大懂。拿买票来说,就是假设三个线程抢着买票。不排队,只剩最后一张的时候,三个线程都看到票了,于是都去购票,三个线程都以为自己买到了。造成了数据不一致
2、三个线程不安全案例
1)车站买票
public class UnsafeBuyTicket {
public static void main(String[] args) {
Station station=new Station();
new Thread(station,"苦逼的狂神").start();
new Thread(station,"牛皮的我").start();
new Thread(station,"不要脸的黄牛党").start();
}
}
class Station implements Runnable{
private int ticketNum=10;
private boolean flag=true;
private synchronized void buy() throws InterruptedException {
if(ticketNum<=0){
flag=false;
return;
}
Thread.sleep(100);
System.out.println(Thread.currentThread().getName()+"抢到了第"+ticketNum--+"张票");
}
@Override
public void run() {
while (flag){
try {
buy();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
2)银行取钱
public class UnsafeBank {
public static void main(String[] args) {
Account account=new Account(100,"结婚基金");
Thread you=new Drawing(account,50,"you");
Thread girlFriend=new Drawing(account,100,"girlFriend");
you.start();
girlFriend.start();
}
}
class Drawing extends Thread{
private int drawingMoney;
Account account;
private int nowMoney=0;
public Drawing(Account account,int drawingMoney,String name){
this.account=account;
this.drawingMoney=drawingMoney;
this.setName(name);
}
@Override
public void run() {
synchronized (account){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
//取钱
if(this.account.getTotalMoney()<this.drawingmoney){ system.out.println(this.getname()+"银行存款不足");="" }else="" {="" this.account.settotalmoney(account.gettotalmoney()-this.drawingmoney);="" this.nowmoney+="this.drawingMoney;" system.out.println(this.getname()+"手头有"+this.nowmoney);="" system.out.println(this.account.getname()+"还剩"+this.account.gettotalmoney());="" }="" class="" account="" private="" int="" totalmoney;="" string="" name;="" 钱的用处="" public="" gettotalmoney()="" return="" void="" settotalmoney(int="" totalmoney)="" this.totalmoney="totalMoney;" getname()="" setname(string="" name)="" this.name="name;" account(int="" totalmoney,="" name){="" ```="" ####="" 3)arraylist集合="" ```java="" import="" java.util.arraylist;="" java.util.list;="" unsafelist="" static="" main(string[]="" args)="" list<string=""> list=new ArrayList<>();
for (int i = 0; i < 1000; i++) {
new Thread(()-> {
synchronized (list){
list.add(Thread.currentThread().getName());
}
}).start();
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(list.size());
}
}
//提一嘴 CopyOnWriteArrayList是线程安全的
3、解决线程安全的方式
1)synchronized代码块,synchronized同步方法
//使用synchronized关键字就是想把需要共享访问的内容锁起来,让线程排队一个一个来。
//个人理解:共享资源就是一扇门,一扇门对应一把锁(门被锁),锁在CPU那,线程要访问门就要找CPU拿锁,谁被CPU分配锁才能访问这扇门,该线程访问完后CPU再进行锁的分配
//synchronized锁的是this对象
public synchronized void test(){
/*同步方法的弊端:
*代码有需要修改和只读的
*使用synchronized关键字导致只读代码也需要锁才能访问
*/
}
//synchronized代码块,obj就是被锁的共享资源
synchronized(obj){
}
2)lock
lock的好处就是显示的加锁,放锁
六、线程通信
1、一些有关通信的方法
2、生产者和消费者问题
1)问题说明
用狂神的话说就是:
简单的说就是你去肯德基买炸鸡,没有了你通知工作人员制作,工作人员做鸡,做好了通知你来吃
2)管程法
public class TestPC {
public static void main(String[] args) {
SynContainer synContainer=new SynContainer();
new Producer(synContainer).start();
new Customer(synContainer).start();
}
}
class Producer extends Thread{
private SynContainer synContainer;
public Producer(SynContainer synContainer){
this.synContainer=synContainer;
}
@Override
public void run() {
for (int i = 0; i < 100; i++) {
try {
synContainer.push(new Chicken(i));
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("生产者做了第"+i+"只鸡");
}
}
}
class Customer extends Thread{
private SynContainer synContainer;
public Customer(SynContainer synContainer){
this.synContainer=synContainer;
}
@Override
public void run() {
for (int i = 0; i < 100; i++) {
try {
System.out.println("消费者吃了-->第"+synContainer.pop().id+"只鸡");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
class Chicken{
int id;//产品编号
public Chicken(int id) {
this.id = id;
}
}
class SynContainer{
//用数组来装商品Chicken
Chicken[] chickens=new Chicken[10];
int count=0;
//生产者做鸡
public synchronized void push(Chicken chicken) throws InterruptedException {
//判断当前chicken的数量满了没有,没有满添加,并通知消费者消费:有鸡
if(chickens.length==count){
this.wait();
}
chickens[count++]=chicken;
this.notifyAll();
}
//消费者吃鸡
public synchronized Chicken pop() throws InterruptedException {
//消费者判断有没有鸡,没鸡了等待生产者通知,有鸡吃了通知生产者做鸡
if(count==0){
this.wait();
}
count--;
Chicken chicken=chickens[count];
this.notifyAll();
return chicken;
}
}
3)信号灯法
//一个演员类
//一个观众类
//一个TV类
public class TestPC2 {
public static void main(String[] args) {
TV tv=new TV();
new Performer(tv).start();
new Audience(tv).start();
}
}
class Performer extends Thread{
TV tv;
public Performer(TV tv){
this.tv=tv;
}
@Override
public void run() {
for (int i = 0; i < 10; i++) {
if(i%2==0){
tv.perform("我的音乐你听吗");
}else {
tv.perform("美味持久,久到离谱");
}
}
}
}
class Audience extends Thread{
TV tv;
public Audience(TV tv){
this.tv=tv;
}
@Override
public void run() {
for (int i = 0; i < 10; i++) {
tv.view();
}
}
}
class TV{
String programName;
boolean flag=true;
//表演
public synchronized void perform(String programName) {
if(this.flag!=true){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("演员表演了"+programName);
this.programName=programName;
this.flag=!this.flag;
//通知观众观看
this.notifyAll();
}
//观看
public synchronized void view() {
if(this.flag==true){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("观众观看了"+this.programName);
this.flag=!this.flag;
//通知演员表演
this.notifyAll();
}
}
七、线程池
线程池相关类:
使用:
创建一个Runnable实现类MyThread
</this.drawingmoney){>

浙公网安备 33010602011771号