多线程:一个进程中有多个执行路径。
好处:解决了多部分代码同时运行的问题
弊端:影响效率(一般会将管理线程和普通线程分开)
原因是多线程就是多个应用程序同时执行,靠CPU做着快速的切换完成的。这个切换是随机的。CPU的切换是需要花费时间的,从而导致了效率的降低。
创建线程的方法一:继承Thread类
1.定义一个类继承Thread类。
2.覆盖Thread类中的run方法。
3.直接创建Thread的子类对象创建线程。
4.调用start方法开启线程并调用线程的任务run方法执行。
例子:
class Demo extends Thread{
private String name;
Demo(String name)
{
this name=name;
}
public void run()
{
for(int x=0;x<10;x++)
{
System.out.println(name+"…x="+"…Thread"+Thread.currentThread().getName());
}
}
}
class ThreadDemo{
public static void main(String[] args)
{
Demo d1=new Demo("旺财");
Demo d2=new Demo("xiaoqiang");
d1.start();
d2.start();
for(int x = 0; x < 20; x++){
System.out.println("x = " + x +"...over..." + Thread.currentThread().getName());}
}
创建线程的方式二:实现Runnable接口
1.定义类实现Runnable接口。
2.覆盖接口中的run方法,将线程的任务代码封装到run方法中。
3.通过Thread类创建线程对象,并将Runnable接口的子类对象作为Thread类的构造函数的参数进行传递。为什么?因为线程的任务都封装在Runnable接口子类对象的run方法中。所以要在线程对象创建时就必须明确要运行的任务。
4.调用线程对象的start方法开启线程。
举例如下:
运行结果如下:
多线程的产生虽然可是实现多段代码同时运行,但是同时也带来了安全问题,安全问题产生的原因是:当一条线程在执行共享数据的过程中,其他线程参与了运算,此时就是导致安全问题的产生,比如卖票,当票剩下一张时,一个线程进来了,它卖出了最后一张票,但是当它没来得及做减法运算时,另外一个线程进来了,又卖了一张票,此时就会造成卖出的票出现负数。解决线程安全问题的办法就是用同步代码块或者同步函数。
同步代码块格式:
Synchronized(对象)//对象就是锁,可以是this也可以是其他
{
需要被同步的代码;
}
同步函数的格式:在函数前加上synchronized修饰符。比如:public synchronized void add(),它的锁固定是this。
注意:静态的同步函数使用的锁是该函数所属字节码文件对象,可以用getClass方法获取,也可以用当前类名.class表示。
同步代码块可以实现只有当前线程运行完毕时其他线程才能参与运算。
同步的好处:解决了线程的安全问题;
同步的坏处:因为每次都要判断锁,影响效率。
同步的前提:多个线程必须使用同一个锁。
由于锁的设置不合理,可能会造成死锁的情况发生,此时程序将无法执行。
死锁常见的情况之一:就是程序中出现同步的嵌套,就是线程要执行的该段程序中存在两把锁,另一个线程要执行的程序中也存在这两把锁,不过锁的顺序调换了而已,在这种情况下就会造成死锁。
下面是视频中的例子:
线程间通信:多个线程在处理同一资源,但是任务却不同。资源可以当做锁,因为资源是唯一的。
等待/唤醒机制涉及的方法:
-
wait:让线程处于冻结状态,被wait的线程会被存储到线程池中。必须拥有对象监视器。wait方法会抛出异常,因为run方法不可以声明异常,所以必须是抛出异常。
2.notify:唤醒随意的一个。(如果要明确由哪个锁唤醒,格式为:r.notify; r.wait();)
3.notifyAll:唤醒所有线程
注意:wait(),notify(),notifyAll()都定义在object中。
原因:因为这些方法是监视器的方法,监视器其实就是锁。锁可以是任意的对象,任意的对象调用的方法一定是定义在Object类中。
举个例子:生产者和消费者的问题:
class Resource{
private Stringname;
private Stringsex;
private booleanflag=false;
public synchronized void set(String name;String sex)
{
if(flag)
{
try{
this.wait();
}
catch(InterruptedException e){ e.printStackTrace();}
this.name=name;
this.sex=sex;
flag=true;
this.notify();
}
public synchronizedvoid out()
{
if(!flag)
{
try{this.wait();}
catch(InterruptedException e){ e.printStackTrace();}
}
System.out.println(name+"…."+age);
flag=false;
this.notify();
}
}
class Input implements Runnable{
Resource r;
Input(Resource r)
{
this.r=r;
}
public void run()
{
int x=0;
while(true)
{
if(x==0)
{
r.set("milk","boy");
}
else
{
r.set("lili","girl");
}
x=(x+1)%2;
}
}
}
class Output implements Runnable{
Resource r;
Output(Resource r)
{
this.r=r;
}
public void run()
{
while(true){r.out();}
}
}
}
class ResourceDemo{
public static voidmain(String[] args)
{
Resource r=new Resource();
Input in=new Input();
Output out=new Output();
Thread t1=new Thread(in);
Thread t2=new Thread(out);
t1.start();
t2.start();
}
}
JDK1.5新特性:
JDK1.5以后将同步和锁封装成了对象,并把操作锁的隐式方式定义到了该对象中,将隐式动作变成了显式。现在就可以通过实现了lock接口的子对象来执行加锁的动作。
JDK1.5后出现的锁接口:
Lock接口:互斥锁;它的出现替代了同步代码快或者同步函数,将同步的隐式锁操作变成了显式锁操作,而且更加灵活,可以一个锁上加上多组监视器。
方法:
lock():获取锁
unlock():释放锁;一般定义在finally中。
lock,unlock是定义在lock接口中的。可以在java.lang.concurrent包中查看。
JDK1.5之后出现的监视器接口:
Condition接口:出现替代了object中的wait,notify,notityAll方法。将这些监视器方法单独进行了封装,变成了condition监视器对象,可以和任意锁进行组合。
Condition接口中的await方法对应于Object中的wait方法。
Condition接口中的signal方法对应于Object中的notify方法。
Condition接口中的signalAll方法对应于Object中的notifyAll方法
一个lock上可以放多个condition,每个condition中封装了一组await,signal, signalAll。
使用Lock来进行同步的一般形式如下:
wait和sleep区别:
-
wait可以指定时间也可以不指定时间;
sleep必须指定时间;
-
在同步中,对CPU的执行权和锁的处理不同。
wait:释放执行权,释放锁;
sleep:释放执行权,不释放锁。因为它不需要被人唤醒。
停止线程:不能用stop。停止线程一般用以下两种方法:
1.定义循环结束标记,缺陷是当线程都处于wait状态时就没用
2.Interrupt:当前冻结状态的清除,会抛出异常。t1.Interrupt();
守护线程:setDaemon(true);必须在线程启动前设置。相当于后台线程,它只要其他线程结束它就会结束,和前台线程需要进行手动退出不同。
join方法:等待该线程终止。
t1.join();//这句程序的意思是t1线程申请加入进来运行,然后主线程等待t1执行完毕再执行。
setPriority:更改线程的优先级;
toString方法:返回该线程的字符串表示形式,包括线程名称,优先级和线程组;
yield方法:暂停当前正在执行的线程对象,并执行其他线程;
例子如下:
class JoinDemo{
public static void main(String[] args){
Demo d = new Demo();
Thread t1 = new Thread(d);
Thread t2 = new Thread(d);
t1.start();
t2.start();
t2.setPriority(Thread. MAX_PRIORITY);//记住这里不能写10,虽然10是最高权限,但是程序无法识别
for(int x = 0; x < 50; x++){
System.out.println(Thread.currentThread().toString() + "..." + x);
}
}
}
class Demo implements Runnable{
public void run(){
for(int x = 0; x < 50; x++){
System.out.println(Thread.currentThread().getName() + "..." + x);
Thread. yield();//释放执行权
}
}
}