Java多线程
Java进程与线程
进程是程序的一次动态执行过程,它经历了从代码加载、执行到执行完毕的一个完整过程,这个过程也是进程本身从产生、发展到最终消亡的过程。
多进程操作系统能同时运行多个进程(程序),由于CPU具备分时机制,所以每个进程都能循环获得自己的CPU时间片。
多线程是指一个进程在执行过程中可以产生多个线程,这些线程可以同时存在、同时运行,一个进程可能包含了多个同时执行的线程。
比如JVM就是一个操作系统,每当使用java命令执行一个类时,实际上都会启动一个jvm,每一个JVM实际上就是在操作系统中启动一个进程,java本身具备了垃圾回收机制,所以每个java运行时至少会启动两个线程,一个main线程,另外一个是垃圾回收机制。
Java中线程的实现
在Java中要想实现多线程代码有两种手段,一种是继承Thread类,另一种就是实现Runnable接口。
1.继承Thread类
class MyThread extends Thread{ private String name; public MyThread(String name) { //构造方法 super(); this.name = name; } public void run(){ //覆写Thread类中的run()方法 for (int i=0;i<10;i++){ System.out.println(name+"运行,i="+i); } } } public class Thread_demo { public static void main(String[] args) { // TODO 自动生成的方法存根 MyThread mt1 = new MyThread("线程A "); MyThread mt2 = new MyThread("线程B "); mt1.start(); mt2.start(); } }
输出的结果可能是A线程和B线程交替进行,哪一个线程对象抢到了CPU资源,哪个线程就可以运行,在线程启动时虽然调用的是start()方法,但是实际上调用的却是run()方法的主体
如果一个类通过Thread类来实现,那么只能调用一次start()方法,如果调用多次,则将会抛出"IllegalThreadStateException"异常。
2.实现Runnable接口
仍然要依靠Thread类完成启动,在Thread类中提供了public Thread(Runnable target)和public Thread(Runnable target,String name)两个构造方法。
这两个构造方法都可以接受Runnable的子类实例对象。
class MyThread_1 implements Runnable{ private String name; public MyThread_1(String name) { //构造方法 super(); this.name = name; } @Override public void run() { //覆写Thread类中的run()方法 // TODO 自动生成的方法存根 for (int i=0;i<10;i++){ System.out.println(name+"运行,i="+i); } } } public class Runnable_demo { public static void main(String[] args) { // TODO 自动生成的方法存根 MyThread_1 mt1 = new MyThread_1("线程A "); //实例化Runnable子类对象 MyThread_1 mt2 = new MyThread_1("线程B "); //实例化Runnable子类对象 Thread t1 = new Thread(mt1); //实例化Thread类对象 Thread t2 = new Thread(mt2); //实例化Thread类对象 t1.start(); //启动线程 t2.start(); //启动线程 } }
通过Thread和Runnable接口都可以实现多线程,其中Thread类也是Runnable接口的子类,但在Thread类中并没有完全地实现Runnable接口中的run()方法。
区别:如果一个类继承了Thread类,则不适合多个线程共享资源,而实现了Runnable接口,就可以方便地实现资源的共享。
如果在Thread子类覆盖的run方法中编写了代码,也为Thread子类对象传递了一个Runnable对象,线程运行的时候执行的是子类的run方法(匿名内部类对象的构造方法如何调用非默认构造方法)
class MyThread_2 implements Runnable{ private int ticket = 5; @Override public void run() { //覆写Thread类中的run()方法 // TODO 自动生成的方法存根 for (int i=0;i<10;i++){ if(ticket>0){ System.out.println("卖票:ticket="+ticket--); } } } } public class Runnable_demo2 { public static void main(String[] args) { // TODO 自动生成的方法存根 MyThread_2 mt = new MyThread_2(); //实例化Runnable子类对象 Thread t1 = new Thread(mt); //实例化Thread类对象 Thread t2 = new Thread(mt); //实例化Thread类对象 Thread t3 = new Thread(mt); //实例化Thread类对象 t1.start(); //启动线程 t2.start(); //启动线程 t3.start(); //启动线程 } }
在没有同步之前会出现下面这种情况
实现Runnable接口相对于继承Thread类来说,有下列优势:
<1>适合多个相同程序代码的线程去处理同一资源的情况
<2>可以避免由于Java的单继承特性带来的局限
<3>增强了程序的健壮性,代码能够被多个线程共享,代码和数据是独立的
线程的生命周期
<1>设计4个线程对象,两个线程执行减操作,两个线程执行加操作
class Operator{ private static int i; class Inc implements Runnable{ @Override public void run() { // TODO 自动生成的方法存根 for(int j=0;j<10;j++){ this.inc(); System.out.println(Thread.currentThread().getName()+",i="+i); } } public synchronized void inc(){ i++; } } class Dec implements Runnable{ @Override public void run() { // TODO 自动生成的方法存根 for(int j=0;j<10;j++){ this.dec(); System.out.println(Thread.currentThread().getName()+",i="+i); } } public synchronized void dec(){ i--; } } } public class Thread4_demo { public static void main(String[] args) { // TODO 自动生成的方法存根 Operator.Inc inc1 = new Operator().new Inc(); //实例化内部类对象 Operator.Inc inc2 = new Operator().new Inc(); //实例化内部类对象 Operator.Dec dec1 = new Operator().new Dec(); //实例化内部类对象 Operator.Dec dec2 = new Operator().new Dec(); //实例化内部类对象 Thread t1 = new Thread(inc1); //实例化Thread类对象 Thread t2 = new Thread(inc2); //实例化Thread类对象 Thread t3 = new Thread(dec1); //实例化Thread类对象 Thread t4 = new Thread(dec2); //实例化Thread类对象 t1.start(); t2.start(); t3.start(); t4.start(); } }
<2>设计一个生产电脑和搬运电脑类,要求生产出一台电脑就搬走一台电脑,如果没有新的电脑生产出来,则搬运工要等待新电脑产出;如果生产出的电脑没有搬走,则要等待电脑搬走之后再生产,并统计出生产的电脑数量。
class Computer { private String name = "未生产"; private boolean flag = true; public String getName() { return name; } public void setName(String name) { this.name = name; } public synchronized void set(String name){ //设置信息名称及内容 if(!flag){ //标志位为false,不可以生产,在这里等待取走 try{ super.wait(); //等待搬运者取走 }catch(InterruptedException e){ e.printStackTrace(); } } this.setName(name); //设置信息名称 System.out.println(this.getName()); //输出信息 try{ Thread.sleep(300); //加入延迟 }catch(InterruptedException e){ e.printStackTrace(); } flag = false; //标志位为true,表示可以取走 super.notify(); //唤醒等待线程 } public synchronized void get(){ //取得信息内容 if(flag){ //标志位为true,不可以取走 try{ super.wait(); //等待生产者生产 }catch(InterruptedException e){ e.printStackTrace(); } } try { Thread.sleep(300); //加入延迟 } catch (InterruptedException e) { e.printStackTrace(); } this.setName("已经搬运完毕"); System.out.println(this.getName()); //输出信息 flag = true; //修改标志位为true,表示可以生产 super.notify(); //唤醒等待线程 } } class producer implements Runnable{ //定义生产者线程 private Computer com = null; //保存Computer引用 public producer(Computer com) { //构造函数 super(); this.com = com; } @Override public void run() { int count = 0; // TODO 自动生成的方法存根 for(int i=0;i<10;i++){ this.com.set("已经生产完毕"); count++; } System.out.println("生产的电脑数量:"+count); } } class transfer implements Runnable{ //定义生产者线程 private Computer com = null; //保存Computer引用 public transfer(Computer com) { //构造函数 super(); this.com = com; } @Override public void run() { // TODO 自动生成的方法存根 for(int i=0;i<10;i++){ this.com.get(); } } } public class computer_transfer_demo { public static void main(String[] args) { // TODO 自动生成的方法存根 Computer c = new Computer(); producer pro = new producer(c); transfer tra = new transfer(c); new Thread(pro).start(); new Thread(tra).start(); } }
java多线程操作方法
取得和设置线程名称
class MyThread_1 implements Runnable{ //实现Runnable接口 private String name; // public MyThread_1(String name) { //构造方法 // super(); // this.name = name; // } @Override public void run() { //覆写Thread类中的run()方法 // TODO 自动生成的方法存根 for (int i=0;i<10;i++){ // System.out.println(name+"运行,i="+i); System.out.println(Thread.currentThread().getName()+",i="+i); //取出当前线程的名称 } } } public class Runnable_demo { public static void main(String[] args) { MyThread_1 mt1 = new MyThread_1(); //实例化Runnable子类对象 new Thread(mt1).start(); //系统自动设置线程名称 new Thread(mt1,"线程A").start(); //手工自动设置线程名称 new Thread(mt1,"线程B").start(); //手工自动设置线程名称 new Thread(mt1).start(); //系统自动设置线程名称 new Thread(mt1).start(); //系统自动设置线程名称 } }
手工设置线程名称 系统自动设置线程名称
判断线程是否启动
使用isAlive()方法来判断线程是否已经启动而且仍然在启动
class MyThread_1 implements Runnable{ //实现Runnable接口 private String name; public MyThread_1(String name) { //构造方法 super(); this.name = name; } @Override public void run() { //覆写Thread类中的run()方法 // TODO 自动生成的方法存根 for (int i=0;i<10;i++){ // System.out.println(name+"运行,i="+i); System.out.println(Thread.currentThread().getName()+",i="+i); //取出当前线程的名称 } } } public class Runnable_demo { public static void main(String[] args) { // TODO 自动生成的方法存根 MyThread_1 mt1 = new MyThread_1("线程A "); //实例化Runnable子类对象 MyThread_1 mt2 = new MyThread_1("线程B "); //实例化Runnable子类对象 Thread t1 = new Thread(mt1); //实例化Thread类对象 Thread t2 = new Thread(mt2); //实例化Thread类对象 System.out.println("线程开始执行之前-->"+t1.isAlive()); t1.start(); //启动线程 System.out.println("线程开始执行之后-->"+t1.isAlive()); t2.start(); //启动线程 } }
主线程有可能比其他线程先执行完
线程的强制运行
在线程操作中,可以使用join()方法让一个线程强制运行,线程强制运行期间,期间线程无法运行,必须等待此线程完成之后才可以继续执行。
线程的休眠
在程序中允许一个线程进行暂时的休眠,直接使用Thread.sleep()方法即可实现休眠
程序在执行的时候,每次的输出都会间隔500ms,达到了延时操作的效果。
Thread.sleep()方法要用try和catch语句包围
class Mythread implements Runnable{ //实现Runnable接口 @Override public void run() { //覆写Thread类中的run()方法 // TODO 自动生成的方法存根 for (int i=0;i<5;i++){ try{ Thread.sleep(500); //线程休眠 }catch (Exception e){} //需要异常处理 System.out.println(Thread.currentThread().getName()+",i="+i); //取出当前线程的名称 } } } public class ThreadSleep_demo { public static void main(String[] args) { // TODO 自动生成的方法存根 Mythread m = new Mythread(); new Thread(m,"线程").start(); } }
中断线程
当一个线程运行时,另外一个线程可以直接通过interrupt()方法中断其运行状态。
一个线程启动之后进入了休眠状态,原来是要休眠10s之后再继续执行,但是主方法在线程启动之后的2s之后就将其中断,休眠一旦中断之后将执行catch中的代码。
class Mythread_1 implements Runnable{ //实现Runnable接口 @Override public void run() { //覆写Thread类中的run()方法 // TODO 自动生成的方法存根 System.out.println("进入run方法"); try{ Thread.sleep(10000); //线程休眠 System.out.println("休眠完成"); }catch (Exception e){ //需要异常处理 System.out.println("休眠被终止"); return; //让程序返回被调用处 } System.out.println("run方法结束"); } } public class ThreadInterrupt_demo { public static void main(String[] args) { // TODO 自动生成的方法存根 Mythread_1 m = new Mythread_1(); Thread t = new Thread(m,"线程"); t.start(); try{ Thread.sleep(2000); //主线程2s之后再执行中断 }catch(Exception e){} t.interrupt(); } }
后台线程
在Java程序中,只要前台有一个线程在运行,则整个Java进程都不会消失,所以此时可以设置一个后台线程,这样即使Java进程结束了,此后台线程依然会继续执行。要想实现这样的操作,直接使用setDaemon()方法即可。
线程的优先级
在Java的线程中使用setPriority()方法可以设置一个线程的优先级,在Java的线程中一共有3种优先级。
class MyThread_1 implements Runnable{ //实现Runnable接口 private String name; // public MyThread_1(String name) { //构造方法 // super(); // this.name = name; // } @Override public void run() { //覆写Thread类中的run()方法 // TODO 自动生成的方法存根 for (int i=0;i<10;i++){ //System.out.println(name+"运行,i="+i); System.out.println(Thread.currentThread().getName()+",i="+i); //取出当前线程的名称 } } } public class Runnable_demo { public static void main(String[] args) { // TODO 自动生成的方法存根 MyThread_1 mt1 = new MyThread_1(); //实例化Runnable子类对象 MyThread_1 mt2 = new MyThread_1(); //实例化Runnable子类对象 MyThread_1 mt3 = new MyThread_1(); //实例化Runnable子类对象 Thread t1 = new Thread(mt1,"线程A"); /实例化Thread类对象 Thread t2 = new Thread(mt2,"线程B"); //实例化Thread类对象 Thread t3 = new Thread(mt3,"线程C"); //实例化Thread类对象 // System.out.println("线程开始执行之前-->"+t1.isAlive()); t1.setPriority(Thread.MIN_PRIORITY); t2.setPriority(Thread.NORM_PRIORITY); t3.setPriority(Thread.MAX_PRIORITY); t1.start(); //启动线程 // System.out.println("线程开始执行之前-->"+t1.isAlive()); t2.start(); //启动线程 t3.start(); //启动线程 // MyThread_1 mt1 = new MyThread_1(); //实例化Runnable子类对象 // new Thread(mt1).start(); //系统自动设置线程名称 // new Thread(mt1,"线程A").start(); //手工自动设置线程名称 // new Thread(mt1,"线程B").start(); //手工自动设置线程名称 // new Thread(mt1).start(); //系统自动设置线程名称 // new Thread(mt1).start(); //系统自动设置线程名称 } }
线程将根据优先级的大小来决定哪个线程会先运行,但是并非线程的优先级越高就一定会先执行,哪个线程先执行将由CPU的调度决定。
主方法的优先级是NORM,通过Thread.currentThread().getPriority()来取得主方法的优先级,结果是5
线程的礼让
在线程的操作中,可以使用yield()方法将一个线程的操作暂时让给其他线程执行。本线程暂停,让其他进程先执行。
class MyThread_1 implements Runnable{ //实现Runnable接口 private String name; // public MyThread_1(String name) { //构造方法 // super(); // this.name = name; // } @Override public void run() { //覆写Thread类中的run()方法 // TODO 自动生成的方法存根 for (int i=0;i<10;i++){ //System.out.println(name+"运行,i="+i); System.out.println(Thread.currentThread().getName()+",i="+i); //取出当前线程的名称 if(i==3){ System.out.println("线程礼让:"); Thread.currentThread().yield(); //线程礼让 } } } } public class Runnable_demo { public static void main(String[] args) { // TODO 自动生成的方法存根 MyThread_1 mt1 = new MyThread_1(); //实例化Runnable子类对象 MyThread_1 mt2 = new MyThread_1(); //实例化Runnable子类对象 MyThread_1 mt3 = new MyThread_1(); //实例化Runnable子类对象 Thread t1 = new Thread(mt1,"线程A"); //实例化Thread类对象 Thread t2 = new Thread(mt2,"线程B"); //实例化Thread类对象 Thread t3 = new Thread(mt3,"线程C"); //实例化Thread类对象 // System.out.println("线程开始执行之前-->"+t1.isAlive()); t1.setPriority(Thread.MIN_PRIORITY); t2.setPriority(Thread.NORM_PRIORITY); t3.setPriority(Thread.MAX_PRIORITY); t1.start(); //启动线程 // System.out.println("线程开始执行之前-->"+t1.isAlive()); t2.start(); //启动线程 t3.start(); //启动线程 // MyThread_1 mt1 = new MyThread_1(); //实例化Runnable子类对象 // new Thread(mt1).start(); //系统自动设置线程名称 // new Thread(mt1,"线程A").start(); //手工自动设置线程名称 // new Thread(mt1,"线程B").start(); //手工自动设置线程名称 // new Thread(mt1).start(); //系统自动设置线程名称 // new Thread(mt1).start(); //系统自动设置线程名称 } }
线程礼让也是不一定的
本文只发表于博客园和tonglin0325的博客,作者:tonglin0325,转载请注明原文链接:https://www.cnblogs.com/tonglin0325/p/5252044.html