参考 Java核心技术系列:Java多线程编程核心技术
1. 使用多线程 继承Thread类
public class MyThread extends Thread {
@Override
public void run() {
System.out.println("MyThread.run");
}
}
public class Demo01 {
public static void main(String[] args) {
MyThread t = new MyThread();
t.start();
//多次调用 start()方法会出现 如下异常
//t.start();java.lang.IllegalThreadStateException
System.out.println("Demo01.main");
}
}
输出结果如下:线程执行的随机性
Demo01.main
MyThread.run
说明线程调用的随机性:
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 Demo01 {
public static void main(String[] args) {
try {
MyThread t = new MyThread();
t.setName("myThread");
t.start();
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();
}
System.out.println("Demo01.main");
}
}
结果如下:
run:myThread
run:myThread
run:main
run:main
run:main
run:myThread
run:main
run:myThread
run:main
run:main
run:main
run:main
run:myThread
run:main
run:main
Demo01.main
run:myThread
run:myThread
run:myThread
run:myThread
run:myThread
说明:执行start()的顺序不代表线程启动的顺序,代码如下:
public class MyThread extends Thread {
private int i;
public MyThread(int i) {
this.i = i;
}
@Override
public void run() {
System.out.println(i);
}
}
public class Demo01 {
public static void main(String[] args) {
new MyThread(1).start();
new MyThread(2).start();
new MyThread(3).start();
new MyThread(4).start();
new MyThread(5).start();
new MyThread(6).start();
new MyThread(7).start();
new MyThread(8).start();
new MyThread(9).start();
new MyThread(10).start();
new MyThread(11).start();
System.out.println("Demo01.main");
}
}
执行结果为:
Demo01.main
1
2
4
3
5
7
8
9
11
6
10
2. 实现Runnable接口略去
3. 实例变量和线程安全
- 实例变量不共享的情况
public class MyThread extends Thread {
private int count = 5;
public MyThread(String name) {
this.setName(name);
}
@Override
public void run() {
while (count > 0) {
count--;
System.out.println("由 " + this.currentThread().getName() + "计算,count=" + count);
}
}
}
public class Demo01 {
public static void main(String[] args) {
new MyThread("A").start();
new MyThread("B").start();
new MyThread("C").start();
System.out.println("Demo01.main");
}
}
执行结果为:
Demo01.main
由 A计算,count=4
由 A计算,count=3
由 A计算,count=2
由 A计算,count=1
由 A计算,count=0
由 B计算,count=4
由 B计算,count=3
由 B计算,count=2
由 B计算,count=1
由 B计算,count=0
由 C计算,count=4
由 C计算,count=3
由 C计算,count=2
由 C计算,count=1
由 C计算,count=0
这里一共创建了三个线程,每个线程都有自己各自的count变量,自己减少自己的count变量值
这样的情况就是变量不共享。
如果想实现三个线程对同一个count变量进行减法操作,如果实现?
- 共享数据的情况
public class MyThread extends Thread {
private int count = 5;
@Override
public void run() {
count--;
System.out.println("由 " + this.currentThread().getName() + "计算,count=" + count);
}
}
public class Demo01 {
public static void main(String[] args) {
MyThread t = new MyThread();
new Thread(t,"A").start();
new Thread(t,"B").start();
new Thread(t,"C").start();
new Thread(t,"D").start();
new Thread(t,"E").start();
System.out.println("Demo01.main");
}
}
执行结果如下:
Demo01.main
由 A计算,count=4
由 B计算,count=3
由 D计算,count=1
由 E计算,count=0
由 C计算,count=2
结果和预期不一样,这就是"非线程安全"的问题
"非线程安全"的问题: 主要是指多个线程对同一个对象中的同一个实例变量进行操作时
会出现某值被更改,值不同步的情况,进而影响执行流程。
在某些JVM中,i–的操作分成如下3步:
a. 取得原有的i值
b. 计算i-1
c. 对i进行赋值
在这3个步骤中,如果多个线程同时访问,那么一定会出现"非线程安全"的问题
修改为如下代码即可:
public class MyThread extends Thread {
private int count = 5;
@Override
synchronized public void run() {
count--;
System.out.println("由 " + this.currentThread().getName() + "计算,count=" + count);
}
}
模拟生成环境的非线程安全问题:
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:"+passwordRef);
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
public class ALogin extends Thread{
@Override
public void run() {
LoginServlet.doPOst("a","aa");
}
}
public class BLogin extends Thread {
@Override
public void run() {
LoginServlet.doPOst("b","bb");
}
}
public class Demo01 {
public static void main(String[] args) {
ALogin a = new ALogin();
a.start();
BLogin b = new BLogin();
b.start();
System.out.println("Demo01.main");
}
}
输出结果为:
Demo01.main
username:b password:bb
username:b password:aa
解决问题:使用synchronized 关键字
synchronized public static void doPOst(String username,String password){
//同上,略去
}
4. i-- 和 System.out.println 的异常
细化以下println()和i++联合使用时"有可能"出现的另外一种异常情况。
代码如下:
public class MyThread extends Thread {
private int i = 5;
@Override
public void run() {
//i-- 放在了 println方法中执行
System.out.println("i= "+(i--) +" threadName="+ MyThread.currentThread().getName());
}
}
public class Demo01 {
public static void main(String[] args) {
MyThread run = new MyThread();
for (int i=0;i<5;i++){
new Thread(run).start();
}
}
}
输出结果如下:
i= 5 threadName=Thread-2
i= 4 threadName=Thread-3
i= 3 threadName=Thread-4
i= 5 threadName=Thread-1
i= 2 threadName=Thread-5
为什么呢?虽然println方法时同步的,但是i–操作是在进入println()方法之前发生的,
所以还是存在非线程安全问题的概率。为了防止发生这个问题,还是继续使用同步方法。
完!