线程类实现了Runnable接口,则线程类的子类可以实现run方法,当获取子类的实例后可以直接启动线程,启动后直接运行run方法。多线程就是有多个线程,主线程中有一个子线程也是多线程。
1. 线程的运行状态
新建状态:new 线程后,即创建线程对象后,但还没有启动的时候
就绪状态:线程调用了start()方法启动线程,就处于就绪状态了,此时等待cpu的调度,获得周期线程就可以运行了
运行状态:获得周期线程,处于运行中,本状态下一步可能进入阻塞状态或死亡状态
阻塞状态:该状态下无法运行,满足条件后继续进入运行状态进入阻塞状态的情况
线程等待输入输出操作,操作完成前不会反悔调用者
线程调用了wait()方法或sleep()方法
线程需要满足某种条件才会继续运行
死亡状态:线程退出run方法就处于死亡状态。
2. 获取正在运行的线程名字
Thread类有私有的String类型的属性保存该线程的名字,当我们没有设置线程名称时在线程类内我们可以通过getName()获取,获取的名称是Thread-0 1 2,而在 主线程的地方通过线程类的静态方法Thread.currentThread()获取当前运行的线程对象,再通过getName()获取。Thread类型的构造方法中有可以传递名字的构造方法。(注意构造方法不能被继承,自己定义的线程类,若传递姓名则写自己的构造方法,将参数通过super(“”)传递给父类)
3. 线程的优先级
线程的执行顺序是一种抢占方式,高优先级的比低优先级的获得的执行时间更多,不会对运行顺序有影响。
通过线程的setPriority()设置器优先级,其参数是1-10之间的整数:MIN_PRIORITY=1:最低优先级,MAX_PRIORITY=10:最高优先级,NORM_PRIORITY=5:默认优先级
4. 线程的休眠和唤醒
线程的休眠:指线程处于阻塞等待的状态,停止运行,可通过seleep()实现,该方法使线程在指定时间内处于阻塞状态,时间到后进入运行状态
线程的唤醒:使线程从阻塞状态进入运行状态,结束休眠状态,会执行sleep的捕获异常中的catch中的代码
Sleep()线程休眠:线程暂时休眠,带锁休眠,休眠后其他的线程仍然不能进入。到时间后自动唤醒。
Wait()也是让线程终止执行,不过wait会释放锁进入wait池,只能通过notify()进行唤醒
5.线程让步:使当前线程退出一次运行状态,其他线程谁抢占到谁运行,通过方法yield()方法
6. 线程的同步
线程是通过抢占实现的,优先抢占到的优先运行,若一个线程运行到一半就被另一个线程抢占了,而他们操作的变量相同,第一个线程运行仍然是以他运行到一半的地方开始,而另一个线程对数据的操作可能造成了数据的重复修改,造成数据的不准确。当对同一个变量进行操作时多用同步,使对这个变量的操作在同一时间只有一根线程执行。(例子:两个子线程同时操作一个变量值是2,第一个线程是对变量的数据-1在放回,第二个线程是对变量*2再放回去,当执行时,第一个线程取了数据-1,而此时第二个线程执行取数据则取回的是还没有修改的数据,后线程一将数据放回去,此时变量值是1,此时线程二执行变量值是4,此时则一个变量有了两个值。)
在java中的每个对象中都包含一把锁(也叫监视器),它自动成为对象的一部分(不需要写特殊的代码)。在给定时刻,只有一个线程可以拥有一个对象监视器,通过启动这把锁实现线程的同步,就实现了在同一时刻对某个变脸只有一个线程对其操作。通过 Synchronized(同步的)实现。
银行异步执行银行取款的数据错误实例:
public class MoneyThread extends Thread{
private Bank bank;
public MoneyThread(Bank bank) {
this.bank = bank;
}
@Override
public void run() {
System.out.println("取出:"+bank.getMoney(800)+"元");
}
}
public class Bank {
private int money;
public Bank(int money) {
this.money = money;
}
public int getMoney(int number){
if(number<0){
return -1;
}else if(number>money){
return -2;
}else{
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
money-=number;
return number;
}
}
}
public class MoneyThread extends Thread{
private Bank bank;
public MoneyThread(Bank bank) {
this.bank = bank;
}
@Override
public void run() {
System.out.println("取出:"+bank.getMoney(800)+"元");
}
}
实现方式:使用Synchronized(同步的)它用来修饰一个方法或者一个代码块的时候 ,在方法前面添加Synchronized变成同步方法,同步方法在同一时间只能被一个线程调用,调用完成后才会释放锁,被其他线程调用。当被锁状态时其他线程调用,则会进入等待状态,等待其他线程执行完后再对本线程上锁,执行。能够保证在同一时刻最多只有一个线程执行该段代码。加锁注意锁定的方法是操作变量的方法,锁定的对象在操作变量的对象。
同步块:
通过Synchronized实现,相当于对线程上了一把锁,只有线程执行完后才能其他线程抢占执行,
使用
Synchronized(obj){
代码段
}
同步化方法
就是对整个方法进行同步,他是对某个方法进行设置同步,只有该方法执行完后才能执行其他的方法
使用
在方法中添加synchronized
Synchronized void FunctionName(){
}
线程的创建方式
1. 继承runnable接口:更加好用,实现该接口后还可以继续继承类,增加了扩展性
2. 继承Thread类:java是单继承的,只能继承一个类。
publicclassThreadOneimplementsRunnable {
@Override
public void run() {
//线程中运行的方法
for(int i=0;i<10;i++){
System.out.println("这是继承Runable接口");}}}
public classThreadTwo extendsThread {
@Override
public void run() {
super.run();
for(int i=0;i<10;i++){
System.out.println("这是继承Thread类,Thread类也实现了Runable接口可以重写run接口");
}}}
public classFileUse{
public static void main(String[] args) {
Thread one=newThread(new ThreadOne());
one.start();//开启线程
ThreadTwotwo=newThreadTwo();
two.start();
for(int i=0;i<10;i++){
System.out.println("这是主线程运行的结果");
}
}
}
同步的实现
(1)同步方法:
就是银行的类中的取款的方法进行加锁,就实现了在同一时刻只有一个线程执行取款的操作,就不会出现数据错误的问题了:
public classBank {
private int money;
public Bank(int money) {
this.money = money;
}
public synchronized int getMoney(int number){
if(number<0){
return -1;
}else if(number>money){
return -2;
}else{
try {
Thread.sleep(1000);
}catch(InterruptedException e) {
e.printStackTrace();
}
money-=number;
return number;
}
}
}
(2)对代码块加同步锁则代码块中的规定的对象在同一时刻只有一个方法执行,则该代码块在同一时刻只有一个线程执行,其中锁定的对象可以是任意对象,当该对象被锁定时,另一个线程过来看到对象已经被锁定了则回进行等待,等待对象解除锁定后在执行锁代码块中的代码。
public classBank {
private int money;
Objectobjecty=new Object();
public Bank(int money) {
this.money = money;
}
public int getMoney(int number){
//这就是一个代码块,其中的对象可以是当前类的对象即this,也可以像现在一样的任意定义的一个对象。
synchronized (objecty) {
if(number<0){
return -1;
}else if(number>money){
return -2;
}else{
try {
Thread.sleep(1000);
}catch(InterruptedException e) {
e.printStackTrace();
}
money-=number;
return number;
}
}
}
}