获取当前运行的线程的名称
public class Test { public static void main(String[] args) { System.out.println(Thread.currentThread().getName()); } }
得到一个结果main,但是这个main是线程的名字,和main方法没有什么关系,仅仅是名字相同而已
继承Thread类
实现多线程编程的方式主要有两种,一种是继承THread类,一种是实现Runnable接口
Thread实现了Runnable接口,它们之间具有多态关系
使用继承Thread的方式创建多线程的时候,最大的局限就是不支持多继承,因为java的特点就是单根继承
所以为了支持多继承,完全可以实现Runnable接口的方式,一边实现一边继承。
使用这两种方法创建的线程在工作室的性质是一样的,没有本质的区别
1,继承Thread类
新建一个MyTHread类
public class MyThread extends Thread { @Override public void run() { super.run(); System.out.println("MyThread"); } }
测试运行
public class Test { public static void main(String[] args) { MyThread mythread = new MyThread(); mythread.start(); System.out.println("运行结束"); } }
可能会运行处这样的结果:
可以发现run方法执行的时间比较晚
这也说明在使用多线程技术时,代码的运行结果与代码的执行顺序或者调用顺序是无关的。
线程是一个子任务,CPU以不确定的方式,或者说是以随机的时间来调用线程中的run方法,
所以就会出现先打印运行结束后输出MyTHread这样的结果了
演示线程的随机性:
public class MyThread extends Thread { @Override public void run() { try { for (int i = 0; i < 10; i++) { int time = (int) (Math.random() * 1000); Thread.sleep(time); System.out.println("run=" + Thread.currentThread().getName()); } } catch (InterruptedException e) { e.printStackTrace(); } } }
测试:
public class Test { public static void main(String[] args) { try { MyThread thread = new MyThread(); thread.setName("myTHread"); thread.start(); for(int i = 0; i < 10; i++) { int time = (int) (Math.random() * 1000); Thread.sleep(time); System.out.println("main=" + Thread.currentThread().getName()); } } catch (InterruptedException e) { e.printStackTrace(); } } }
另外,执行start()方法的顺序并不代表线程的启动顺序。
2.实现Runnable接口
public class MyRunnable implements Runnable { @Override public void run() { System.out.println("运行中!"); } }
使用方式:
public class Test { public static void main(String[] args) { Runnable runnable = new MyRunnable(); Thread thread = new Thread(runnable); thread.start(); System.out.println("运行结束!"); } }
Thread的构造可以传入一个Thread对象,也就是说,一个Thread的run方法可以交给其他的线程进行调用
实例变量与线程安全
自定义线程类中的实例变量针对其他线程可以有共享和不共享之分。
不共享数据的情况
public class MyThread extends Thread { private int count = 5; public MyThread(String name) { super(); this.setName(name); //设置线程的名称 } @Override public void run() { super.run(); while(count > 0) { count--; System.out.println("由 " + this.currentThread().getName() + " 计算, count=" + count); } } }
运行类:
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(); } }
共享数据的情况
共享数据的情况就是多个线程可以访问同一个变量,
比如实现投票功能的软件时,多个线程可以同时处理同一个人的票数
public class MyThread extends Thread { private int count = 5; @Override public void run() { super.run(); count--; System.out.println("由 " + this.currentThread().getName() + " 计算, count=" + count); } }
测试类:
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(); } }
多运行几次会发现,线程中产生了错误,某些线程同时对一个数据进行处理
导致结果产生了重复的
在某些jvm中,i--的操作分为三步:
1.取得原有i值
2.计算i-1
3.对i进行赋值
在执行这三个步骤中,如果有多个线程同时访问,那么就会出现非线程安全问题
要使多个线程进行同步,也就是按顺序执行,需要修改代码
public class MyThread extends Thread { private int count = 5; @Override synchronized public void run() { super.run(); count--; System.out.println("由 " + this.currentThread().getName() + " 计算, count=" + count); } }
synchronized关键字可以在任意对象以及方法上加锁,
而加锁的这段代码称为互斥区或临界区
当一个线程想要执行同步方法里面的代码时,首先会尝试拿到这把锁,
如果能拿到,就执行代码,如果拿不到,就不断地尝试拿到这把锁,直到能够拿到为止
非线程安全:
主要是指多个线程对同一对象中的同一个实例变量进行操作时会出现值被更改,值不同步的情况,
进而影响程序的执行流程。
i--与System.out.println()的异常
public class MyThread extends Thread { private int i = 5; @Override synchronized public void run() { super.run(); System.out.println("i=" + (i--) + " threadName=" + Thread.currentThread().getName()); } }
测试:
public class Test { 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(); } }
根据结果可以看出,System.out.println方法内部是同步的
但是i--的操作确实进入内部之前发生的,所以还是会有发生非线程安全带额概率
currentThread()方法
public class MyThread extends Thread { public MyThread() { System.out.println("构造方法的打印:" + Thread.currentThread().getName()); } @Override synchronized public void run() { System.out.println("run方法的打印" + Thread.currentThread().getName()); } }
测试:
public class Test { public static void main(String[] args) { MyThread mythread = new MyThread(); mythread.start(); //mythread.run(); } }
根据结果可以看出,构造方法是main线程调用的,
而run方法是一个叫做Thread-0的线程调用的,run方法是自动调用的
当然run方法也可以手动调用
比较复杂的情况:
public class CoutOperate extends Thread { public CoutOperate() { System.out.println("CoutOperate---begin"); System.out.println("Thread.currentThread().getName()=" + Thread.currentThread().getName()); System.out.println("this.getName()=" + this.getName()); System.out.println("CoutOperate---end"); } @Override public void run() { System.out.println("run---begin"); System.out.println("Thread.currentThread().getName()=" + Thread.currentThread().getName()); System.out.println("this.getName()=" + this.getName()); System.out.println("run---end"); } }
测试:
public class Test { public static void main(String[] args) { CoutOperate c = new CoutOperate(); Thread t1 = new Thread(c); t1.setName("A"); t1.start(); } }
结果:
isAlive()方法
此方法用来判断当前的线程是否处于活动状态
public class MyThread extends Thread { @Override synchronized public void run() { System.out.println("run=" + this.isAlive()); } }
测试:
public class Test { public static void main(String[] args) { MyThread mythread = new MyThread(); System.out.println("begin ==" + mythread.isAlive()); mythread.start(); System.out.println("end ==" + mythread.isAlive()); } }
结果:
活动状态的概念:
活动状态就是线程已经启动并且尚未终止。
线程处于正在运行或准备开始运行的状态,就认为是“存活”的
上面结果的第二行其实是不确定的,可以加一个sleep看一下
public class Test { public static void main(String[] args) throws InterruptedException { MyThread mythread = new MyThread(); System.out.println("begin ==" + mythread.isAlive()); mythread.start(); Thread.sleep(1000); System.out.println("end ==" + mythread.isAlive()); } }
结果就变成了:
在使用isAlive方法时,如果将线程对象以构造参数的方式传递给Thread对象,
接着进行start启动时,结果是会有差异的
public class CountOperate extends Thread { public CountOperate() { System.out.println("CountOperate---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("CountOperate---end"); } @Override 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"); } }
测试:
public class Test { public static void main(String[] args) throws InterruptedException { CountOperate c = new CountOperate(); Thread t1 = new Thread(c); System.out.println("main begin t1 isAlive=" + t1.isAlive()); t1.setName("A"); t1.start(); System.out.println("main end t1 isAlive=" + t1.isAlive()); } }
运行结果:
sleep()方法
这个方法的作用是在指定的毫秒数内让当前“正在执行的线程”休眠(暂停执行)。
这个“正在执行的线程”是指this.currentThread()返回的线程
示例:
public class MyThread extends Thread { @Override 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) { e.printStackTrace(); } } }
测试:
public class Test { public static void main(String[] args) throws InterruptedException { MyThread mythread = new MyThread(); System.out.println("begin =" + System.currentTimeMillis()); mythread.run(); System.out.println("end =" + System.currentTimeMillis()); } }
结果:
另一个实验:
public class MyThread extends Thread { @Override public void run() { try { System.out.println("run threadName=" + this.currentThread().getName() + "begin =" + System.currentTimeMillis()); Thread.sleep(2000); System.out.println("run threadName=" + this.currentThread().getName() + "end =" + System.currentTimeMillis()); } catch (InterruptedException e) { e.printStackTrace(); } } }
测试:
public class Test { public static void main(String[] args) throws InterruptedException { MyThread mythread = new MyThread(); System.out.println("begin =" + System.currentTimeMillis()); mythread.start(); System.out.println("end =" + System.currentTimeMillis()); } }
运行结果:
getId()方法
这个方法用来获取线程的唯一标识
停止线程
停止一个线程意味着在线程处理完任务之前停掉正在做的操作,也就是放弃当前的操作。
停止一个线程可以使用Thread.stop()方法,但这是不安全的。
大多数停止一个线程的操作使用Thread.interrupt()方法,
尽管方法名是停止的意思,但是这个方法不会终止一个正在运行的线程,还需要加入一个判断才可以完成线程的停止。
在java中有三种方法可以终止正在运行的线程:
1.使用退出标志,是线程正常退出,也就是当run方法完成后线程终止
2.使用stop方法强行终止线程
3.使用interrupt方法中断线程
interrupt()方法
public class MyThread extends Thread { @Override public void run() { super.run(); for(int i = 0; i < 500000; i++) { System.out.println("i=" + (i + 1)); } } }
测试代码:
public class Test { public static void main(String[] args) { try { MyThread thread = new MyThread(); thread.start(); Thread.sleep(2000); thread.interrupt(); } catch (InterruptedException e) { e.printStackTrace(); } } }
将输出结果粘贴出来,发现是五万行
也就是说,调用interrupt()方法并没有停止线程
判断线程是否是停止的状态:
this.interrupted():测试当前线程是否已经中断
this.isInterrupted:测试线程是否已中断
public class MyThread extends Thread { @Override public void run() { super.run(); for(int i = 0; i < 500000; i++) { System.out.println("i=" + (i + 1)); } } }
测试代码:
public class Test { public static void main(String[] args) { try { MyThread thread = new MyThread(); thread.start(); Thread.sleep(1000); thread.interrupt(); //Thread.currentThread().interrupt(); System.out.println("是否停止1? =" + thread.interrupted()); System.out.println("是否停止2? =" + thread.isInterrupted()); } catch (InterruptedException e) { e.printStackTrace(); } } }