多线程学习
多线程的实现主要两种范式:继承Thread类;实现Runnable接口。
本质上,Thread类也是实现了Runnable接口。但是由于JAVA的单继承原因,通过Thread实现多线程会无法继承其他类,所以可以使用实现Runnable接口的方式达到目的。
1、线程的运行是无序的,CPU以不确定的方式运行子任务,即线程。如果在同一代码块启动多个线程,其在代码块中的start顺序也不是线程的运行顺序。
Thread类的核心用法
继承Thread类
运行类:
public class Test {
public static void main(String[] args) {
System.out.println("-------------Start running----------------");
Thread1 thread1 = new Thread1();
Thread2 thread2 = new Thread2();
Thread3 thread3 = new Thread3();
thread1.start();
thread3.start();
thread2.start();
System.out.println("------------End running------------");
}
}
线程代码(1、2、3均相同,仅sleep时间不同)
public class Thread1 extends Thread{
@Override
public void run() {
super.run();
for(int i=0; i<5; i++)
if(i == 3){
try {
this.sleep(10000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}else{
System.out.println("This is the first thread : "+i);
}
}
}
由于多线程的异步性,每次运行的结果都不相同,其中一次运行结果如下:
-------------Start running----------------
------------End running------------
This is the first thread : 0
This is the first thread : 1
This is the first thread : 2
This is the third thread : 0
This is the second thread : 0
This is the third thread : 1
This is the second thread : 1
This is the third thread : 2
This is the second thread : 2
This is the second thread : 4
This is the third thread : 4
This is the first thread : 4
由以上结果可以得出,线程执行的顺序完全是随机的。
实现Runnable接口
运行类
public class Test {
public static void main(String[] args) {
System.out.println("-------------Start running----------------");
Runnable1 runnable1 = new Runnable1();
Runnable2 runnable2 = new Runnable2();
Thread1 thread1 = new Thread1(runnable1);
Thread2 thread2 = new Thread2(runnable2);
runnable2.run();
runnable1.run();
System.out.println("------------End running------------");
}
}
Runnable类(实际处理事务模块)
public class Runnable1 implements Runnable{
@Override
public void run() {
for(int i=0; i<5; i++){
System.out.println("This is the first thread : "+i);
}
}
}
Thread类
public class Thread1 extends Thread{
public Thread1(Runnable1 runnable1) {
System.out.println("constructor Thread1....");
}
public Thread1() {
super();
}
@Override
public void run() {
super.run();
for(int i=0; i<5; i++)
if(i == 3){
try {
this.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}else{
System.out.println("run This is the first thread : "+i);
}
}
}
某个运行结果:
-------------Start running----------------
constructor Thread1....
constructor Thread2....
This is the second thread : 0
This is the second thread : 1
This is the second thread : 2
This is the second thread : 3
This is the second thread : 4
This is the first thread : 0
This is the first thread : 1
This is the first thread : 2
This is the first thread : 3
This is the first thread : 4
------------End running------------
根据以上总结;
Thread类在一开始的应用上,只重写了run方法,调用该线程时默认调用此方法。后来使用了实现Runnable接口的方法,发现Thread类拥有7个构造函数。其中包括Runnable类型的参数。所以,可以实现使用一个线程控制其他线程运行的目的。当然,这种情况下,必须重写构造函数。
实例变量与线程安全
数据不共享时,及每个线程的逻辑独立,且处理的数据独立,则不会出现安全问题。但如果出现共享数据时,不做处理则会出现脏数据读取,导致线程安全问题。例子如下:
运行类
package test2_thread;
public class test {
public static void main(String[] args) {
System.out.println("Thread start ... ");
Thread1 thread1 = new Thread1();
Thread mul1 = new Thread(thread1, "fangcheng");
Thread mul2 = new Thread(thread1, "xuniyou");
Thread mul3 = new Thread(thread1, "liujianfei");
Thread mul4 = new Thread(thread1, "liuxuelian");
Thread mul5 = new Thread(thread1, "donglixin");
mul1.start();
mul2.start();
mul3.start();
mul4.start();
mul5.start();
System.out.println("Thread end ... ");
}
}
Thread类
package test2_thread;
public class Thread1 extends Thread{
private int count = 30;
@Override
public void run() {
// TODO Auto-generated method stub
super.run();
while(count>0){
System.out.println("由"+this.currentThread().getName()+
"执行 count-- 得: "+ --count);
}
}
}
某个运行结果
Thread start ...
由xuniyou执行 count-- 得: 28
由xuniyou执行 count-- 得: 27
由liujianfei执行 count-- 得: 29
Thread end ...
由liuxuelian执行 count-- 得: 26
由liuxuelian执行 count-- 得: 23
由liuxuelian执行 count-- 得: 22
由liuxuelian执行 count-- 得: 21
由liuxuelian执行 count-- 得: 20
由liuxuelian执行 count-- 得: 19
由liuxuelian执行 count-- 得: 18
由liuxuelian执行 count-- 得: 17
由liuxuelian执行 count-- 得: 16
由liuxuelian执行 count-- 得: 15
由liuxuelian执行 count-- 得: 14
由liuxuelian执行 count-- 得: 13
由liuxuelian执行 count-- 得: 12
由liuxuelian执行 count-- 得: 11
由liuxuelian执行 count-- 得: 10
由liuxuelian执行 count-- 得: 9
由liuxuelian执行 count-- 得: 8
由liuxuelian执行 count-- 得: 6
由fangcheng执行 count-- 得: 5
由xuniyou执行 count-- 得: 25
由fangcheng执行 count-- 得: 3
由liuxuelian执行 count-- 得: 4
由donglixin执行 count-- 得: 7
由liujianfei执行 count-- 得: 24
由liuxuelian执行 count-- 得: 0
由fangcheng执行 count-- 得: 1
由xuniyou执行 count-- 得: 2
出现多线程并发中的安全问题。
为避免以上原因,采用最简单可行的方法就是加 synchronized 关键字。
修改Thread代码为:
package test2_thread;
public class Thread1 extends Thread{
private int count = 30;
@Override
synchronized public void run() {
// TODO Auto-generated method stub
super.run();
while(count>0){
System.out.println("由"+this.currentThread().getName()+
"执行 count-- 得: "+ --count);
}
}
}
任何一次的结果为:
由xuniyou执行 count-- 得: 29
由xuniyou执行 count-- 得: 28
由xuniyou执行 count-- 得: 27
由xuniyou执行 count-- 得: 26
由xuniyou执行 count-- 得: 25
由xuniyou执行 count-- 得: 24
由xuniyou执行 count-- 得: 23
由xuniyou执行 count-- 得: 22
由xuniyou执行 count-- 得: 21
由xuniyou执行 count-- 得: 20
由xuniyou执行 count-- 得: 19
由xuniyou执行 count-- 得: 18
由xuniyou执行 count-- 得: 17
由xuniyou执行 count-- 得: 16
由xuniyou执行 count-- 得: 15
由xuniyou执行 count-- 得: 14
由xuniyou执行 count-- 得: 13
由xuniyou执行 count-- 得: 12
由xuniyou执行 count-- 得: 11
由xuniyou执行 count-- 得: 10
由xuniyou执行 count-- 得: 9
由xuniyou执行 count-- 得: 8
由xuniyou执行 count-- 得: 7
由xuniyou执行 count-- 得: 6
由xuniyou执行 count-- 得: 5
由xuniyou执行 count-- 得: 4
由xuniyou执行 count-- 得: 3
由xuniyou执行 count-- 得: 2
由xuniyou执行 count-- 得: 1
由xuniyou执行 count-- 得: 0
synchronized关键字
在run方法前添加synchronized关键字,使多个线程以排队的方式调用run方法。即加锁。
当线程1调用run时,给该方法加锁,其他线程无法调用该加锁的run方法。等待线程1执行完之后,解锁,其他线程可调用该方法。
通过以上的锁机制,达到线程安全的目的。
synchronized关键字的使用
在共享方法前加关键字即可。例子:
运行类
package test3_synchronized;
import test2_thread.Thread1;
public class test {
public static void main(String[] args) {
Login1 login1 = new Login1();
Login2 login2 = new Login2();
login1.start();
login2.start();
}
}
Login1类
package test3_synchronized;
public class Login1 extends Thread{
@Override
public void run() {
super.run();
Servlet servlet = new Servlet();
servlet.doPost("fangcheng", "Equation");
}
}
Login2类
package test3_synchronized;
public class Login2 extends Thread{
@Override
public void run() {
super.run();
Servlet servlet = new Servlet();
servlet.doPost("xuniyou", "silvery");
}
}
servlet类
package test3_synchronized;
public class Servlet {
private static String name;
private static String pwd;
private static int count = 30;
synchronized public void doPost(String name,String pwd){
name = name;
pwd = pwd;
while(count > 0)
System.out.println("name = "+name+" pwd = "+pwd+" count : "+ --count);
}
}
某个执行结果:
name = xuniyou pwd = silvery count : 29
name = fangcheng pwd = Equation count : 28
name = xuniyou pwd = silvery count : 27
name = fangcheng pwd = Equation count : 26
name = xuniyou pwd = silvery count : 25
name = fangcheng pwd = Equation count : 24
name = xuniyou pwd = silvery count : 23
name = fangcheng pwd = Equation count : 22
name = xuniyou pwd = silvery count : 21
name = fangcheng pwd = Equation count : 20
name = xuniyou pwd = silvery count : 19
name = fangcheng pwd = Equation count : 18
name = xuniyou pwd = silvery count : 17
name = fangcheng pwd = Equation count : 16
name = xuniyou pwd = silvery count : 15
name = fangcheng pwd = Equation count : 14
name = xuniyou pwd = silvery count : 13
name = fangcheng pwd = Equation count : 12
name = xuniyou pwd = silvery count : 11
name = fangcheng pwd = Equation count : 10
name = xuniyou pwd = silvery count : 9
name = fangcheng pwd = Equation count : 8
name = xuniyou pwd = silvery count : 7
name = fangcheng pwd = Equation count : 6
name = fangcheng pwd = Equation count : 4
name = fangcheng pwd = Equation count : 3
name = xuniyou pwd = silvery count : 5
name = fangcheng pwd = Equation count : 2
name = xuniyou pwd = silvery count : 1
name = fangcheng pwd = Equation count : 0
结论:满足线程安全了。