继承Thread类
使用继承Thread类的方式创建新线程时,最大的局限是不支持多集成。因为Java 语言的特点是单继承。
public class MyThread extends Thread{
public void run() {
System.out.println("MyThread");
}
}
public class Run {
public static void main(String[] args) {
MyThread myThread = new MyThread();
myThread.start();
System.out.println("--- end ---");
}
}
输出结果
--- end ---
MyThread
线程时一个子任务,CPU以不确定的方式调用线程中的run()方法,所以会出现先打印---end---
打印MyThread
这样的结果。
创建自定义线程类
public class MyThread extends Thread{
public void run() {
System.out.println("MyThread");
}
}
public class MyThread1 extends Thread{
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(Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
MyThread1 thread = new MyThread1();
thread.setName("myThread");
thread.start();
for(int i=0;i<10;i++) {
int time = (int)(Math.random() *1000 );
try {
Thread.sleep(time);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("main=" + Thread.currentThread().getName());
}
}
}
运行结果
run=myThread
main=main
run=myThread
main=main
main=main
run=myThread
main=main
run=myThread
main=main
main=main
run=myThread
main=main
run=myThread
main=main
main=main
run=myThread
main=main
run=myThread
run=myThread
run=myThread
Thread.start()方法的顺序不代表线程启动的顺序。
package com.extthread;
public class MyThread extends Thread{
private int i = 0;
public MyThread(int i) {
this.i = i;
}
public void run() {
System.out.println("i=" + i);
}
}
package com.extthread;
public class Test {
public static void main(String[] args) {
MyThread tl1 = new MyThread(1);
MyThread tl2 = new MyThread(2);
MyThread tl3 = new MyThread(3);
tl1.start();
tl2.start();
tl3.start();
}
}
运行结果为:
i=2
i=1
i=3
线程启动顺序与start()执行顺序无关。
实现Runnable接口
package com.runable;
public class MyRunnable implements Runnable{
public void run() {
System.out.println("运行中");
}
}
package com.runable;
public class Run {
public static void main(String[] args) {
Runnable runnable = new MyRunnable();
Thread thread = new Thread(runnable);
thread.start();
System.out.println("--- main end ---");
}
}
运行结果为:
--- main end ---
运行中
构造函数Thread(Runnable target)不光可以传入Runnable接口的对象,还还可以传入一个Thread 类的对象。
实例变量与线程安全
1,不共享数据的情况
package com.test2;
public class MyThread extends Thread{
private int count = 5;
public MyThread(String name) {
this.setName(name); //设置线程名称
}
public void run() {
while( count > 0 ) {
count--;
System.out.println("由 " + Thread.currentThread().getName() + " 计算 ,count=" + count);
}
}
}
package com.test2;
public class Run {
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();
}
}
计算结果为:
由 B 计算 ,count=4
由 C 计算 ,count=4
由 A 计算 ,count=4
由 C 计算 ,count=3
由 B 计算 ,count=3
由 C 计算 ,count=2
由 A 计算 ,count=3
由 C 计算 ,count=1
由 B 计算 ,count=2
由 C 计算 ,count=0
由 A 计算 ,count=2
由 B 计算 ,count=1
由 A 计算 ,count=1
由 B 计算 ,count=0
由 A 计算 ,count=0
2, 共享数据的情况
package com.test2;
public class Run1 {
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");
a.start();
b.start();
c.start();
}
}
运行结果为
由 B 计算 ,count=3
由 C 计算 ,count=3
由 B 计算 ,count=2
由 C 计算 ,count=1
由 B 计算 ,count=0
非线程安全
非线程安全主要是指多个线程对同一个对象中同一个实例变量进行操作时出现值被更改,值不同步的情况,进而影响程序的执行流程。
package com.controller;
public class LoginServlet {
private static String usernameRef;
private static String passwordRef;
public static void doPost(String username, String password) {
usernameRef = username;
if(usernameRef.equals("a")) {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
passwordRef = password;
System.out.println("usernameRef="+usernameRef+",passwordRef="+passwordRef);
}
}
package com.controller;
public class ALogin extends Thread{
public void run() {
LoginServlet.doPost("a", "aaa");
}
}
package com.controller;
public class BLogin extends Thread{
public void run() {
LoginServlet.doPost("b", "bbb");
}
}
package com.controller;
public class Run {
public static void main(String[] args) {
ALogin a = new ALogin();
a.start();
BLogin b = new BLogin();
b.start();
}
}
运行结果为
usernameRef=b,passwordRef=bbb
usernameRef=b,passwordRef=aaa
解决这个非线程安全的方法是使用synchronized关键字。
package com.controller;
public class LoginServlet {
private static String usernameRef;
private static String passwordRef;
public synchronized static void doPost(String username, String password) {
usernameRef = username;
if(usernameRef.equals("a")) {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
passwordRef = password;
System.out.println("usernameRef="+usernameRef+",passwordRef="+passwordRef);
}
}
留意i--与System.out.println()的异常
解决非线程安全问题使用的是 synchronized 关键字 ,i--与System.out.println()的异常有可能出现非线程安全问题。
package com.controller;
public class MyThread extends Thread {
private int i = 5;
public void run() {
System.out.println("i=" + i-- + ", threadName=" + Thread.currentThread().getName());
}
}
package com.controller;
public class Run1 {
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();
}
}
关于作者
王硕,网名信平,十年软件开发经验,业余产品经理,精通Java/Python/Go等,喜欢研究技术,著有《PyQt 5 快速开发与实战》《Python 3.* 全栈开发》,多个业余开源项目托管在GitHub上,欢迎微博交流。