线程
线程:
线程:一个线程可以看做是一个顺序流,它支持一个进程的运行,开启一个进程,有可能有一个或者多个线程运行启动
进程:在操作系统中每开启一个程序,都可以看做是一个进程
多线程:在同一个任务程序中可以有多个线程支持它运行
多进程:在操作系统中可以同时开启多个任务,一个任务代表一个进程
Java线程模型:
执行一个线程类 有java虚拟机虚拟出一个Cpu来,来处理线程类所做的操作,虚拟出来的cpu就可以对线程中数据或者代码进行运行,将对应的数据传递到线程对象中去,每次cpu执行的线程都是一个单一线程。
线程的创建有两种方法:
² 继承Thread类
此类实现了Runnable接口
Thread类的构造器:
构造方法摘要 |
|
Thread() |
|
Thread(Runnable target) |
|
Thread(Runnable target,String name) |
|
Thread(String name) |
|
Thread(ThreadGroup group,Runnable target) |
|
Thread(ThreadGroup group,Runnable target, String name) |
|
Thread(ThreadGroup group,Runnable target, String name, long stackSize) |
|
Thread(ThreadGroup group,String name) |
|
package com.ibm.thread;
public class FirstThread extends Thread {
public void run(){ //在run方法中输出1到10并且加上线程的名称 for(int i = 1;i<=10;i++){ System.out.println(this.getName()+":"+i); System.out.println(Thread.currentThread().getName()+":"+i); } }
public static void main(String[] args) { Thread tt = new FirstThread(); tt.start(); }
//1、启动一个线程调用的是start方法 //2、线程的命名默认为Thread-num //3、通过继承创建线程类的对象是将子类对象赋给了父类的引用 //4、Thread.currentThread()是取到当前线程 在当前线程中可以用this关键字代替 //5、run方法所做的操作就是线程体 //6、一个线程在创建之后处于可运行状态,当调用start方法之后,那么线程就从可运行状态到运行状态 ,当执行完run中的内容,那么线程就结束。 //7、不可以直接调用run方法来执行线程体里面的内容
}
|
*实现Runnable接口
创建线程需要实现Runnable接口,那么就应该将接口中未实现的方法实现过来,此接口中只有一个未实现的Run方法
package com.ibm.runnable;
public class FirstRunnableimplements Runnable{
//继承Runnable接口,并且添加未实现的方法 @Override public void run() { //在线程体中输出1到10 for(int i=1;i<=10;i++){ System.out.println(Thread.currentThread().getName()+":"+i); } }
//创建线程类的对象,启动线程 public static void main(String[] args) { //创建一个接口的实现类的对象 FirstRunnable fr = new FirstRunnable(); //将接口的实现类的对象赋给接口的引用并传递到Thread的构造器中去 Thread tt = new Thread(fr,"线程一"); tt.start(); }
//1、创建线程需要实现Runnable接口 //2、需要实现接口中的run方法 //3、创建线程类的对象调方法 //4、Thread类中构造器传递的字符串就是线程的名字
//继承Thread类与实现Runnable的优缺点? //继承thread的优点:1、创建线程类对象时编写简单、可以直接使用this关键字 //缺点:继承thread类之后,那么此类就不能继承其他类 //实现Runnable接口的优点:1、可以继承其他类 //2、将cpu和代码数据分开形成清晰的模型 //实现多线程的时候一般情况下都是实现runnable接口,可以在方法中直接传递接口的引用, //每一个实现此接口的对象都可以传递到此方法中来 //如果是继承提thread类,那么就必须创建thread类的对象,传递的必须是thread类的对象,灵活性不高 //3、都要实现runnable接口,实现run方法 //缺点:不能使用this关键字
//创建线程类对象的不同? //继承thread类创建对象 thread 对象名 = new 子类构造(); 对象名.start(); //实现runnable接口 创建对象 实现类 实现类的对象名 = new 实现类的构造器(); //Thread thread类对象名 = new Thread(实现类的对象名) //thread类对象名.start();
}
|
结束线程:怎么判断一个线程结束了
1. 线程中线程体的操作执行完成,在执行过程中不出任何异常则执行完成
2. 线程执行过程中线程结束或者线程出现错误则线程停止
package com.ibm.runnable;
public class OutThread implements Runnable{
@Override public void run() { for(int i =1;i<=50;i++){ System.out.println(i); //当i输出到20的时候,我让程序退出 if(i==20){ //当i=20时程序退出,线程结束 System.exit(0); } }
}
public static void main(String[] args) { OutThread ot = new OutThread(); Thread tt = new Thread(ot); tt.start(); }
}
|
当i=20时,让程序退出,那么开启的输出1到20的线程就退出了,不会输出20之后的i的值
后台线程:
如果我们同时启动多个线程,那么可以让其中的某一个线程或者多个线程设置为后台线程,那么主线程结束,后台线程也跟着结束,后台线程依附于主线程运行
|
线程的join方法
当一个线程正在执行的时候,那么在 某个条件下,可以让另一个线程B加入进来执行,但是线程A就需要停止,然后执行线程B,当线程B执行完之后,在接着执行A里面的内容
|
线程的isAlive方法:
package com.ibm.control;
public class IsAliveDemo extends Thread{
public void run(){ for(int i=0;i<10;i++){ if(i==5){ System.out.println(this.getName()+"状态:"+this.isAlive()); } if(i==10){ System.out.println(this.getName()+"状态:"+this.isAlive()); } }
}
public static void main(String[] args) { Thread tt=new IsAliveDemo(); System.out.println("线程启动前:"+tt.isAlive()); tt.start(); System.out.println("线程启动后:"+tt.isAlive()); }
//isAlive方法是用来判断一个线程是否是处于运行状态,在线程启动之前,或者线程 //执行完成后,那么线程都处于结束状态,那么isAlive方法返回false,如果线程启动 //后并且在线程的执行过程中线程的isAlive都是处于可运行状态,方法返回true }
|
线程让步(Yield方法)
|
创建一个以时间命名的文件存放在指定的目录中
package com.ibm.control;
import java.io.File; import java.io.IOException; import java.text.SimpleDateFormat; import java.util.Date;
public class SleepDemo02 extends Thread{
public void run(){ for (int i = 0; i < 10; i++) { try { sleep(5*1000); show(); } catch (InterruptedException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } }
public void show() throws IOException{ //创建一个以时间命名的文件存放在制定的目录中 File file = new File("d:\\aa"); Date date =new Date(); SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd hh-mm-ss-SSS"); String datename = sdf.format(date); String filename = datename+".js"; //拼接生成文件的完整路径 String str = file.getPath(); String name = str+"\\"+filename; File newFile = new File(name); if(!newFile.exists()){ newFile.createNewFile(); } }
public static void main(String[] args)throws IOException { new SleepDemo02().start(); } } |
线程优先级
最小值,最大值
package com.ibm.control;
public class PriorityDemo01 extends Thread{
public void run(){ //输出线程的优先级的值 System.out.println("PriorityDemo01线程:"+this.getPriority()); //输出线程的最大值 System.out.println("线程优先级最大值:"+Thread.MAX_PRIORITY); System.out.println("线程优先级最小值:"+Thread.MIN_PRIORITY); }
public static void main(String[] args) { System.out.println("main线程:"+Thread.currentThread().getPriority()); //启动PriorityDemo01线程 new PriorityDemo01().start();
}
}
|
两个线程设置优先级作比较
package com.ibm.control;
public class PriorityDemo02 extends Thread {
public void run() { for (int i = 0; i < 500; i++) { System.out.println(this.getName() +" " + i); } }
}
|
|
package com.ibm.control;
public class PriorityDemo03 extends Thread {
public void run() { for (int i = 0; i < 500; i++) { System.out.println(this.getName() +" " + i); } }
}
|
wait()、 notify()、notifyAll()
wait() 使线程处于等待状态,那么此线程需要线程监视器来唤醒等待的线程接着执行
notify() 唤醒单个的线程使线程从等待状态转变成运行状态
notifyAll() 唤醒全部处于等待的线程
|
关键字synchronized :
来与对象的互斥锁联系。当某个对象用synchronized修饰时,表明该对象在任一时刻只能由一个线程访问。
|
|
多线程编程知识概括
使用synchronized关键字修饰一个对象时,表明此对象在任意时刻只能有一个线程来操作此对象,在操作对象时,其他线程处于等待状态,只有等待操作的线程执行完毕之后,那么其他的线程才能够操作此对象。
释放对象锁的条件:
² 当线程执行到synchronized()块结束时,释放对象锁。
² 当在synchronized()块中遇到break,return或抛出exception,则自动释放对象锁。
² 当一个线程调用wait()方法时,它放弃拥有的对象锁并进入等待队列(按照优先级来说,等待线程高于给对象加锁)
死锁:
就是两个线程之间相互等待,都等待对象释放资源供自己使用,了但是在同一时刻,两个线程都不释放资源,那么两个线程就一直处于等待状态,那么线程的死锁就出现了。线程的死锁是可以采取操作避免出现的。
1. Wait(), notify() notifyall()都是出自于Object类
2. 调用wait()方法使线程处于等待状态,要想改变线程的等待状态,可以调用线程类的中断方法或者是调用object()类的notify()或者notifyAll方法
3. notify是用来唤醒单个线程的等待状态 notifyAll是用来唤醒多个线程等待状态
同步方法:指的是线程中调用方法时同步,具体的指明多个线程同时调用一个相同的方法,一般情况下,这样会造成同时执行操作会降低处理效率。
避免无谓的同步方法
因为同步会降低程序的执行效率,所以应该避免无谓的同步
通过所谓的Fine-Grained锁的机制,可以避免这种情况
多线程编程一般规则:
如果两个或两个以上的线程都修改一个对象,那么把执行修改的方法定义为被同步的,如果对象更新影响到只读方法,那么只读方法也要定义成同步的。
不要滥用同步。如果在一个对象内的不同的方法访问的不是同一个数据,就不要将方法设置为synchronized的。
如果一个线程必须等待一个对象状态发生变化,那么他应该在对象内部等待,而不是在外部。他可以通过调用一个被同步的方法,并让这个方法调用wait()。
每当一个方法返回某个对象的锁时,它应当调用notifyAll()来让等待队列中的其他线程有机会执行。
记住wait()和notify()/notifyAll()是Object类方法,而不是Thread类的方法。仔细查看每次调用wait()方法(等待方法),都有相应的notify()/notifyAll()方法(唤醒方法),且它们均作用于同一个对象。
多线程编程一般规则(con.)
a) 针对wait()、notify()/notifyAll()使用旋锁(spin lock);
b) 优先使用notifyAll()而不是notify();
c) 按照固定的顺序获得多个对象锁,以避免死锁;避免同时对对象进行加锁
d) 不要对上锁的对象改变它的引用;
e) 不要滥用同步机制,避免无谓的同步控制。