《Java多线程编程核心技术》——2.1节synchronized同步方法

2.1 synchronized同步方法
在第1章中已经接触“线程安全”与“非线程安全”相关的技术点,它们是学习多线程技术时一定会遇到的经典问题。“非线程安全”其实会在多个线程对同一个对象中的实例变量进行并发访问时发生,产生的后果就是“脏读”,也就是取到的数据其实是被更改过的。而“线程安全”就是以获得的实例变量的值是经过同步处理的,不会出现脏读的现象。此知识点在第1章也介绍,但本章将细化线程并发访问的内容,在细节上更多接触在并发时变量值的处理方法。

2.1.1 方法内的变量为线程安全
“非线程安全”问题存在于“实例变量”中,如果是方法内部的私有变量,则不存在“非线程安全”问题,所得结果也就是“线程安全”的了。
下面的示例项目就是实现方法内部声明一个变量时,是不存在“非线程安全”问题的。
创建t1项目,HasSelfPrivateNum.java文件代码如下:

package service;
public class HasSelfPrivateNum {
    public void addI(String username) {
        try {
            int num = 0;
            if (username.equals("a")) {
                num = 100;
                System.out.println("a set over!");
                Thread.sleep(2000);
            } else {
                num = 200;
                System.out.println("b set over!");
            }
            System.out.println(username + " num=" + num);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

}
文件ThreadA.java代码如下:
package extthread;
import service.HasSelfPrivateNum;
public class ThreadA extends Thread {
    private HasSelfPrivateNum numRef;
    public ThreadA(HasSelfPrivateNum numRef) {
        super();
        this.numRef = numRef;
    }
    @Override
    public void run() {
        super.run();
        numRef.addI("a");
    }
}
文件ThreadB.java代码如下:
package extthread;
import service.HasSelfPrivateNum;
public class ThreadB extends Thread {
    private HasSelfPrivateNum numRef;
    public ThreadB(HasSelfPrivateNum numRef) {
        super();
        this.numRef = numRef;
    }
    @Override
    public void run() {
        super.run();
        numRef.addI("b");
    }
}
文件Run.java代码如下:
package test;
import service.HasSelfPrivateNum;
import extthread.ThreadA;
import extthread.ThreadB;
public class Run {
    public static void main(String[] args) {
        HasSelfPrivateNum numRef = new HasSelfPrivateNum();
        ThreadA athread = new ThreadA(numRef);
        athread.start();
        ThreadB bthread = new ThreadB(numRef);
        bthread.start();
    }
}

程序运行后的效果如图2-1所示。

可见,方法中的变量不存在非线程安全问题,永远都是线程安全的。这是方法内部的变量是私有的特性造成的。

2.1.2 实例变量非线程安全
如果多个线程共同访问1个对象中的实例变量,则有可能出现“非线程安全”问题。
用线程访问的对象中如果有多个实例变量,则运行的结果有可能出现交叉的情况。此情况在第1章中非线程安全的案例演示过。
如果对象仅有1个实例变量,则有可能出现覆盖的情况。
创建t2项目,HasSelfPrivateNum.java文件代码如下:

package service;
public class HasSelfPrivateNum {
    private int num = 0;
    public void addI(String username) {
        try {
            if (username.equals("a")) {
                num = 100;
                System.out.println("a set over!");
                Thread.sleep(2000);
            } else {
                num = 200;
                System.out.println("b set over!");
            }
            System.out.println(username + " num=" + num);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

}
文件ThreadA.java代码如下:
package extthread;
import service.HasSelfPrivateNum;
public class ThreadA extends Thread {
    private HasSelfPrivateNum numRef;
    public ThreadA(HasSelfPrivateNum numRef) {
        super();
        this.numRef = numRef;
    }
    @Override
    public void run() {
        super.run();
        numRef.addI("a");
    }
}
文件ThreadB.java代码如下:
package extthread;
import service.HasSelfPrivateNum;
public class ThreadB extends Thread {
    private HasSelfPrivateNum numRef;
    public ThreadB(HasSelfPrivateNum numRef) {
        super();
        this.numRef = numRef;
    }
    @Override
    public void run() {
        super.run();
        numRef.addI("b");
    }
}
文件Run.java代码如下:
package test;
import service.HasSelfPrivateNum;
import extthread.ThreadA;
import extthread.ThreadB;
public class Run {
    public static void main(String[] args) {
        HasSelfPrivateNum numRef = new HasSelfPrivateNum();
        ThreadA athread = new ThreadA(numRef);
        athread.start();
        ThreadB bthread = new ThreadB(numRef);
        bthread.start();
    }
}

程序运行后的结果如图2-2所示。

本实验是两个线程同时访问一个没有同步的方法,如果两个线程同时操作业务对象中的实例变量,则有可能会出现“非线程安全”问题。此示例的知识点在前面已经介绍过,只需要在public void addI(String username)方法前加关键字synchronized即可。更改后的代码如下:

package service;
public class HasSelfPrivateNum {
    private int num = 0;
    synchronized public void addI(String username) {
        try {
            if (username.equals("a")) {
                num = 100;
                System.out.println("a set over!");
                Thread.sleep(2000);
            } else {
                num = 200;
                System.out.println("b set over!");
            }
            System.out.println(username + " num=" + num);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}

程序再次运行结果如图2-3所示。

实验结论:在两个线程访问同一个对象中的同步方法时一定是线程安全的。本实验由于是同步访问,所以先打印出a,然后打印出b。

2.1.3 多个对象多个锁
再来看一个实验,创建项目名称为twoObjectTwoLock,创建HasSelfPrivateNum.java类,代码如下:

package service;
public class HasSelfPrivateNum {
    private int num = 0;
    synchronized public void addI(String username) {
        try {
            if (username.equals("a")) {
                num = 100;
                System.out.println("a set over!");
                Thread.sleep(2000);
            } else {
                num = 200;
                System.out.println("b set over!");
            }
            System.out.println(username + " num=" + num);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}

上面的代码中有同步方法addI,说明此方法应该被顺序调用。
创建线程ThreadA.java和ThreadB.java代码,如图2-4所示。

类Run.java代码如下:

package test;
import service.HasSelfPrivateNum;
import extthread.ThreadA;
import extthread.ThreadB;
public class Run {
    public static void main(String[] args) {
        HasSelfPrivateNum numRef1 = new HasSelfPrivateNum();
        HasSelfPrivateNum numRef2 = new HasSelfPrivateNum();
        ThreadA athread = new ThreadA(numRef1);
        athread.start();
        ThreadB bthread = new ThreadB(numRef2);
        bthread.start();
    }
}

创建了2个HasSelfPrivateNum.java类的对象,程序运行的结果如图2-5所示。

上面示例是两个线程分别访问同一个类的两个不同实例的相同名称的同步方法,效果却是以异步的方式运行的。本示例由于创建了2个业务对象,在系统中产生出2个锁,所以运行结果是异步的,打印的效果就是先打印b,然后打印a。
从上面程序运行结果来看,虽然在HasSelfPrivateNum.java中使用了synchronized关键字,但打印的顺序却不是同步的,是交叉的。为什么是这样的结果呢?
关键字synchronized取得的锁都是对象锁,而不是把一段代码或方法(函数)当作锁,所以在上面的示例中,哪个线程先执行带synchronized关键字的方法,哪个线程就持有该方法所属对象的锁Lock,那么其他线程只能呈等待状态,前提是多个线程访问的是同一个对象。
但如果多个线程访问多个对象,则JVM会创建多个锁。上面的示例就是创建了2个HasSelfPrivateNum.java类的对象,所以就会产生出2个锁。
同步的单词为synchronized,异步的单词为asynchronized。

2.1.4 synchronized方法与锁对象
为了证明前面讲述线程锁的是对象,创建实验用的项目synchronizedMethodLockObject,类MyObject.java文件代码如下:

package extobject;
public class MyObject {
    public void methodA() {
        try {
            System.out.println("begin methodA threadName="
                    + Thread.currentThread().getName());
            Thread.sleep(5000);
            System.out.println("end");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
自定义线程类ThreadA.java代码如下:
package extthread;
import extobject.MyObject;
public class ThreadA extends Thread {
    private MyObject object;
    public ThreadA(MyObject object) {
        super();
        this.object = object;
    }
    @Override
    public void run() {
        super.run();
        object.methodA();
    }
}
自定义线程类ThreadB.java代码如下:
package extthread;
import extobject.MyObject;
public class ThreadB extends Thread {
    private MyObject object;
    public ThreadB(MyObject object) {
        super();
        this.object = object;
    }
    @Override
    public void run() {
        super.run();
        object.methodA();
    }
}
运行类Run.java代码如下:
package test.run;
import extobject.MyObject;
import extthread.ThreadA;
import extthread.ThreadB;
public class Run {
    public static void main(String[] args) {
        MyObject object = new MyObject();
        ThreadA a = new ThreadA(object);
        a.setName("A");
        ThreadB b = new ThreadB(object);
        b.setName("B");
        a.start();
        b.start();
    }
}

程序运行后的效果如图2-6所示。
更改MyObject.java代码如下:

package extobject;
public class MyObject {
    synchronized public void methodA() {
        try {
            System.out.println("begin methodA threadName="
                    + Thread.currentThread().getName());
            Thread.sleep(5000);
            System.out.println("end");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

如上面代码所示,在methodA方法前加入了关键字synchronized进行同步处理。程序再次运行效果如图2-7所示。

通过上面的实验得到结论,调用用关键字synchronized声明的方法一定是排队运行的。另外需要牢牢记住“共享”这两个字,只有共享资源的读写访问才需要同步化,如果不是共享资源,那么根本就没有同步的必要。
那其他的方法在被调用时会是什么效果呢?如何查看到Lock锁对象的效果呢?继续新建实验用的项目synchronizedMethodLockObject2,类文件MyObject.java代码如下:

package extobject;
public class MyObject {
    synchronized public void methodA() {
        try {
            System.out.println("begin methodA threadName="
                    + Thread.currentThread().getName());
            Thread.sleep(5000);
            System.out.println("end endTime=" + System.currentTimeMillis());
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    public void methodB() {
        try {
            System.out.println("begin methodB threadName="
                    + Thread.currentThread().getName() + " begin time="
                    + System.currentTimeMillis());
            Thread.sleep(5000);
            System.out.println("end");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

两个自定义线程类分别调用不同的方法,代码如图2-8所示。

文件Run.java代码如下:

package test.run;
import extobject.MyObject;
import extthread.ThreadA;
import extthread.ThreadB;
public class Run {
    public static void main(String[] args) {
        MyObject object = new MyObject();
        ThreadA a = new ThreadA(object);
        a.setName("A");
        ThreadB b = new ThreadB(object);
        b.setName("B");
        a.start();
        b.start();
    }
}

程序运行结果如图2-9所示。
通过上面的实验可以得知,虽然线程A先持有了object对象的锁,但线程B完全可以异步调用非synchronized类型的方法。
继续实验,将MyObject.java文件中的methodB()方法前加上synchronized关键字,代码如下:

    synchronized public void methodB() {
        try {
            System.out.println("begin methodB threadName="
                    + Thread.currentThread().getName() + " begin time="
                    + System.currentTimeMillis());
            Thread.sleep(5000);
            System.out.println("end");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

本示例是两个线程访问同一个对象的两个同步的方法,运行结果如图2-10所示。
 

此实验的结论是:
1)A线程先持有object对象的Lock锁,B线程可以以异步的方式调用object对象中的非synchronized类型的方法。
2)A线程先持有object对象的Lock锁,B线程如果在这时调用object对象中的synchronized类型的方法则需等待,也就是同步。

2.1.5 脏读
在2.1.4节示例中已经实现多个线程调用同一个方法时,为了避免数据出现交叉的情况,使用synchronized关键字来进行同步。
虽然在赋值时进行了同步,但在取值时有可能出现一些意想不到的意外,这种情况就是脏读(dirtyRead)。发生脏读的情况是在读取实例变量时,此值已经被其他线程更改过了。
创建t3项目,PublicVar.java文件代码如下:

package entity;
public class PublicVar {
    public String username = "A";
    public String password = "AA";
    synchronized public void setValue(String username, String password) {
        try {
            this.username = username;
            Thread.sleep(5000);
            this.password = password;
            System.out.println("setValue method thread name="
                    + Thread.currentThread().getName() + " username="
                    + username + " password=" + password);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    public void getValue() {
        System.out.println("getValue method thread name="
                + Thread.currentThread().getName() + " username=" + username
                + " password=" + password);
    }
}
同步方法setValue()的锁属于类PublicVar的实例。
创建线程类ThreadA.java的代码如下:
package extthread;
import entity.PublicVar;
public class ThreadA extends Thread {
    private PublicVar publicVar;
    public ThreadA(PublicVar publicVar) {
        super();
        this.publicVar = publicVar;
    }
    @Override
    public void run() {
        super.run();
        publicVar.setValue("B", "BB");
    }
}
文件Test.java代码如下:
package test;
import entity.PublicVar;
import extthread.ThreadA;
public class Test {
    public static void main(String[] args) {
        try {
            PublicVar publicVarRef = new PublicVar();
            ThreadA thread = new ThreadA(publicVarRef);
            thread.start();
            Thread.sleep(200);// 打印结果受此值大小影响
            publicVarRef.getValue();
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}

程序运行后的结果如图2-11所示。

出现脏读是因为public void getValue()方法并不是同步的,所以可以在任意时候进行调用。解决办法当然就是加上同步synchronized关键字,代码如下:

    synchronized public void getValue() {
        System.out.println("getValue method thread name="
                + Thread.currentThread().getName() + " username=" + username
                + " password=" + password);
    }

程序运行后的结果如图2-12所示。

可见,方法setValue()和getValue()被依次执行。通过这个案例不仅要知道脏读是通过synchronized关键字解决的,还要知道如下内容:
当A线程调用anyObject对象加入synchronized关键字的X方法时,A线程就获得了X方法锁,更准确地讲,是获得了对象的锁,所以其他线程必须等A线程执行完毕才可以调用X方法,但B线程可以随意调用其他的非synchronized同步方法。
当A线程调用anyObject对象加入synchronized关键字的X方法时,A线程就获得了X方法所在对象的锁,所以其他线程必须等A线程执行完毕才可以调用X方法,而B线程如果调用声明了synchronized关键字的非X方法时,必须等A线程将X方法执行完,也就是释放对象锁后才可以调用。这时A线程已经执行了一个完整的任务,也就是说username和password这两个实例变量已经同时被赋值,不存在脏读的基本环境。
脏读一定会出现操作实例变量的情况下,这就是不同线程“争抢”实例变量的结果。

2.1.6 synchronized锁重入
关键字synchronized拥有锁重入的功能,也就是在使用synchronized时,当一个线程得到一个对象锁后,再次请求此对象锁时是可以再次得到该对象的锁的。这也证明在一个synchronized方法/块的内部调用本类的其他synchronized方法/块时,是永远可以得到锁的。
创建实验用的项目synLockIn_1,类Service.java代码如下:

package myservice;
public class Service {
    synchronized public void service1() {
        System.out.println("service1");
        service2();
    }
    synchronized public void service2() {
        System.out.println("service2");
        service3();
    }
    synchronized public void service3() {
        System.out.println("service3");
    }
}
线程类MyThread.java代码如下:
package extthread;
import myservice.Service;
public class MyThread extends Thread {
    @Override
    public void run() {
        Service service = new Service();
        service.service1();
    }
}
运行类Run.java代码如下:
package test;
import extthread.MyThread;
public class Run {
    public static void main(String[] args) {
        MyThread t = new MyThread();
        t.start();
    }
}

程序运行结果如图2-13所示。

“可重入锁”的概念是:自己可以再次获取自己的内部锁。比如有1条线程获得了某个对象的锁,此时这个对象锁还没有释放,当其再次想要获取这个对象的锁的时候还是可以获取的,如果不可锁重入的话,就会造成死锁。
可重入锁也支持在父子类继承的环境中。
创建实验用的项目synLockIn_2,类Main.java代码如下:

package myservice;
public class Main {
    public int i = 10;
    synchronized public void operateIMainMethod() {
        try {
            i--;
            System.out.println("main print i=" + i);
            Thread.sleep(100);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}
子类Sub.java代码如下:
package myservice;
public class Sub extends Main {
    synchronized public void operateISubMethod() {
        try {
            while (i > 0) {
                i--;
                System.out.println("sub print i=" + i);
                Thread.sleep(100);
                this.operateIMainMethod();
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
自定义线程类MyThread.java代码如下:
package extthread;
import myservice.Main;
import myservice.Sub;
public class MyThread extends Thread {
    @Override
    public void run() {
        Sub sub = new Sub();
        sub.operateISubMethod();
    }
}
运行类Run.java代码如下:
package test;
import extthread.MyThread;
public class Run {
    public static void main(String[] args) {
        MyThread t = new MyThread();
        t.start();
    }
}

程序运行后的效果如图2-14所示。

此实验说明,当存在父子类继承关系时,子类是完全可以通过“可重入锁”调用父类的同步方法的。
2.1.7 出现异常,锁自动释放
当一个线程执行的代码出现异常时,其所持有的锁会自动释放。
创建实验用的项目throwExceptionNoLock,类Service.java代码如下:

package service;
public class Service {
    synchronized public void testMethod() {
        if (Thread.currentThread().getName().equals("a")) {
            System.out.println("ThreadName=" + Thread.currentThread().getName()
                    + " run beginTime=" + System.currentTimeMillis());
            int i = 1;
            while (i == 1) {
                if (("" + Math.random()).substring(0, 8).equals("0.123456")) {
                    System.out.println("ThreadName="
                            + Thread.currentThread().getName()
                            + " run   exceptionTime="
                            + System.currentTimeMillis());
                    Integer.parseInt("a");
                }
            }
        } else {
            System.out.println("Thread B run Time="
                    + System.currentTimeMillis());
        }
    }
}

两个自定义线程代码如图2-15所示。

运行类Run.java代码如下:

package controller;
import service.Service;
import extthread.ThreadA;
import extthread.ThreadB;
public class Test {
    public static void main(String[] args) {
        try {
            Service service = new Service();
            ThreadA a = new ThreadA(service);
            a.setName("a");
            a.start();
            Thread.sleep(500);
            ThreadB b = new ThreadB(service);
            b.setName("b");
            b.start();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

程序运行后的效果如图2-16所示。

线程a出现异常并释放锁,线程b进入方法正常打印,实验的结论就是出现异常的锁被自动释放了。

2.1.8 同步不具有继承性
同步不可以继承。
创建测试用的项目synNotExtends,类Main.java代码如下:

package service;
public class Main {
    synchronized public void serviceMethod() {
        try {
            System.out.println("int main 下一步sleep begin threadName="
                    + Thread.currentThread().getName() + " time="
                    + System.currentTimeMillis());
            Thread.sleep(5000);
            System.out.println("int main 下一步sleep   end threadName="
                    + Thread.currentThread().getName() + " time="
                    + System.currentTimeMillis());
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
类Sub.java代码如下:
package service;
public class Sub extends Main {
    @Override
    public void serviceMethod() {
        try {
            System.out.println("int sub 下一步sleep begin threadName="
                    + Thread.currentThread().getName() + " time="
                    + System.currentTimeMillis());
            Thread.sleep(5000);
            System.out.println("int sub 下一步sleep   end threadName="
                    + Thread.currentThread().getName() + " time="
                    + System.currentTimeMillis());
            super.serviceMethod();
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}

类MyThreadA.java和MyThreadB.java代码如图2-17所示。

类Test.java代码如下:

package controller;
import service.Sub;
import extthread.MyThreadA;
import extthread.MyThreadB;
public class Test {
    public static void main(String[] args) {
        Sub subRef = new Sub();
        MyThreadA a = new MyThreadA(subRef);
        a.setName("A");
        a.start();
        MyThreadB b = new MyThreadB(subRef);
        b.setName("B");
        b.start();
    }
}

程序运行后的效果如图2-18所示。

时间: 2024-08-30 19:58:10

《Java多线程编程核心技术》——2.1节synchronized同步方法的相关文章

读《Java多线程编程核心技术》一些问题

读<Java多线程编程核心技术>一些问题 1.suspend resume虽然不推荐使用,但是书中还是介绍了,实例中的1-48图 println()方法是同步锁的,当在线程中调用println()方法,线程被suspend()暂停的话, 那么println()的锁也被锁住了.

《Java多线程编程核心技术》——1.2节使用多线程

1.2 使用多线程想学习一个技术就要"接近"它,所以在本节,首先用一个示例来接触一下线程.一个进程正在运行时至少会有1个线程在运行,这种情况在Java中也是存在的.这些线程在后台默默地执行,比如调用public static void main()方法的线程就是这样的,而且它是由JVM创建的.创建示例项目callMainMethodMainThread,创建Test.java类.代码如下: package test; public class Test { public static

《Java多线程编程核心技术》——1.8节暂停线程

1.8 暂停线程 暂停线程意味着此线程还可以恢复运行.在Java多线程中,可以使用suspend()方法暂停线程,使用resume()方法恢复线程的执行. 1.8.1 suspend与resume方法的使用 本节将讲述如何使用suspend与resume方法. 创建测试用的项目suspend_resume_test,文件MyThread.java代码如下: package mythread; public class MyThread extends Thread { private long

《Java多线程编程核心技术》——第2章对象及变量的并发访问

第2章对象及变量的并发访问本章主要介绍Java多线程中的同步,也就是如何在Java语言中写出线程安全的程序,如何在Java语言中解决非线程安全的相关问题.多线程中的同步问题是学习多线程的重中之重,这个技术在其他的编程语言中也涉及,如C++或C#.本章应该着重掌握如下技术点:synchronized对象监视器为Object时的使用.synchronized对象监视器为Class时的使用.非线程安全是如何出现的.关键字volatile的主要作用.关键字volatile与synchronized的区别

《Java多线程编程核心技术》——导读

前 言为什么要写这本书早在几年前笔者就曾想过整理一份与Java多线程有关的稿件,因为市面上所有的Java书籍都是以一章或两章的篇幅介绍多线程技术,并没有完整地覆盖该技术的知识点,但可惜,苦于当时的时间及精力有限,一直没有达成所愿.也许是注定的安排,我目前所在的单位是集技术与教育为一体的软件类企业.我在工作中发现很多学员在学习完JavaSE/JavaEE之后想对更深入的技术进行探索,比如在对大数据.分布式.高并发类的专题进行攻克时,立即遇到针对java.lang包中Thread类的学习,但Thre

《Java多线程编程核心技术》——第1章Java多线程技能

第1章Java多线程技能作为本书的第1章,一定要引导读者快速进入Java多线程的学习,所以本章中主要介绍Thread类中的核心方法.Thread类的核心方法较多,读者应该着重掌握如下关键技术点:线程的启动如何使线程暂停如何使线程停止线程的优先级线程安全相关的问题上面的5点也是本章学习的重点与思路,掌握这些内容是学习Java多线程的必经之路.

《Java多线程编程核心技术》——1.7节停止线程

1.7 停止线程停止线程是在多线程开发时很重要的技术点,掌握此技术可以对线程的停止进行有效的处理.停止线程在Java语言中并不像break语句那样干脆,需要一些技巧性的处理.使用Java内置支持多线程的类设计多线程应用是很常见的事情,然而,多线程给开发人员带来了一些新的挑战,如果处理不好就会导致超出预期的行为并且难以定位错误.本节将讨论如何更好地停止一个线程.停止一个线程意味着在线程处理完任务之前停掉正在做的操作,也就是放弃当前的操作.虽然这看起来非常简单,但是必须做好防范措施,以便达到预期的效

《Java多线程编程核心技术》——1.1节进程和多线程的概念及线程的优点

1.1 进程和多线程的概念及线程的优点 本节主要介绍在Java语言中使用多线程技术.但讲到多线程这个技术时不得不提及"进程"这个概念,"百度百科"里对"进程"的解释如图1-1所示. 初看这段文字会觉得十分的抽象,难以理解,但如果你看到图1-2所示的内容,那么你对进程还不能理解吗? 难道可以将一个正在操作系统中运行的exe程序理解成一个"进程"吗?没错! 通过查看"Windows任务管理器"中的列表,完全可以

《Java多线程编程核心技术》——1.4节isAlive()方法

1.4 isAlive()方法方法isAlive()的功能是判断当前的线程是否处于活动状态.新建项目t7,类文件MyThread.java代码如下: public class MyThread extends Thread { @Override public void run() { System.out.println("run=" + this.isAlive()); } } 运行Run.java代码如下: public class Run { public static voi