Java多线程理解
一、常用方法
1、join()方法,让一个线程等待另一个线程执行完之后再执行。例如:A线程执行体中执行B线程的join方法,那么需要等到B线程执行完毕之后再执行A线程。
示例:
public class MyRunnable implements Runnable{
private boolean flag = false;
@Override
public void run() {
for(int i = 0 ; i < 100 && !flag ; i++){
System.out.println(Thread.currentThread().getName()+"---"+i);
}
}
}
public class Test extends Thread{
public static void main(String[] args) throws InterruptedException, ExecutionException {
MyRunnable myRunnable = new MyRunnable();
Thread thread = new Thread(myRunnable);
for(int i = 0 ; i < 100 ; i++){
System.out.println(Thread.currentThread().getName()+"--"+i);
if(i == 30){
thread.start();
thread.join();
}
}
}
}
当i== 30时,在主线程中插入thread线程,直到thread线程执行完毕,主线程才开始执行。
2、sleep():让线程暂停执行指定的时间。由于线程不是出于就绪状态,在指定时间之内,线程不能执行。特殊应用:当new一个线程并调用start()方法之后,线程需要等待某个时间获取CPU时间片才能执行,如果希望这个线程立即执行,可以使用sleep(1),当然这个立即执行的线程不一定是多线程中的哪一个。
public class Test extends Thread{
public static void main(String[] args) throws InterruptedException, ExecutionException {
MyRunnable myRunnable = new MyRunnable();
Thread thread1 = new Thread(myRunnable);
thread1.setName("A");
Thread thread2 = new Thread(myRunnable);
thread2.setName("B");
Thread thread3 = new Thread(myRunnable);
thread3.setName("C");
for(int i = 0 ; i < 100 ; i++){
System.out.println(Thread.currentThread().getName()+"---"+i);
if(i==30){
thread1.start();
Thread.sleep(1);
}
if(i==40){
thread2.start();
Thread.sleep(1);
}
if(i==50){
thread3.start();
Thread.sleep(1);
}
}
}
}
class MyRunnable implements Runnable {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getName() + " " + i);
}
}
}
3、后台线程
概念/目的:后台线程主要是为其他线程(相对可以称之为前台线程)提供服务,或“守护线程”。如JVM中的垃圾回收线程。
生命周期:后台线程的生命周期与前台线程生命周期有一定关联。主要体现在:当所有的前台线程都进入死亡状态时,后台线程会自动死亡(其实这个也很好理解,因为后台线程存在的目的在于为前台线程服务的,既然所有的前台线程都死亡了,那它自己还留着有什么用...伟大啊 ! !)。
设置后台线程:调用Thread对象的setDaemon(true)方法可以将指定的线程设置为后台线程。
判断线程是否是后台线程:调用thread对象的isDeamon()方法。
注: main 线程默认是前台线程,前台线程创建中创建的子线程默认是前台线程,后台线程中创建的线程默认是后台线程。调用 setDeamon(true) 方法将前台线程设置为后台线程时,需要在 start() 方法调用之前。前天线程都死亡后, JVM 通知后台线程死亡,但从接收指令到作出响应,需要一定的时间。public class Test extends Thread{
public static void main(String[] args) throws InterruptedException, ExecutionException {
MyRunnable myRunnable = new MyRunnable();
Thread thread = new Thread(myRunnable);
for(int i = 0 ; i < 10 ; i++){
System.out.println(Thread.currentThread().getName()+"--"+i);
if(i == 3){
thread.setDaemon(true);
thread.start();
}
}
}
}
public class MyRunnable implements Runnable{
private boolean flag = false;
@Override
public void run() {
for(int i = 0 ; i < 10000 && !flag ; i++){
System.out.println(Thread.currentThread().getName()+"---"+i);
}
}
public void stoped(){
this.flag = true;
}
}
执行结果:
main--0
main--1
main--2
main--3
main--4
main--5
main--6
main--7
main--8
main--9
Thread-0---0
Thread-0---1
Thread-0---2
Thread-0---3
Thread-0---4
Thread-0---5
Thread-0---6
Thread-0---7
Thread-0---8
Thread-0---9
4、常用的快速终止线程的方法
一般线程状态分成就绪状态、运行状态、死亡状态。
就绪状态转换成运行状态:线程得到处理器资源
运行状态转换成就绪窗台:此线程主动执行yeild()方法或者再运行过程中失去处理器资源。
运行状态转成死亡状态: 线程执行体执行结束或者发生了异常。
此处需要特别注意的是:当调用线程的yield()方法时,线程从运行状态转换为就绪状态,但接下来CPU调度就绪状态中的哪个线程具有一定的随机性,因此,可能会出现A线程调用了yield()方法后,接下来CPU仍然调度了A线程的情况。
由于实际的业务需要,常常会遇到需要在特定时机终止某一线程的运行,使其进入到死亡状态。目前最通用的做法是设置一boolean型的变量,当条件满足时,使线程执行体快速执行完毕。如:
public class MyRunnable implements Runnable{
private boolean flag = false;
@Override
public void run() {
for(int i = 0 ; i < 10000 && !flag ; i++){
System.out.println(Thread.currentThread().getName()+"---"+i);
}
}
public void stoped(){
this.flag = true;
}
}
public class Test extends Thread{
public static void main(String[] args) throws InterruptedException, ExecutionException {
MyRunnable myRunnable = new MyRunnable();
Thread thread = new Thread(myRunnable);
for(int i = 0 ; i < 10000000 ; i++){
if(i==3){
thread.start();
}
if(i == 400000){
myRunnable.stoped();
}
}
}
}
二、对象及变量的并发访问
1、(1)方法内变量是线程安全的
public class SelfNum {
public void addI(String name){
int num = 0;
if("a".equals(name)){
num = 100;
System.out.println(ThreadA.currentThread().getName()+",a set over");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if("b".equals(name)){
num = 200;
System.out.println(ThreadA.currentThread().getName()+",b set over");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
System.out.println(ThreadA.currentThread().getName()+",当前num值是:"+num);
}
}
(2)实例变量非线程安全
public class SelfNum {
private int num = 0;
public void addI(String name){
if("a".equals(name)){
num = 100;
System.out.println(ThreadA.currentThread().getName()+",a set over");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if("b".equals(name)){
num = 200;
System.out.println(ThreadA.currentThread().getName()+",b set over");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
System.out.println(ThreadA.currentThread().getName()+",当前num值是:"+num);
}
}
测试:
public class MainTest {
public static void main(String[] args) {
SelfNum selfNum = new SelfNum();
ThreadA threadA = new ThreadA(selfNum);
ThreadB threadB = new ThreadB(selfNum);
threadA.start();
threadB.start();
}
}
第一种情况结果:
Thread-0,a set over
Thread-1,b set over
Thread-1,当前num值是:200
Thread-0,当前num值是:100
第二种情况结果:
Thread-0,a set over
Thread-1,b set over
Thread-0,当前num值是:200
Thread-1,当前num值是:200