方法内的变量为线程安全
--如果是方法内部的私有变量,则不会存在“非线程安全”问题,所得结果也就是“线程安全”的了。
示例:
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) {
e.printStackTrace();
}
}
}
class ThreadA extends Thread {
private HasSelfPrivateNum hasSelfPrivateNum;
public ThreadA(HasSelfPrivateNum numRef) {
super();
this.hasSelfPrivateNum = numRef;
}
@Override
public void run() {
super.run();
hasSelfPrivateNum.addI("a");
}
}
class ThreadB extends Thread {
private HasSelfPrivateNum hasSelfPrivateNum;
public ThreadB(HasSelfPrivateNum numRef) {
super();
this.hasSelfPrivateNum = numRef;
}
@Override
public void run() {
super.run();
hasSelfPrivateNum.addI("b");
}
}
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();
}
}
output:线程A启动,num的值为100,设值线程沉睡1秒,B线程启动,则num的值变为200,但num的值是私有变量,并没有出现脏读,输出结果正常。
实例变量非线程安全
--如果多个线程共同访问1个对象的实例变量,则可能出现“非线程安全”
示例:更改HasSelfPrivateNum类,其他类不变
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");
}else {
num =200;
System.out.println("b set over");
}
System.out.println(username + "num=" + num);
}catch(InterruptedException e ){
e.printStackTrace();
}
}
}
output:出现“非线程安全”
示例:通过synchronized关键字解决
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");
}else {
num =200;
System.out.println("b set over");
}
System.out.println(username + "num=" + num);
}catch(InterruptedException e ){
e.printStackTrace();
}
}
}
output:
多个对象多个锁
示例:更改Run类,创建两个对象
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();
}
}
output:显示的结果是交叉运行的,因为多个线程访问多个对象,则JVM会创建多个锁(运行发现,因为两个对象,所以此处不加synchronized也不会出现“非线程安全”,只有共享的资源读写访问才需要同步化,如果不是共享资源,那么根本不需要同步化)
synchronized方法与锁对象
--A线程先持有对象的Lock锁,B线程可以以异步的方式调用Object对象中的非synchronized类型的方法
--A线程先持有object对象的Lock锁,B线程如果在这是调用object对象中的synchronized类型的方法则需要等待,也就是同步。
示例:
public class MyObject {
synchronized public void methodA() {
try {
System.out.println("begin methodA threadName=" + Thread.currentThread().getName());
Thread.sleep(2000);
System.out.println("end a");
} catch (Exception e) {
}
}
public void methodB() {
try {
System.out.println("begin methodB threadName=" + Thread.currentThread().getName());
Thread.sleep(2000);
System.out.println("end b");
} catch (Exception e) {
}
}
}
class ThreadA extends Thread {
private MyObject myObject;
public ThreadA(MyObject myObject) {
super();
this.myObject = myObject;
}
@Override
public void run() {
super.run();
myObject.methodA();
}
}
class ThreadB extends Thread {
private MyObject myObject;
public ThreadB(MyObject myObject) {
super();
this.myObject = myObject;
}
@Override
public void run() {
super.run();
myObject.methodB();
}
}
class Run {
public static void main(String[] args) {
MyObject myObject = new MyObject();
ThreadA threadA = new ThreadA(myObject);
ThreadB threadB = new ThreadB(myObject);
threadA.start();
threadB.start();
}
}
output:
更改MyObject类
public class MyObject {
synchronized public void methodA() {
try {
System.out.println("begin methodA threadName=" + Thread.currentThread().getName());
Thread.sleep(2000);
System.out.println("end a");
} catch (Exception e) {
}
}
synchronized public void methodB() {
try {
System.out.println("begin methodB threadName=" + Thread.currentThread().getName());
Thread.sleep(2000);
System.out.println("end b");
} catch (Exception e) {
}
}
}
output:
synchronized 锁重入
--当一个线程得到一个对象锁后,再次请求对象锁时,是可以再次得到该对象的锁。在一个synchronized 方法/块 的内部调用本类的其他synchronized方法/块时,是永远可以得到锁的
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");
}
}
class ThreadA extends Thread {
Service service;
public ThreadA(Service service) {
super();
this.service = service;
}
@Override
public void run() {
service.service1();
}
}
class Run {
public static void main(String[] args) {
Service service = new Service();
ThreadA threadA = new ThreadA(service);
threadA.start();
}
}
output:
出现异常,锁自动释放
示例:
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) {
Integer.parseInt("a");
}
} else {
System.out.println("Thread B run");
}
}
}
class ThreadA extends Thread {
private Service service;
public ThreadA(Service service) {
super();
this.service = service;
}
@Override
public void run() {
service.testMethod();
}
}
class ThreadB extends Thread {
private Service service;
public ThreadB(Service service) {
super();
this.service = service;
}
@Override
public void run() {
service.testMethod();
}
}
class Run {
public static void main(String[] args) throws Exception {
Service service = new Service();
ThreadA threadA = new ThreadA(service);
threadA.setName("a");
threadA.start();
Thread.sleep(3030);
ThreadB threadB = new ThreadB(service);
threadA.setName("b");
threadB.start();
}
}
output:线程a出现异常并释放锁,线程b进入方法正常打印,实验的结论就是出现异常的锁被自动释放了。
同步不具有继承性
示例:
public class Main {
synchronized public void serviceMethod() {
System.out.println("int main 下一步sleep begin threadName=" + Thread.currentThread().getName() + " time=" + System.currentTimeMillis());
System.out.println("int main 下一步sleep sleep end threadName=" + Thread.currentThread().getName() + " time=" + System.currentTimeMillis());
}
}
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(4000);
System.out.println("int sub 下一步sleep end threadName=" + Thread.currentThread().getName() + " time=" + System.currentTimeMillis());
super.serviceMethod();
} catch (Exception e) {
}
}
}
class MyThreadA extends Thread {
private Sub sub;
public MyThreadA(Sub sub) {
super();
this.sub = sub;
}
@Override
public void run() {
sub.serviceMethod();
}
}
class MyThreadB extends Thread {
private Sub sub;
public MyThreadB(Sub sub) {
super();
this.sub = sub;
}
@Override
public void run() {
sub.serviceMethod();
}
}
class Test {
public static void main(String[] args) {
Sub sub = new Sub();
MyThreadA threadA = new MyThreadA(sub);
MyThreadB threadB = new MyThreadB(sub);
threadA.setName("A");
threadB.setName("B");
threadA.start();
threadB.start();
}
}
output:
synchronized同步语句块
--用关键字synchronized声明方法在某些情况下是有弊端的,比如A线程调用同步方法执行一个时间的任务,那么B线程则必须等待较长的时间,在这样的情况下可以使用synchronized同步语句块来解决。
--synchronized方法是对当前对象加锁,而synchronized代码块是对某一个对象加锁。
public class Task {
private String getDate1;
private String getDate2;
public synchronized void doLongTimeTask() {
try {
System.out.println("begin task");
Thread.sleep(3000);
getDate1 = "长时间处理任务后从远程返回的值1 threadName=" + Thread.currentThread().getName();
getDate2 = "长时间处理任务后从远程返回的值2 threadName=" + Thread.currentThread().getName();
System.out.println(getDate1);
System.out.println(getDate2);
System.out.println("end task");
} catch (Exception e) {
}
}
}
class CommonUtils {
public static long beginTime;
public static long endTime;
public static long beginTime2;
public static long endTime2;
}
class MyThread1 extends Thread {
private Task task;
public MyThread1(Task task) {
super();
this.task = task;
}
@Override
public void run() {
super.run();
CommonUtils.beginTime = System.currentTimeMillis();
task.doLongTimeTask();
CommonUtils.endTime = System.currentTimeMillis();
}
}
class MyThread2 extends Thread {
private Task task;
public MyThread2(Task task) {
super();
this.task = task;
}
@Override
public void run() {
super.run();
CommonUtils.beginTime2 = System.currentTimeMillis();
task.doLongTimeTask();
CommonUtils.endTime2 = System.currentTimeMillis();
}
}
class Test2 {
public static void main(String[] args) {
Task task = new Task();
MyThread1 thread1 = new MyThread1(task);
thread1.start();
MyThread2 thread2 = new MyThread2(task);
thread2.start();
try {
Thread.sleep(10000);
} catch (Exception e) {
}
long beginTime = CommonUtils.beginTime;
if (CommonUtils.beginTime2 < CommonUtils.beginTime) {
beginTime = CommonUtils.beginTime2;
}
long endTime = CommonUtils.endTime;
if (CommonUtils.endTime2 > CommonUtils.endTime) {
endTime = CommonUtils.endTime2;
}
System.out.println("耗时:" + (endTime - beginTime) / 1000);
}
}
output:
用同步代码块解决同步方法的弊端
更改文件Task.java文件
public class Task {
private String getDate1;
private String getDate2;
public void doLongTimeTask() {
try {
System.out.println("begin task");
Thread.sleep(3000);
String gd1 = "长时间处理任务后从远程返回的值1 threadName=" + Thread.currentThread().getName();
String gd2 = "长时间处理任务后从远程返回的值2 threadName=" + Thread.currentThread().getName();
synchronized (this) {
getDate1 = gd1;
getDate2 = gd2;
}
synchronized (this) {
System.out.println(getDate1);
System.out.println(getDate2);
System.out.println("end task");
}
} catch (Exception e) {
}
}
}
output:
synchronized代码块间的同步性
--当一个线程访问object的一个synchronized(this)同步代码块时,其他线程对同一个object中所有其他synchronized(this)同步代码块的访问将被阻塞,这说明synchronized使用的“对象监视器”是一个。
public class ObjectService {
public void serviceMethodA() {
try {
synchronized (this) {
System.out.println("A begin time=" + System.currentTimeMillis());
Thread.sleep(2000);
System.out.println("A end end=" + System.currentTimeMillis());
}
} catch (Exception e) {
e.printStackTrace();
}
}
public void serviceMethodB() {
synchronized (this) {
System.out.println("B begin time=" + System.currentTimeMillis());
System.out.println("B end end=" + System.currentTimeMillis());
}
}
}
class ThreadA extends Thread {
private ObjectService service;
public ThreadA(ObjectService service) {
super();
this.service = service;
}
@Override
public void run() {
super.run();
service.serviceMethodA();
}
}
class ThreadB extends Thread {
private ObjectService service;
public ThreadB(ObjectService service) {
super();
this.service = service;
}
@Override
public void run() {
super.run();
service.serviceMethodB();
}
}
class Run {
public static void main(String[] args) {
ObjectService service = new ObjectService();
ThreadA a = new ThreadA(service);
a.setName("a");
a.start();
ThreadB b = new ThreadB(service);
b.setName("b");
b.start();
}
}
output:
将任意对象作为对象监听器
--使用“synchronized(非this对象x)同步代码块”格式进行同步操作时,对象监听器必须是同一个对象。如果不是同一个对象监视器,运行的结果是异步调用了,就会交叉运行。
静态同步synchronized方法与synchronized(class)代码块
--将synchronized关键字加到非static方法上使用的效果是一样的。synchronized关键字加到static静态方法上是给Class类上锁,而synchronized关键字加到非static静态方法上是给对象上锁。
数据类型String的常量池特性
示例:
public class Service2 {
public void print(String str) {
try {
synchronized (str) {
while (true) {
System.out.println(Thread.currentThread().getName());
Thread.sleep(1000);
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
class ThreadA extends Thread {
private Service2 service2;
public ThreadA(Service2 service2) {
super();
this.service2 = service2;
}
@Override
public void run() {
service2.print("AA");
}
}
class ThreadB extends Thread {
private Service2 service2;
public ThreadB(Service2 service2) {
super();
this.service2 = service2;
}
@Override
public void run() {
service2.print("AA");
}
}
class Run {
public static void main(String[] args) {
Service2 service2 = new Service2();
ThreadA a = new ThreadA(service2);
a.setName("A");
a.start();
ThreadB b = new ThreadB(service2);
b.setName("B");
b.start();
}
}
output:出现这样的情况就是因为String的两个值都是AA,两个线程持有相同的锁,所以造成线程B不能执行。这就是String常量池所带来的问题。因此在绝大数情况下,同步synchronized代码块都不适用String作为锁对象,而改用其他,不如new Object()实例化一个Object对象,但它并不放入缓存中。
更改代码如下:
public class Service2 {
public void print(Object object) {
try {
synchronized (object) {
while (true) {
System.out.println(Thread.currentThread().getName());
Thread.sleep(1000);
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
class ThreadA extends Thread {
private Service2 service2;
public ThreadA(Service2 service2) {
super();
this.service2 = service2;
}
@Override
public void run() {
service2.print(new Object());
}
}
class ThreadB extends Thread {
private Service2 service2;
public ThreadB(Service2 service2) {
super();
this.service2 = service2;
}
@Override
public void run() {
service2.print(new Object());
}
}
class Run {
public static void main(String[] args) {
Service2 service2 = new Service2();
ThreadA a = new ThreadA(service2);
a.setName("A");
a.start();
ThreadB b = new ThreadB(service2);
b.setName("B");
b.start();
}
}
output:
同步synchronized方法无限等待与解决
示例:同步方法容易造成死循环,b线程永远无法运行
public class Service {
synchronized public void methodA() {
System.out.println("methodA begin");
boolean isContinueRun = true;
while (isContinueRun) {
}
System.out.println("methodA end");
}
synchronized public void methodB() {
System.out.println("methodB begin");
System.out.println("methodB end");
}
}
class ThreadA extends Thread {
private Service service;
public ThreadA(Service service) {
super();
this.service = service;
}
@Override
public void run() {
service.methodA();
}
}
class ThreadB extends Thread {
private Service service;
public ThreadB(Service service) {
super();
this.service = service;
}
@Override
public void run() {
service.methodB();
}
}
class Test {
public static void main(String[] args) {
Service service = new Service();
ThreadA threadA = new ThreadA(service);
threadA.setName("A");
threadA.start();
ThreadB threadB = new ThreadB(service);
threadB.setName("B");
threadB.start();
}
}
output:
通过同步块来解决这样的问题
public class Service {
public void methodA() {
synchronized (new Object()) {
System.out.println("methodA begin");
boolean isContinueRun = true;
while (isContinueRun) {
}
System.out.println("methodA end");
}
}
synchronized public void methodB() {
System.out.println("methodB begin");
System.out.println("methodB end");
}
}
class ThreadA extends Thread {
private Service service;
public ThreadA(Service service) {
super();
this.service = service;
}
@Override
public void run() {
service.methodA();
}
}
class ThreadB extends Thread {
private Service service;
public ThreadB(Service service) {
super();
this.service = service;
}
@Override
public void run() {
service.methodB();
}
}
class Test {
public static void main(String[] args) {
Service service = new Service();
ThreadA threadA = new ThreadA(service);
threadA.setName("A");
threadA.start();
ThreadB threadB = new ThreadB(service);
threadB.setName("B");
threadB.start();
}
}
output:
volatile关键字
示例:
public class PrintString {
private boolean isContinuePrint = true;
public boolean isContinuePrint() {
return isContinuePrint;
}
public void setContinuePrint(boolean isContinuePrint) {
this.isContinuePrint = isContinuePrint;
}
public void printStringMethod() {
try {
while (isContinuePrint == true) {
System.out.println("run printStringMethod threadName=" + Thread.currentThread().getName());
Thread.sleep(1000);
}
} catch (Exception e) {
}
}
}
class Run {
public static void main(String[] args) {
PrintString printString = new PrintString();
printString.printStringMethod();
System.out.println("我要停止它!" + Thread.currentThread().getName());
printString.setContinuePrint(false);
}
}
output:程序运行后,根本停不下来,如图所示
使用多线程技术:
public class PrintString implements Runnable {
private boolean isContinuePrint = true;
public boolean isContinuePrint() {
return isContinuePrint;
}
public void setContinuePrint(boolean isContinuePrint) {
this.isContinuePrint = isContinuePrint;
}
public void printStringMethod() {
try {
while (isContinuePrint == true) {
System.out.println("run printStringMethod threadName=" + Thread.currentThread().getName());
Thread.sleep(1000);
}
} catch (Exception e) {
}
}
@Override
public void run() {
printStringMethod();
}
}
class Run {
public static void main(String[] args) {
PrintString printString = new PrintString();
new Thread(printString).start();
System.out.println("我要停止它!" + Thread.currentThread().getName());
printString.setContinuePrint(false);
}
}
output:
当上面的示例代码运行在-server服务器模式中64bit的JVM上时,会出现死循环,解决的办法是使用volatile关键字。
关键字volatile的作用是强制从公共堆栈中取得变量的值,而不是从线程私有数据栈中取得变量的值。
synchronized和volatile比较
--关键字volatile是线程同步的轻量级实现,所以volatile性能肯定比synchronized要好,并且volatile只能修饰变量,而synchronized可以修饰方法,以及代码块。
--多线程访问volatile不会发生阻塞,而synchronized会出现阻塞
--volatile能保证数据的可见性,但不能保证原子性;而synchronized可以保证原子性,也可以间接保证可见性,因为它会将私有内存和公共内存的数据做同步。
synchronized代码块有volatile同步的功能
示例:
public class Service {
private boolean isContinueRun = true;
public void runMethod() {
String anyString = new String();
while (isContinueRun == true) {
synchronized (anyString) {
}
}
System.out.println("停下来了!");
}
public void stopMethod() {
isContinueRun = false;
}
}
class ThreadA extends Thread {
private Service service;
public ThreadA(Service service) {
super();
this.service = service;
}
@Override
public void run() {
service.runMethod();
}
}
class ThreadB extends Thread {
private Service service;
public ThreadB(Service service) {
super();
this.service = service;
}
@Override
public void run() {
service.stopMethod();
}
}
class Run {
public static void main(String[] args) {
try {
Service service = new Service();
ThreadA a = new ThreadA(service);
a.start();
Thread.sleep(1000);
ThreadB b = new ThreadB(service);
b.start();
System.out.println("已经发起停止的命令了");
} catch (Exception e) {
}
}
}
output:
参考:《Java多线程编程核心技术》--机械工业出版社 高洪岩 著