多线程的状态
创建线程的第一种方式—继承Thread类(其实就是覆盖run方法,然后调用自己的show方法)
创建线程的第二种方式—实现Runnable接口
但是人家万一已经有爹了,那就没法了。不支持多继承。此时要拓展该类的功能,创建接口。让其中的内容作为线程的任务执行。接口就是为了实现拓展功能;一个类在继承一个类的同时,也可以实现多个接口,为了这个类增加多个功能。通过接口的形式完成(拓展Demo类的功能,让其中的内容可以作为线程的任务执行。)
Class Demo implements Runnable
{
Public void run()
{
Show();
}//run()方法是Runnable里面就有的,而 show()是自己写的。
Public void show()
{
}
}
Class ThreadDemo
{
Public static void main()
{
Demo d=new Demo();//尽量跟下面2个对象结合起来。
加一条这个语句就可以了。(JAVA的API里面有)
Thread t1=new Thread(d);
Thread t2=new Thread(d);
T1.start();
T2.start();
}
}
1. 定义类实现Runnable接口。
2. 覆盖接口中的run方法,将线程的任务代码封装到run方法中。
3. 通过Thread类创建线程对象,并将Runnable接口子类对象作为Thread类的构造函数的参数进行传递。为什么?
因为线程的任务都封装在Runnable接口子类对象的run方法中。所以要在线程对象创建时就必须明确要运行的任务。
4. 调用线程对象的start方法开启线程。
第二种方式的细节(2种方式多线程的原理弄懂)
第二种方式的好处
把线程任务封装成了对象(直接就将线程任务封装,不用线程出现,不用继承)其实本质目的:就是为了运行那一小团代码。
接口(额外功能)
实现Runnable接口的好处:
1. 将线程的任务从线程的子类中分离出来,进行了单独的封装。按照面向对象的思想将任务进行封装成对象。
2. 避免Java单继承的局限性。
所以,创建线程的第二种方式较为常用。
线程安全问题的现象(卖票的例子那集没有看)
因为cpu切换不确定性来来的安全隐患。
Sleep(Java.lang包里面有)
原因:1.多个线程操作共享数据。
2.操作共享数据的线程代码有多条。
当一个线程在执行操作共享数据的多条代码过程中,其他线程也参与了运算,就会导致问题出现。
同步代码
解决思路:
就是将多条操作共享数据的线程代码封装起来,当有线程执行这些代码的时候,其他线程不可以参与运算。必须要当前线程把这些代码执行完,其他线程才可以参与运算。
在Java中用Synchronized(同步代码块)可以解决这个问题。
格式是:Synchronized(对象)
{
需要被同步的代码;
}
对象该放什么呢?
自己造一个也行,放Object对象也行。
同步的前提
必须有多个线程并使用同一个锁。
同步函数
/*
需求:储户,两个,每个都到银行存钱,每次存100,共存3次。
*/
Class Bank
{
Private int sum;
Public void add(int num)
{
Sum+=num;
Sop(“sum”+=sum);
}
}
Class Cus implements Runnable
{
Public void run()
{
Bank b=new bank();//需要把这一句放到run方法外面,并且加上private修饰。
For(int x=0;x<3;x++)
{
B.add(100);
}
}
}
Class BankDemo
{
Public static void main(String[] args)
{
Cus c=new Cus();
Thread t1=new Thread(c);
Thread t2=new Thread(c);
T1.start();
T2.start();
}
}
上述运行结果为:
显然,银行的总金额不能像上述这样变化。
同步代码块和同步函数都能解决线程的安全问题。同步代码块有锁,但是同步函数的锁到底是谁呢?
验证同步函数的锁(听的还是有点模糊)
同步函数的锁是this(这一集的代码就是为了验证锁是this)
同步函数和同步代码块的区别:
同步函数的锁是固定的this,同步代码块是任意的对象。建议使用同步代码块,同步函数可以作为同步代码块的简写形式。简写的话就会有他的弊端。
验证静态同步函数的锁
它的锁(对象)肯定不是this,静态函数也没有this所属啊。那么它的锁到底是哪个呢?
静态函数的同步锁使用的锁是:该函数所属字节码文件对象,可以用getclass方法获取,也可以当前类名.class表示。
其实只需要保证多个线程用的是同一个锁就行。
单例模式涉及的多线程问题
多线程下的单例:
饿汉式
Class Single
{
Private static final Single s=new Single();
Private Single(){}
Public static Single getInstance()
{
Return s;
}
}
懒汉式
Class Single
{
Private static Single s=null;
Private Single(){}
Public static synchronized Single getInstance()
{
If(s==null)
s=new Single();
Return s;
}
} //会导致对象的不唯一,要解决问题,可以用同步函数解决问题,但是降低了效率,每次需要判断是不是同一个锁。可以考虑用同步代码块来解决:
Class Single
{
Private static Single s=null;
Private Single(){}
Public static synchronized Single getInstance()
{
If(s==null)
{
Synchronized(Single.class)//this和Single.getclass都不行,因为静态方法不存在this,另外getclass方法是非静态的。而且还不用判断锁,但是这样效率就提高了。(设计java的人真心聪明啊!)加锁是为了解决安全问题,而if是为了解决效率问题。(面试多用这个)
{
If(s==null)
s=new Single();
Return s;
}
}
}
}
死锁
程序实例来演示。
我的锁里面有你的锁,你的锁里面有我的锁。常见情景之一是同步嵌套。
Class Test
{
Private boolean flag;
Test(boolean flag)
{
This.flag=flag;
}
Public void run()
{
If(flag)
{
Synchronized(MyLock.locka)
{
Sop(Thread.currentThread().getname()+“ if locka”);
Synchronized(MyLock.lockb)
{
Sop(Thread.currentThread().getname()+“ if lockb”);
}
}
}
Else
{
Synchronized(MyLock.lockb)
{
Sop(Thread.currentThread().getname()+“ else lockb”);
Synchronized(MyLock.locka)
{
Sop(Thread.currentThread().getname()+“ else locka”);
}
}
}
}
}
Class MyLock
{
Public static final Object locka=new Object();
Public static final Object lockb=new Object();
}
Class DeadLockTest
{
Public static void main(String[] args)
{
Test a=new Test(true);
Test b=new Test(false);
Thread t1=new Thread(a);
Thread t2=new Thread(a);
T1.start();
T2.start();
}
}
虽然flag2个线程各自分别有一份,但是因为flag是boolean类型的变量,只有2种取值也行,若是int类型肯定不行。(还是有点不太理解暂时)
答疑
要玩同步,一定要使用同一个锁。