1、继承Thread
package First; public class MyThread extends Thread { public void run() { super.run(); System.out.println("mythread"); } }
package First; public class Test { public static void main(String[] args) { MyThread myThread = new MyThread(); myThread.start(); System.out.println("main"); } }
在使用多线程技术时,代码的运行结果与代码执行顺序或调用顺序是无关的。
package First; public class Test { public static void main(String[] args) { MyThread myThread = new MyThread(); myThread.start(); myThread.start(); System.out.println("main"); } }
多次执行start(),会出现java.lang.IllegalThreadStateException异常
package First; public class Test { public static void main(String[] args) { MyThread myThread = new MyThread(); myThread.run(); System.out.println("main"); } }
start()通知“”线程规划器“”此线程已经准备就绪,等待调用线程对象的run(),具有异步效果。如果直接调用run(),则是同步,从上到下顺序依次执行
执行start()顺序不代表线程启动的顺序。
2、实现runnable接口
package First; public class MyRunnable implements Runnable { @Override public void run() { System.out.println("运行中"); } }
package First; public class Run { public static void main(String[] args) { Runnable runnable = new MyRunnable(); Thread thread = new Thread(runnable); thread.start(); System.out.println("运行结束"); } }
Thread构造函数:
构造函数Thread(Runnable target)意味着不光可以传入runnable接口的对象,还可以传入Thread类的对象,这样做完全可以将一个Thread对象的run()方法交给其他线程进行调用
实例变量与线程安全
(1)不共享数据
package First; public class MyThread extends Thread { private int count = 5; public MyThread(String name) { super(); //设置线程名称 this.setName(name); } public void run() { super.run(); while(count>0) { count--; System.out.println("由"+this.currentThread().getName()+"计算,count="+count); } } }
package First; public class Test { public static void main(String[] args) { MyThread a = new MyThread("A"); MyThread b = new MyThread("B"); MyThread c = new MyThread("C"); a.start(); b.start(); c.start(); System.out.println("main"); } }
(2)共享数据
package First; public class MyThread extends Thread { private int count = 5; public void run() { super.run(); //此实例不要用for语句,因为使用同步后其他线程就得不到运行的机会了 count--; System.out.println("由"+this.currentThread().getName()+"计算,count="+count); } }
package First; public class Test { public static void main(String[] args) { MyThread myThread = new MyThread(); Thread a = new Thread(myThread,"A"); Thread b = new Thread(myThread,"B"); Thread c = new Thread(myThread,"C"); Thread d = new Thread(myThread,"D"); Thread e = new Thread(myThread,"E"); a.start(); b.start(); c.start(); d.start(); e.start(); System.out.println("main"); } }
非线程安全问题(随机):主要是指多个线程对同一个对象中的同一个实例变量进行操作时会出现值被更改、值不同步的情况,进而影响程序的执行流程
某些JVM中,i--的操作分成三步:
1)取得原有的i值
2)计算i-1
3)对i进行赋值
在这三个步骤中,如果有多个线程同时访问,那么一定会出现非线程安全问题
package First; public class MyThread extends Thread { private int count = 5; synchronized public void run() { super.run(); count--; System.out.println("由"+this.currentThread().getName()+"计算,count="+count); } }
程序改成上述,就不会出现问题了
一个非线程安全的例子:
package First; public class LoginServlet { private static String usernameRef; private static String passwordRef; public static void doPost(String username,String password) { try { usernameRef = username; if(username.equals("a")) { Thread.sleep(5000); } passwordRef = password; System.out.println("username="+usernameRef+" password="+password); } catch (Exception e) { // TODO: handle exception } } }
package First; public class ALogin extends Thread{ public void run() { LoginServlet.doPost("a", "aa"); } }
package First; public class BLogin extends Thread{ public void run() { LoginServlet.doPost("b", "bb"); } }
package First; public class Run { public static void main(String[] args) { ALogin a = new ALogin(); a.start(); BLogin b = new BLogin(); b.start(); } }
解决非线程安全问题使用synchronized
package First; public class LoginServlet { private static String usernameRef; private static String passwordRef; synchronized public static void doPost(String username,String password) { try { usernameRef = username; if(username.equals("a")) { Thread.sleep(5000); } passwordRef = password; System.out.println("username="+usernameRef+" password="+password); } catch (Exception e) { // TODO: handle exception } } }
留意i--与System.out.println()
println()与i++联合使用时有可能出现另外一种异常情况
package First; public class MyThread extends Thread { private int i = 5; public void run() { //代码i--由前面项目中单独一行运行改成在当前项目中在println()方法中直接进行打印 System.out.println("i="+(i--)+" threadName="+Thread.currentThread().getName()); } }
package First; public class Run { public static void main(String[] args) { MyThread run = new MyThread(); Thread t1 = new Thread(run); Thread t2 = new Thread(run); Thread t3 = new Thread(run); Thread t4 = new Thread(run); Thread t5 = new Thread(run); t1.start(); t2.start(); t3.start(); t4.start(); t5.start(); } }
虽然println()内部是同步的,但i--的操作却是在进入println()之前发生的。所以为了防止非线程安全问题,还是应该继续使用同步方法
currentTread()方法
package First; public class MyThread extends Thread { public MyThread() { System.out.println("构造方法的打印: "+Thread.currentThread().getName()); } public void run() { System.out.println("run方法打印:"+Thread.currentThread().getName()); } }
package First; public class Run { public static void main(String[] args) { MyThread myThread = new MyThread(); myThread.start(); } }
package First; public class Run { public static void main(String[] args) { MyThread myThread = new MyThread(); //myThread.start(); myThread.run(); } }
由第一个实验可以看出:Mythread的构造函数是被main函数调用的,而run()方法是被名称为Thread-0调用的
由第二个实验可以看出:直接调用run()的话,则调用者名称为main
package First; public class CountOperate extends Thread { public CountOperate() { System.out.println("CountOperation---begin"); System.out.println("Thread.currentThread().getName()="+Thread.currentThread().getName()); System.out.println("this.getName()"+this.getName()); System.out.println("CountOperation---end"); } public void run() { System.out.println("CountOperation---begin"); System.out.println("Thread.currentThread().getName()="+Thread.currentThread().getName()); System.out.println("this.getName()"+this.getName()); System.out.println("CountOperation---end"); } }
package First; public class Run { public static void main(String[] args) { System.out.println(Thread.currentThread().getName()); CountOperate countOperate = new CountOperate(); Thread thread = new Thread(countOperate); thread.setName("A"); thread.start(); } }
isLive()
判断当前线程是否处于活跃状态
package First; public class MyThread extends Thread { public void run() { System.out.println("run="+this.isAlive()); } }
package First; public class Run { public static void main(String[] args) { MyThread myThread = new MyThread(); System.out.println("begin="+myThread.isAlive()); myThread.start(); System.out.println("end="+myThread.isAlive()); } }
注意最后一个println()的值是不确定的
package First; public class CountOperate extends Thread { public CountOperate() { System.out.println("CountOperation---begin"); System.out.println("Thread.currentThread().getName()="+Thread.currentThread().getName()); System.out.println("Thread.currentThread().isAlive()="+Thread.currentThread().isAlive()); System.out.println("this.getName()"+this.getName()); System.out.println("this.isAlive()"+this.isAlive()); System.out.println("CountOperation---end"); } public void run() { System.out.println("run---begin"); System.out.println("Thread.currentThread().getName()="+Thread.currentThread().getName()); System.out.println("Thread.currentThread().isAlive()="+Thread.currentThread().isAlive()); System.out.println("this.getName()"+this.getName()); System.out.println("this.isAlive()"+this.isAlive()); System.out.println("run---end"); } }
package First; public class Run { public static void main(String[] args) { CountOperate countOperate = new CountOperate(); Thread thread = new Thread(countOperate); System.out.println("main begin t1 isAlive="+thread.isAlive()); thread.setName("A"); thread.start(); System.out.println("main end t1 isAlive="+thread.isAlive()); } }
如果将线程对象以构造参数的方式传递给Thread对象进行start()启动时,运行的结果和前面实例的结果有些差异。造成这样的差异的原因还是来自于Thread.currentThread()和this之间的差异(具体解释参考:http://blog.csdn.net/yezis/article/details/57513130)
sleep()方法
是在指定的毫秒数内让当前“正在执行的线程”休眠(暂停执行)这个“正在执行的线程”是指this.currentThread()返回的线程
package First; public class MyThread extends Thread { public void run() { try { System.out.println("run threadName="+this.currentThread().getName()+" begin"); Thread.sleep(2000); System.out.println("run threadName="+this.currentThread().getName()+" end"); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
package First; public class Run { public static void main(String[] args) { MyThread myThread = new MyThread(); System.out.println("begin = " +System.currentTimeMillis()); myThread.run(); //myThread.start(); System.out.println("end = " +System.currentTimeMillis()); } }
package First; public class Run { public static void main(String[] args) { MyThread myThread = new MyThread(); System.out.println("begin = " +System.currentTimeMillis()); //myThread.run(); myThread.start(); System.out.println("end = " +System.currentTimeMillis()); } }
getId()方法
作用是获取线程的唯一标识
package First; public class Run { public static void main(String[] args) { Thread runThrad = Thread.currentThread(); System.out.println(runThrad.getId()); } }
停止线程
Thread.stop()是不安全的,已经被弃用
大多数使用Thread.interrupt(),但这个方法不会终止一个正在运行的线程,还需要加入一个判断才可以完成线程的停止
停不了的线程
调用interrupt仅仅是在当前线程中打了一个停止的标记,并不是真的停止线程
package First; public class MyThread extends Thread { public void run() { super.run(); for(int i = 0;i <50000;i++) { System.out.println("i"+(i+1)); } } }
package First; public class Run { public static void main(String[] args) { try { Thread myThread= new MyThread(); myThread.start(); Thread.sleep(2000); Thread.interrupted(); } catch (InterruptedException e) { System.out.println("main catch"); e.printStackTrace(); } } }
说明调用interrupt并没有停止线程
判断线程是否是停止状态
this.interrupted();是static方法,测试当前线程是否已经中断,当前线程是指运行this.interrupted()方法的线程
package First; public class Run { public static void main(String[] args) { try { Thread myThread= new MyThread(); myThread.start(); Thread.sleep(2000); Thread.interrupted(); System.out.println("是否停止1? = "+Thread.interrupted()); System.out.println("是否停止1? = "+Thread.interrupted()); } catch (InterruptedException e) { System.out.println("main catch"); e.printStackTrace(); } System.out.println("end"); } }
线程并未停止,这也说明了interrupted()方法的解释:测试当前线程是否已经中断。这个“当前线程”是main,它从未中断过,所以打印的是两false
package First; public class Run { public static void main(String[] args) { Thread.currentThread().interrupt(); System.out.println("是否停止1? = " + Thread.interrupted()); System.out.println("是否停止1? = " + Thread.interrupted()); System.out.println("end"); } }
为什么第2个布尔值是false呢?:
this.isInterrupted():方法不是static的
package First; public class Run { public static void main(String[] args) { try { Thread myThread= new MyThread(); myThread.start(); Thread.sleep(1000); Thread.interrupted(); System.out.println("是否停止1? = "+myThread.isInterrupted()); System.out.println("是否停止1? = "+myThread.isInterrupted()); } catch (InterruptedException e) { System.out.println("main catch"); e.printStackTrace(); } System.out.println("end"); } }
两者之间的区别:
能停止的线程-异常法(这两个实验我都没成功)
package First; public class MyThread extends Thread { public void run() { super.run(); for(int i = 0;i <50000;i++) { if(Thread.interrupted()) { System.out.println("已经是停止的状态,退出"); break; } System.out.println("i"+(i+1)); } } }
package First; public class Run { public static void main(String[] args) { try { Thread myThread= new MyThread(); myThread.start(); Thread.sleep(2000); Thread.interrupted(); } catch (InterruptedException e) { System.out.println("main catch"); e.printStackTrace(); } System.out.println("end"); } }
虽然停止了线程,但如果for语句下面还有语句,还是会继续执行的
package First; public class MyThread extends Thread { public void run() { super.run(); try { for(int i = 0;i <50000;i++) { if(Thread.interrupted()) { System.out.println("已经是停止的状态,退出"); break; } System.out.println("i"+(i+1)); } System.out.println("我在for下面"); } catch (Exception e) { System.out.println("进入MyThread.java类run方法中的catch"); } } }
package First; public class Run { public static void main(String[] args) { try { Thread myThread= new MyThread(); myThread.start(); Thread.sleep(2000); Thread.interrupted(); } catch (InterruptedException e) { System.out.println("main catch"); e.printStackTrace(); } System.out.println("end"); } }