一、实现多线程的方式 --继承Thread 和 实现Runnable接口
1.继承Thread类
Thread结构: public class Thread implements Runnable. Thread实现了Runnable 两者之间具有多态的关系。
局限性: 不支持多继承。 但是这两种方式创建的线程在工作时的性质是一样的。
例1:
public class MyThread extends Thread{
@Override
public void run() {
super.run();
System.out.println("MyThread");
}
}
public class Run {
public static void main(String[] args) {
MyThread m = new MyThread();
m.start();
System.out.println("运行结束");
}
}
运行结果: 运行结束
运行中
注:从代码运行结果可看出 代码运行结果与执行顺序或调用顺序无关。
线程是一个子任务,CPU随机调用。
执行start()方法的顺序性也不代表线程的启动顺序。
2.实现Runnable 接口
解决java不能多继承的局限性
例1:
public class MyRunnable implements Runnable{
@Override
public void run() {
System.out.println("运行中");
}
}
public class Run {
public static void main(String[] args) {
Runnable r = new MyRunnable();
Thread t = new Thread(r);
t.start();
System.out.println("运行结束");
}
}
运行结果: 运行结束
运行中
从以上可以看出: 使用Thread和Runnable的执行效果是一样的。
3. 实例变量与线程安全
自定义线程类的实例变量可共享也可以不共享
(1) 不共享
例1:
public class NoSharedVariable extends Thread{
private int count = 5;
public NoSharedVariable(String name) {
super();
this.setName(name);
}
@Override
public void run() {
super.run();
while (count > 0) {
count--;
System.out.println("由" + Thread.currentThread().getName() + "计算. count=" + count);
}
}
}
调用:
package com.ljx.thread;
public class NoSharedVariableRun {
public static void main(String[] args) {
NoSharedVariable a = new NoSharedVariable("A");
NoSharedVariable b = new NoSharedVariable("B");
NoSharedVariable c = new NoSharedVariable("B");
a.start();
b.start();
c.start();
}
}
运行结果: 由B计算. count=4
由B计算. count=4
由A计算. count=4
由B计算. count=3
由B计算. count=3
由B计算. count=2
由A计算. count=3
由B计算. count=1
由A计算. count=2
由B计算. count=2
由A计算. count=1
由B计算. count=0
由A计算. count=0
由B计算. count=1
由B计算. count=0
由运行结果可知: 每个线程都有一个自己的计数器, 互不共享。
(2)共享数据
不安全情况:
public class SharedVariable extends Thread {
private int count = 5;
@Override
public void run() {
super.run();
count--;
System.out.println("由" + Thread.currentThread().getName() + "计算. count=" + count);
}
}
public class SharedVariableRun {
public static void main(String[] args) {
SharedVariable myThread = new SharedVariable();
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();
}
}
运行结果:
由A计算. count=2
由B计算. count=0
由E计算. count=1
由D计算. count=2
由C计算. count=2
如运行结果所示:我们要的结果是 5 4 3 2 1依次递减的,但是如A D C 结果为一样的,
说明其同时对count进行处理 造成了非线程安全
解决方法: 在run方法前加一个同步锁 synchronized,一次只允许一个线程调用,防止同时调用
synchronized public void run();
4.currentThread()方法
可以返回代码段正在被哪个线程调用的信息。以上例子有体现,不再赘述
5.isAlive()方法 判断当前线程是否处于活动状态
例:
public class AliveThread extends Thread{
@Override
public void run() {
System.out.println("run=" + this.isAlive());
}
}
public class AliveRun {
public static void main(String[] args) {
// TODO Auto-generated method stub
AliveThread a = new AliveThread();
System.out.println("begin ==" + a.isAlive());
a.start();
System.out.println("end ==" + a.isAlive());
}
}
运行结果:
begin ==false
end ==true
run=true
活动状态: 指线程已经启动且尚未终止。线程处于正在运行或准备开始运行的状态,就认为线程是'存活'的
注意:System.out.println("end ==" + a.isAlive());==true 此值是不确定的 打印true是因为线程还未执行完毕就执行了
6.sleep() 方法
其作用是在指定的毫秒数内让"正在执行的线程"休眠(暂停执行)-- this.currentThread()返回的线程
例: 此后为省篇幅只贴上方法
@Override
synchronized public void run() {
try {
System.out.println("begin-- " + System.currentTimeMillis());
Thread.sleep(2000);
System.out.println("end-- " + System.currentTimeMillis());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
运行结果: begin-- 1577676349044
end-- 1577676351044 差两千毫秒
7.getID() 取得线程的唯一标识
例子:
public static void main(String[] args) {
Thread t = Thread.currentThread();
System.out.println(t.getId());
}
运行结果:1
8.停止线程
三种方法:
(1)使用退出标志,使线程正常退出,当run方法完成后线程终止
(2) 使用stop() 强行终止线程,不推荐 stop suspend resume 都是作废过期的方法
(3) 使用interrupt方法中断线程
8.1 停止不了的线程
例子:
@Override
public void run() {
super.run();
for (int i = 0; i < Integer.MAX_VALUE; i++) {
System.out.println("第" + i + "打印");
}
}
public static void main(String[] args) {
try {
InterruptThread i = new InterruptThread();
i.start();
Thread.sleep(2000);
i.interrupt();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
运行结果:
第204867937打印
第204867938打印
第204867939打印
...............
停止不了
8.2判断线程是否是停止状态
(1)this.interrupted();测试当前线程是否已经中断 执行后将状态标志清除为false
(2) this.isInterrupted();测试线程是否已经中断 不清除状态标志
8.3能停止的线程————异常法
例:
@Override
public void run() {
super.run();
for (int i = 0; i < Integer.MAX_VALUE; i++) {
if (this.isInterrupted()) {//如果线程状态停止跳出循环
System.out.println("停止运行!");
break ;
}
System.out.println("第" + i + "打印");
}
System.out.println("此处还可以继续执行");
}
public static void main(String[] args) {
try {
StopThreadWithException stop = new StopThreadWithException();
stop.start();
Thread.sleep(1000);
stop.interrupt();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
运行结果: ......
第174268打印
第174269打印
第174270打印
停止运行!
此处还可以继续执行
以上结果显示: 线程结束后还会继续执行
解决办法:
@Override
public void run() {
super.run();
try {
for (int i = 0; i < Integer.MAX_VALUE; i++) {
if (this.isInterrupted()) {//如果线程状态停止跳出循环
System.out.println("停止运行!");
throw new InterruptedException();
}
System.out.println("第" + i + "打印");
}
System.out.println("此处还可以继续执行");
} catch(InterruptedException e) {
System.out.println("捕获到异常");
}
}
在run方法中 进行手动抛出中断异常
运行结果: ......
第173703打印
第173704打印
停止运行!
捕获到异常
8.4 在沉睡中停止
例:
@Override
public void run() {
super.run();
try {
System.out.println("begin----");
Thread.sleep(200000);
System.out.println("end------");
} catch (InterruptedException e) {
System.out.println("捕获到异常" + this.isInterrupted());
}
}
public static void main(String[] args) {
try {
SleepInterruptThread si = new SleepInterruptThread();
si.start();
Thread.sleep(200);
si.interrupt();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
运行结果:
begin----
捕获到异常false
如果在沉睡中被停止:就会遇到中断异常 并清除中断标志为false
中断遇到中止
@Override
public void run() {
super.run();
try {
for (int i = 0; i < 10; i++) {
System.out.println("第" + i + "打印");
}
System.out.println("begin----");
Thread.sleep(200);
System.out.println("end------");
} catch (InterruptedException e) {
System.out.println("捕获到异常" + this.isInterrupted());
}
}
public static void main(String[] args) {
SleepInterruptThread si = new SleepInterruptThread();
si.start();
si.interrupt();
}
8.5 暴力停止
例子:
private int i = 0;
@Override
public void run() {
try {
while(true) {
i++;
System.out.println("第" + i + "打印");
Thread.sleep(1000);
}
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public static void main(String[] args) {
try {
ViolenceStopThread v = new ViolenceStopThread();
v.start();
v.sleep(9000);
v.stop();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
运行结果:......
第7打印
第8打印
第9打印
在第九次的时候被暴力停止 注意不建议使用此方法!
8.6 方法stop()与 ThreadDeath异常
public void run() {
try {
System.out.println("停止前");
this.stop();
System.out.println("停止后");
} catch (ThreadDeath e) {
System.out.println("报ThreadDeath异常了");
}
}
public static void main(String[] args) {
ThreadDeathException t = new ThreadDeathException();
t.start();
}
运行结果:
停止前
报ThreadDeath异常了
8.7释放锁的不良后果
例子:
synchronized public void printStr(String username, String password) {
try {
this.username = username;
Thread.sleep(100000);
this.password = password;
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public class SynchronizedObjectThread extends Thread {
private SynchronizedObject object;
public SynchronizedObjectThread(SynchronizedObject object) {
super();
this.object = object;
}
@Override
public void run() {
object.printStr("b", "bb");
}
}
public class SynchronizedObjectThreadRun {
public static void main(String[] args) {
try {
SynchronizedObject obj = new SynchronizedObject();
SynchronizedObjectThread objThread = new SynchronizedObjectThread(obj);
objThread.start();
Thread.sleep(500);
objThread.stop();
System.out.println(obj.getUsername() + " " + obj.getPassword());
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
输出结果:b aa
8.8使用return 停止线程
@Override
public void run() {
while(true) {
if (this.isInterrupted()) {
System.out.println("停止了!");
return;
}
System.out.println("timer" + System.currentTimeMillis());
}
}
public static void main(String[] args) {
try {
StopThreadWithReturn s = new StopThreadWithReturn();
s.start();
Thread.sleep(1000);
s.interrupt();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
运行结果:.....
timer1577845081443
timer1577845081443
timer1577845081443
停止了!
建议使用异常停止线程 因为异常可以向上抛
9.暂停线程
9.1 suspend与resume方法的使用
suspend: 暂停
resume: 唤醒
不推荐使用 suspend() 去挂起线程的原因,是因为 suspend() 在导致线程暂停的同时,并不会去释放任何锁资源。其他线程都无法访问被它占用的锁。直到对应的线程执行 resume() 方法后,被挂起的线程才能继续,从而其它被阻塞在这个锁的线程才可以继续执行。
SuspendAndResumeThread thread = new SuspendAndResumeThread();
thread.start();
Thread.sleep(5000);
//A段
thread.suspend();
System.out.println("A= " + System.currentTimeMillis() + " i=" + thread.getI());
Thread.sleep(5000);
System.out.println("A= " + System.currentTimeMillis() + " i=" + thread.getI());
//B段
thread.resume();
Thread.sleep(5000);
System.out.println("B= " + System.currentTimeMillis() + " i=" + thread.getI());
System.out.println("B= " + System.currentTimeMillis() + " i=" + thread.getI());
运行结果:
A= 1577850949485 i=2544173612
A= 1577850954486 i=2544173612
B= 1577850954486 i=2544177579 详情请看B
B= 1577850959486 i=5097346394
C= 1577850959486 i=5097406539
C= 1577850964487 i=5097406539
9.2 suspend与resume的缺点---独占
9.1有简略说道 suspend与resume的缺点 下面详细说明
例子:
public class SRSynchronizedObject {
synchronized public void printString() {
System.out.println("begin");
if (Thread.currentThread().getName().equals("a")) {
System.out.println("a 线程永远suspend了");
Thread.currentThread().suspend();
System.out.println("end");
}
System.out.println("你猜猜你能不能见到这条消息?");
}
}
public static void main(String[] args) {
try {
final SRSynchronizedObject object = new SRSynchronizedObject();
Thread thread1 = new Thread() {
@Override
public void run() {
object.printString();
}
};
thread1.setName("a");
thread1.start();
Thread.sleep(1000);
Thread thread2 = new Thread() {
@Override
public void run() {
System.out.println("线程2");
object.printString();
System.out.println("线程2你猜猜能不能进来?");
}
};
thread2.start();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
运行结果:
begin
a 线程永远suspend了
线程2
线程a被永远挂起 没办法执行了 得等到其它线程唤醒它
9.3 suspend与resume的缺点---未同步
例子:
private String username = "1";
private String password = "11";
public void setVlaue(String u, String p) {
this.username = u;
if (Thread.currentThread().getName().equals("a")) {
System.out.println("停止a线程");
Thread.currentThread().suspend();
}
this.password = p;
}
public void printStr() {
System.out.println(username + " " + password);
}
public static void main(String[] args) throws InterruptedException {
final OutOfSyncThread o = new OutOfSyncThread();
Thread thread1 = new Thread() {
public void run() {
o.setVlaue("a", "aa");
}
};
thread1.setName("a");
thread1.start();
Thread.sleep(500);
Thread thread2 = new Thread() {
public void run() {
o.printStr();
}
};
thread2.start();
}
运行结果: a 11
分析: 线程thread1运行到suspend 被挂起了 所以password并没有被赋值。
10. yield方法
作用:放弃当前的CPU资源,将它让给其它的任务去占用CPU执行时间。但是放弃的时间不确定 有可能刚刚放弃,马上又获得CPU时间段
例子:
@Override
public void run() {
long beginTime = System.currentTimeMillis();
int count = 0;
for (int i = 0; i < Integer.MAX_VALUE; i++) {
Thread.yield();
count = count + (i + 1);
}
long endTime = System.currentTimeMillis();
System.out.println("用时:" + (endTime - beginTime) + "毫秒!");
}
调用线程的运行结果:用时:167526毫秒!
分析:yield() 把cpu资源大方的让给其它任务啦 要对比请注释掉Thread.yield()试试
11.线程的优先级
优先级高的得到的CPU资源也比较多
设置线程优先级用setPriority();
java中线程的优先级分为1~10这10个等级 在这个氛围之外 jdk会抛出IllegalArgumentException();
三个常量:MIN_PRIORITY = 1; NORM_PRIORITY = 5; MAX_PRIORITY = 10;
11.1 优先级的继承特性
例子:
public class PriorityThread1 extends Thread {
@Override
public void run() {
System.out.println("PriorityThread1 priority=" + this.getPriority());
PriorityThread2 thread2 = new PriorityThread2();
thread2.start();
}
}
public class PriorityThread2 extends Thread {
@Override
public void run() {
System.out.println("PriorityThread2 priority=" + this.getPriority());
}
}
public static void main(String[] args) {
System.out.println("Main Thread begin priority=" + Thread.currentThread().getPriority());
Thread.currentThread().setPriority(6);
System.out.println("Main Thread end priority=" + Thread.currentThread().getPriority());
PriorityThread1 p1 = new PriorityThread1();
p1.start();
}
运行结果:
Main Thread begin priority=5
Main Thread end priority=6
PriorityThread1 priority=6
PriorityThread2 priority=6
分析:main线程启动p1线程 p1启动p2线程 他们的权限级别是一样的
11.2 优先级具有规则性
例子:
@Override
public void run() {
long beginTime = System.currentTimeMillis();
long addResult = 0;
for (int i = 0; i < 10; i++) {
for (int j = 0; j < 1000000; j++) {
Random random = new Random();
random.nextInt();
addResult = addResult + i;
}
}
long endTime = System.currentTimeMillis();
System.out.println("***** thread 1 use time" + (endTime - beginTime));
}
注: 其实中间的双重for循环没有什么作用 只是用来消耗时间而已 并让你更清楚的看运行顺序
public static void main(String[] args) {
for (int i = 0; i < 5; i++) {
PriorityFunctionThread1 p1 = new PriorityFunctionThread1();
p1.setPriority(10);
p1.start();
PriorityFunctionThread2 p2 = new PriorityFunctionThread2();
p1.setPriority(1);
p2.start();
}
}
运行结果:
***** thread 1 use time7240
***** thread 1 use time7754
***** thread 1 use time9869
***** thread 1 use time9922
***** thread 1 use time9948
***** thread 2 use time12246
***** thread 2 use time12476
***** thread 2 use time12679
***** thread 2 use time12750
***** thread 2 use time12804
分析:优先级高的先运行完。 试试调换下线程1 和 线程2 的优先级别
11.3 优先级具有随机性
不是说优先级会改变 而是说不一定优先级高一定先执行完。
优先级高的 只是它执行的频率高了些
12 守护线程
java线程中有两种线程,一种是用户线程,另一种是守护线程。
守护线程是用户线程的伴生线程。如果没有用户线程 则也没有守护线程
只有当jvm内最后一个非守护线程结束时,守护线程才会随着JVM一同结束工作。
典型的守护线程是:垃圾回收线程
例子:
public class DaemonThread extends Thread {
private int i = 0;
@Override
public void run() {
try {
while(true) {
i++;
System.out.println("i=" + i);
Thread.sleep(1000);
}
} catch(InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
try {
DaemonThread d = new DaemonThread();
d.setDaemon(true);
d.start();
Thread.sleep(5000);
System.out.println("一切都结束了");
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
运行结果:
i=1
i=2
i=3
i=4
i=5
一切都结束了