java基础<多线程>

时间:2021-08-25 16:02:36

线程概述

一、概述 进程:是一个正在执行中的程序。每一个进程执行都有一个执行顺序。该顺序是一个执行路径,或者叫一个控制单元。

线程:就是进程中的一个独立的控制单元。线程在控制着进程的执行。(一个进程中至少有一个线程。)

JVM 启动时,会有一个进程java.exe
该进程中至少有一个线程,在负责java程序的执行,而且这个线程运行的代码存在于main方法中。
该线程称之为————主线程。(其实还有一个负责“垃圾回收”的线程)

创建线程

一.如何在自定义的代码中,自定义一个线程呢?
通过对api的查找,java已经提供了对线程这类事物的描述,就是Thread类。

1.创建线程的第一种方式:继承Thread类。
①.定义类继承Thread
②.重写Thread类中的run方法。
将自定义代码存储在run方法中,让线程运行。
③.调用线程的start方法
该方法有两个作用:启动线程,调用run()方法。

2.创建线程的第一种方式:实现Runnable接口。
二、发现运行结果每一次都不同。
因为多个线程都在获取cpu的执行权,cpu执行到谁,谁就运行。
明确一点:在某一个时刻,只能有一个程序在运行。(多核除外)
cpu在做着快速的切换,以达到看上去是同时运行的效果。
我们可以形象把多线程的运行行为在互相抢夺cpu的执行权。
这就是多线程的一个特性:随机性。至于谁执行多长时间,由cpu来决定。

三、为什么要覆盖run方法? Thread类用于描述线程。该类就定义了一个存储功能,用于存储线程要运行的代码,该存储功能就是run方法。也就是说,Thread类中的run方法,用于存储线程要运行的代码。
四、代码
class Demo extends Thread
{
public void run()
{
for(int x=0;x<60;x++)
System.out.println("demo run");
}

}
classThreadDemo
{
public static void main(String[] args)
{
Demo d=nw Demo();
d.start();
//d.run();//如果直接调用run()方法,那么就不会启动线程,仅仅是一个对象调用方法。
for(int x=0;x<60;x++)
System.out.println("Hello World__"+x);
}
}


线程运行状态

一、概述java基础<多线程>

获取线程对象以及名称

一、代码 
class Demo extends Thread
{
Demo(String name)
{
super(name);
}
public void run()
{
for(int x=0;x<60;x++)
{
System.out.println(this.getName()+x);
System.out.println(Thread.currentThread().getName()+x);//结果同上。
}
}

}
classThreadDemo
{
public static void main(String[] args)
{
Demo d1=new Demo("one---");
//d1.setName("one---");
Demo d2=new Demo("two---");
//d2.setName("two---");
d1.start();
d2.start();
//d.run();//如果直接调用run()方法,那么就不会启动线程,仅仅是一个对象调用方法。

}
}

售票的例子

一、概述需求:简单的卖票程序。
多个窗口同时卖票 
class Ticket extends Thread
{
private static int ticket=100;//不建议使用,因为static的声明周期过长。使用下一节的方法来替代。
public void run()
{
while(true)
{
if(ticket>0)
{
System.out.println(currentThread().getName()+"sale:"+ticket--);
}

}
}
}


class TicketDemo
{
public static void main(String[] args)
{
Ticket t1=new Ticket();
Ticket t2=new Ticket();
Ticket t3=new Ticket();
Ticket t4=new Ticket();

t1.start();
t2.start();
t3.start();
t4.start();
}
}

Runnable接口

一、创建线程的第二种方式:实现Runnable接口
步骤:
1.定义类实现Runnable接口。
2.覆盖Runnable接口中的run方法。
将线程要运行的代码存放在该run方法中。
3.通过Thread类建立线程对象。
4.将Runnable接口的子类对象作为实际参数传递给Thread类的构造方法。
为什么要讲Runnable接口的子类对象传递给Thread的构造方法。
因为,自定义的run方法所属的对象是Runnable接口的子类对象。
所以要让线程去指定指定对象的run方法。就必须明确该run方法所属的对象。
5.调用Thread类的start方法开启线程并调用Runnable接口子类的run方法


二、****实现方式和继承方式有什么区别呢?****

1.实现方式好处:避免了单继承的局限性。
   在定义线程时,建议使用实现方式。

2.两种方式的区别:
   继承Thread:线程代码存放在Thread子类run方法中。
   实现Runnable:线程代码存放在接口的子类的run方法中。

三、代码
class Ticket implements Runnable
{
private int ticket=100;
public void run()
{
while(true)
{
if(ticket>0)
{
System.out.println(Thread.currentThread().getName()+"sale:"+ticket--);
}

}
}
}


class TicketDemo2
{
public static void main(String[] args)
{

Ticket t=new Ticket();
Thread td1=new Thread(t);
Thread td2=new Thread(t);
Thread td3=new Thread(t);
Thread td4=new Thread(t);

td1.start();
td2.start();
td3.start();
td4.start();

}
}

多线程的安全问题——同步代码块

一、概述当在程序中加入sleep(10)后,发现打印出0,-1,-2等错票,即多线程的运行出现了安全问题。
问题的原因:
当多条语句在操作同一个线程共享数据时,一个线程对多条语句只执行了一部分,还没有执行完,另一个线程参与进来执行,导致共享数据的错误。

解决办法:
对多条操作共享数据的语句,只能让一个线程都执行完,在执行过程中,其他线程不可以参与执行。

java对于多线程的安全问题提供了专业的解决方式————同步代码块

二、同步代码块
synchronized(对象)
{
需要被同步的代码。
}

对象如同锁,持有锁的线程可以再同步中执行。没有持有锁的线程即使获取cpu的执行权,也进不去,因为没有获取锁。
三、同步的前提:1.必须要有两个或者两个以上的线程。2.必须是多个线程使用同一个锁。
四、同步的利弊好处:解决了多线程的安全问题。弊端:多个线程都需要判断锁,较为消耗资源。
五、代码
class Ticket implements Runnable
{
private int ticket=100;
Object obj=new Object();
public void run()
{
while(true)
{
synchronized(obj)//不可以直接放入new object(),否则依然会出现安全问题,具体原因还不清楚,有待查明。
{
if(ticket>0)
{
try
{
Thread.sleep(10);
}
catch (Exception e)
{
}
System.out.println(Thread.currentThread().getName()+"sale:"+ticket--);
}
}
}
}
}


class TicketDemo2
{
public static void main(String[] args)
{

Ticket t=new Ticket();
Thread td1=new Thread(t);
Thread td2=new Thread(t);
Thread td3=new Thread(t);
Thread td4=new Thread(t);

td1.start();
td2.start();
td3.start();
td4.start();

}
}

多线程——同步代码块

一、需求银行有一个金库。
有两个储户分别存300元,每次存100,存3次。

二、目的该程序是否有安全问题,如果有,如何解决。
三、如何找问题1.明确哪些代码是多线程运行代码。
2.明确共享数据
3.明确多线程运行代码中,哪些语句是操作共享数据的。

四、代码
class Bank
{
private int sum;
//Object obj=new Object();
public synchronized void add(int n)//在函数上加上 synchronized,该函数就被称为同步函数,能够实现共享数据的同步操作。
{
//synchronized(obj)
//{
sum=sum+n;
try
{
Thread.sleep(10);
}
catch (Exception e)
{
}

System.out.println(Thread.currentThread().getName()+"...."+sum);
//}
}
}

class Consumer implements Runnable
{
private Bank b=new Bank();
public void run()
{

for(int i=0;i<20;i++)
{
//synchronized(b)
//{

b.add(100);

//}
}
}
}

class ThreadDemo1
{
public static void main(String[] args)
{
Consumer con=new Consumer();
new Thread(con).start();
new Thread(con).start();
}
}

同步函数的锁是——this

一、同步函数用的是哪一个锁呢?函数需要被对象调用,那么函数都一个所属对象引用,那就是this.
所以同步函数使用的锁是————this

二、通过该程序进行验证。为了验证方便,使用两个线程来卖票
一个线程在同步代码块中。
一个线程在同步函数中。
都在执行卖票动作。

三、代码
class Ticket implements Runnable
{
private int ticket=100;
Object obj=new Object();
boolean flag=true;
public void run()
{
if(flag)
{
while(ticket>0)
{
synchronized(this)
{
if(ticket>0)
{
try{Thread.sleep(10);}catch (Exception e){}
System.out.println(Thread.currentThread().getName()+"code"+ticket--);
}
}

}

}

else
while(ticket>0)
buyTicket();

}

public synchronized void buyTicket()
{
if(ticket>0)
{
try{Thread.sleep(10);}catch (Exception e){}
System.out.println(Thread.currentThread().getName()+"method"+ticket--);
}
}
}


class TicketThisLockDemo
{
public static void main(String[] args) throws Exception
{

Ticket t=new Ticket();
Thread td1=new Thread(t);
Thread td2=new Thread(t);
//Thread td3=new Thread(t);
//Thread td4=new Thread(t);

td1.start();
Thread.sleep(10);
t.flag=false;
td2.start();
//td3.start();
//td4.start();

}
}

静态同步函数的锁是——Class对象

一、概述静态进内存时,内存中没有本类对象,但是一定有该类对应的字节码文件对象——类名.class

二、代码
class Ticket implements Runnable
{
private static int ticket=100;
boolean flag=true;
public void run()
{
if(flag)
{
while(ticket>0)
{
synchronized(Ticket.this)
{
if(ticket>0)
{
try{Thread.sleep(10);}catch (Exception e){}
System.out.println(Thread.currentThread().getName()+"code"+ticket--);
}
}

}

}

else
while(ticket>0)
buyTicket();

}

public static synchronized void buyTicket()
{
if(ticket>0)
{
try{Thread.sleep(10);}catch (Exception e){}
System.out.println(Thread.currentThread().getName()+"method"+ticket--);
}
}
}


class TicketThisLockDemo
{
public static void main(String[] args) throws Exception
{

Ticket t=new Ticket();
Thread td1=new Thread(t);
Thread td2=new Thread(t);
//Thread td3=new Thread(t);
//Thread td4=new Thread(t);

td1.start();
Thread.sleep(10);
t.flag=false;
td2.start();
//td3.start();
//td4.start();

}
}

多线程——单例设计模式

一、恶汉式
class Single1
{
private static final Single1 single=new Single1();

private Single1(){}

public static Single1 getInstance()
{
return single;
}
}
二、懒汉式
class Single
{
private static Single single=null;

private Single(){}

public static Single getInstance()
{
if(single==null)
{
synchronized(Single.class)
{
if(single==null)
single=new Single();
}
}
return single;
}
}

多线程——死锁

一、同步出现的问题——死锁同步中嵌套同步,详情看下面代码例子
二、代码
class Test implements Runnable
{
private boolean flag;
Test(boolean flag)
{
this.flag=flag;
}


public void run()
{
if(flag)
{
synchronized(Lock.a)
{
System.out.println("if...a");
synchronized(Lock.b)
{
System.out.println("if...b");
}
}
}
else
{
synchronized(Lock.b)
{
System.out.println("else...b");
synchronized(Lock.a)
{
System.out.println("else...a");
}
}
}
}


}

class Lock
{
public static Object a=new Object();
public static Object b=new Object();
}




class DeadLockDemo2
{
public static void main(String[] args)
{
Thread t1=new Thread(new Test(true));
Thread t2=new Thread(new Test(false));
t1.start();
t2.start();
}
}

线程间通信——简单的示例代码

一、概述线程间通讯:

其实就是多个线程在操作同一个资源,但是操作的工作不同。

二、代码****此代码会出现安全问题,解决方式看下一节*****
class Student
{
String name;
String sex;
}

class Input implements Runnable
{
private Student stu;
Input(Student stu)
{
this.stu=stu;
}

public void run()
{
boolean flag=true;
while(true)
{

if(flag)
{
stu.name="张三";
stu.sex="男";
flag=false;
}
else
{
stu.name="翠花";
stu.sex="女";
flag=true;
}
}
}

}

class Output implements Runnable
{
private Student stu;
Output(Student stu)
{
this.stu=stu;
}
public void run()
{
while(true)
{
System.out.println(stu.name+"...."+stu.sex);
}

}
}


class ThreadCommunicationDemo
{
public static void main(String[] args)
{
Student stu=new Student();
new Thread(new Input(stu)).start();
new Thread(new Output(stu)).start();
}
}

线程间通信——解决安全问题

一、概述要解决上一节的问题,同样要从同步的两个前提入手1.同步的线程要是两个或者两个以上。2.使用同一个锁。
解决代码如下二、代码
class Student
{
String name;
String sex;
}

class Input implements Runnable
{
private Student stu;
Input(Student stu)
{
this.stu=stu;
}

public void run()
{
boolean flag=true;
while(true)
{
synchronized(stu)
{
if(flag)
{
stu.name="张三";
stu.sex="男";
flag=false;
}
else
{
stu.name="翠花";
stu.sex="女";
flag=true;
}
}
}
}

}

class Output implements Runnable
{
private Student stu;
Output(Student stu)
{
this.stu=stu;
}
public void run()
{
while(true)
{
synchronized(stu)
{
System.out.println(stu.name+"...."+stu.sex);
}
}

}
}


class ThreadCommunicationDemo
{
public static void main(String[] args)
{
Student stu=new Student();
new Thread(new Input(stu)).start();
new Thread(new Output(stu)).start();
}
}

线程间通信——等待唤醒机制

一、概述1.wait()
   notify();
   notifyAll();
   都使用在同步中,因为要对持有监视器(锁)的线程操作。
   所以要使用在同步中,因为只有同步才具有锁。


2.为什么这些操作线程的方法要定义在Object类中呢?
    因为锁可以是任意对象。
二、代码1.原始代码
class Student
{
String name;
String sex;
boolean flag=false;
}

class Input implements Runnable
{
private Student stu;
Input(Student stu)
{
this.stu=stu;
}

public void run()
{
boolean flag=true;
while(true)
{
synchronized(stu)
{
if(stu.flag==true)
try
{
stu.wait();
}
catch (Exception e)
{
}

if(flag)
{
stu.name="张三";
stu.sex="男";
flag=false;
}
else
{
stu.name="翠花";
stu.sex="女";
flag=true;
}

stu.flag=true;
stu.notify();
}
}
}

}

class Output implements Runnable
{
private Student stu;
Output(Student stu)
{
this.stu=stu;
}
public void run()
{
while(true)
{
synchronized(stu)
{
if(stu.flag==false)
try
{
stu.wait();
}
catch (Exception e)
{
}
System.out.println(stu.name+"...."+stu.sex);
stu.flag=false;
stu.notify();
}
}

}
}


class ThreadCommunicationDemo
{
public static void main(String[] args)
{
Student stu=new Student();
new Thread(new Input(stu)).start();
new Thread(new Output(stu)).start();
}
}
2.优化后代码
class Student
{
private String name;
private String sex;
boolean flag=false;

public synchronized void set(String name,String sex)
{
if(this.flag)
try{this.wait();}catch (Exception e){}
this.name=name;
this.sex=sex;

this.flag=true;
this.notify();

}

public synchronized void print()
{
if(!this.flag)
try{this.wait();}catch (Exception e){}
System.out.println(name+"...."+sex);

this.flag=false;
this.notify();
}

}

class Input implements Runnable
{
private Student stu;
Input(Student stu)
{
this.stu=stu;
}

public void run()
{
boolean flag=true;
while(true)
{
if(flag)
{
stu.set("张三","男");
flag=false;
}
else
{
stu.set("翠花","女");
flag=true;
}

}
}

}

class Output implements Runnable
{
private Student stu;
Output(Student stu)
{
this.stu=stu;
}
public void run()
{
while(true)
{
stu.print();
}

}
}


class ThreadCommunicationDemo2
{
public static void main(String[] args)
{
Student stu=new Student();
new Thread(new Input(stu)).start();
new Thread(new Output(stu)).start();
}
}

线程间通信——生产者消费者(JDK1.5新版本改进方法)

一、概述对于多个生产者和消费者,为什么要定义while判断标记?
原因:让被唤醒的线程再一次判断标记。
为什么定义notifyAll?因为需要唤醒对方进程。因为只用notify,容易出现只唤醒本方的情况,导致程序中所有线程都在等待
二、代码(JDK1.5以前的方法)
class WareHouse
{
private String name;
private int count=0;
private boolean flag=false;
public synchronized void set(String name)
{
while(flag)
try{this.wait();}catch (Exception e){}
this.name=name;
count++;
System.out.println(Thread.currentThread().getName()+":"+name+".....生产者......"+count);
flag=true;
this.notifyAll();
}

public synchronized void print()
{
while(!flag)
try{this.wait();}catch (Exception e){}
System.out.println(Thread.currentThread().getName()+":"+name+"...消费者..."+count);
flag=false;
this.notifyAll();
}
}

class Producer implements Runnable
{
private WareHouse wh;
Producer(WareHouse wh)
{
this.wh=wh;
}

public void run()
{
while(true)
{
wh.set("商品");
}
}
}

class Consumer implements Runnable
{
private WareHouse wh;
Consumer(WareHouse wh)
{
this.wh=wh;
}

public void run()
{
while(true)
{
wh.print();
}
}
}



class ProducerAndConsumerDemo
{
public static void main(String[] args)
{
WareHouse wh=new WareHouse();
new Thread(new Producer(wh)).start();
new Thread(new Producer(wh)).start();
new Thread(new Consumer(wh)).start();
new Thread(new Consumer(wh)).start();
}
}
三、代码(JDK1.5以后的方法)
JDK1.5 中提供了多线程升级解决方案。将同步Synchronized替换成现实Lock操作。将Object中的wait,notify,notifyAll,替换了Condition对象。该对象可以Lock锁进行获取。该示例中,实现了本方只唤醒对方发操作。
import java.util.concurrent.locks.*; 

class WareHouse
{
private String name;
private int count=1;
private ReentrantLock lock=new ReentrantLock();
private Condition cd1=lock.newCondition();
private Condition cd2=lock.newCondition();
private boolean flag=false;
public void set(String name)
{
lock.lock();

try{
while(flag)
cd1.await();
this.name=name;
System.out.println(Thread.currentThread().getName()+":"+name+".....生产者......"+(++count));
flag=true;
cd2.signal();
}
catch (Exception e){}
finally
{
lock.unlock();
}
}

public void print()
{

lock.lock();
try{
while(!flag)
cd2.await();
System.out.println(Thread.currentThread().getName()+":"+name+"...消费者..."+count);
flag=false;
cd1.signal();
}
catch (Exception e){}
finally
{
lock.unlock();
}

}
}

class Producer implements Runnable
{
private WareHouse wh;
Producer(WareHouse wh)
{
this.wh=wh;
}

public void run()
{
while(true)
{
wh.set("商品");
}
}
}

class Consumer implements Runnable
{
private WareHouse wh;
Consumer(WareHouse wh)
{
this.wh=wh;
}

public void run()
{
while(true)
{
wh.print();
}
}
}



class ProducerAndConsumerDemo
{
public static void main(String[] args)
{
WareHouse wh=new WareHouse();
new Thread(new Producer(wh)).start();
new Thread(new Producer(wh)).start();
new Thread(new Consumer(wh)).start();
new Thread(new Consumer(wh)).start();
}
}

停止线程

一、stop方法已过时,那么如何停止线程?只有一种,run方法结束。
开启多线程运行,运行代码通常是循环结构。
只要控制住循环就可以让线程结束。

二、特殊情况当线程处于了冻结状态,就不会读取到标记,那么线程就不会结束。
解决办法:
这时需要对冻结状态进行清除,强制让线程恢复到运行状态中来,这样就可以操作标记让线程结束。
Thread类提供了该方法————interrupt();

三、代码
class StopThread implements Runnable
{
private boolean flag=true;
public synchronized void run()
{
while(flag)
{
try
{
wait();
}
catch (InterruptedException e)
{
System.out.println(Thread.currentThread().getName()+".....Exception");
flag=false;
}
System.out.println(Thread.currentThread().getName()+"...run");
}
}

public void stop()
{
flag=false;
}
}


class StopThreadDemo
{
public static void main(String[] args)
{
StopThread st=new StopThread();
Thread td1=new Thread(st);
Thread td2=new Thread(st);

td1.start();
td2.start();

int num=0;

while(true)
{
if(num++==60)
{
//st.stop();
td1.interrupt();
td2.interrupt();
break;
}

System.out.println(Thread.currentThread().getName()+"...."+num);
}
}
}

守护线程

一、概述Thread类中的——setDaemon(boolean on)方法。该方法必须在启动线程前调用。
setDaemon(boolean on)方法用于将线程设置为守护线程——守护线程的意思是,当正在运行的线程”都是“守护线程时,Java 虚拟机退出。
二、代码
class StopThread implements Runnable
{
private boolean flag=true;
public synchronized void run()
{
while(flag)
{
try
{
wait();
}
catch (InterruptedException e)
{
System.out.println(Thread.currentThread().getName()+".....Exception");
flag=false;
}
System.out.println(Thread.currentThread().getName()+"...run");
}
}

public void stop()
{
flag=false;
}
}


class StopThreadDemo
{
public static void main(String[] args)
{
StopThread st=new StopThread();
Thread td1=new Thread(st);
Thread td2=new Thread(st);
td1.setDaemon(true);
td2.setDaemon(true);
td1.start();
td2.start();

int num=0;

while(true)
{
if(num++==60)
{
break;
}
System.out.println(Thread.currentThread().getName()+"...."+num);
}
}
}

join方法

一、概述
Thread类中的——join方法用于抢夺CPU执行权,只有当调用该方法的线程结束,其他线程才有执行权,否则该线程会一直执行。当A线程执行到了B线程的join()方法,A就会等待。等B线程都执行完,A才会执行。
注意:该方法的执行,并不代表其他线程不执行,而是指——一A线程中有B,C两个线程,如果B执行了join(),那么A线程就会释放执行资格进入冻结状态,如果在B线程调用join()方法之前C线程已经有执行资格,那么B,C就会共同抢夺CPU资源来执行,直到B线程执行完毕,A线程才会拥有执行资格。
二、代码
class Demo implements Runnable
{
public void run()
{
for(int x=0;x<70;x++)
{
System.out.println(Thread.currentThread().getName()+"....."+x);
}

}
}

class JoinDemo
{
public static void main(String[] args) throws InterruptedException
{
Demo d=new Demo();
Thread td1=new Thread(d);
Thread td2=new Thread(d);
td1.start();
td2.start();
td1.join();//等待该线程结束。

for(int x=0;x<80;x++)
{
System.out.println("main..."+x);
}

System.out.println("over");
}
}

线程执行优先级 and yield()方法

一、线程执行优先级优先级:代表线程抢夺CPU资源的频率(划分等级为1——10,10为最高),所有程序的默认优先级为5,可以通过setPriority()方法来设置。setPriority()方法:MAX_PRIORITYMIN_PRIORITYNORM_PRIORITY 三种优先级
二、yield()方法。暂停当前正在执行的线程对象,并执行其他线程。
class Demo implements Runnable
{
public void run()
{
for(int x=0;x<70;x++)
{
System.out.println(Thread.currentThread().getName()+"....."+x);
Thread.yield();
}

}
}

class YieldDemo
{
public static void main(String[] args) throws InterruptedException
{
Demo d=new Demo();
Thread td1=new Thread(d);
Thread td2=new Thread(d);
td1.start();
td2.start();
System.out.println("over");
}
}