本章目录:
1.1.内部变量不存在线程安全问题
1.2.全局变量导致非线程安全
1.3.使用synchronized进行同步
1.4.多个对象多个锁
1.5.脏读
1.6.将任意对象作为对象监视器
1.7.同步synchronized方法无限等待与解决
1.8.多线程死锁
1.9.死循环
2.0.使用关键字volatile更新数据
2.1.使用原子类进行修改操作
2.2.原子类也并不完全安全
1.1.内部变量不存在线程安全问题
当多个线程执行一个方法,方法内部的局部变量并不是临界资源,因为方法是在栈上执行的,而Java栈是线程私有的,因此不会产生线程安全问题。class PrivateObject {
public void add(String username) {
int num = 0;
if ("a".equals(username)) {
num = 100;
System.out.println("a set over");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
} else {
num = 200;
System.out.println("b set over");
}
System.out.println(username + " num = " + num);
}
}
class ThreadA extends Thread{
private PrivateObject object;
public ThreadA(PrivateObject object) {
super();
this.object = object;
}
@Override
public void run() {
super.run();
object.add("a");
}
}
class ThreadB extends Thread{
private PrivateObject object;
public ThreadB(PrivateObject object) {
super();
this.object = object;
}
@Override
public void run() {
super.run();
object.add("b");
}
}
public static void main(String[] args) {
PrivateObject privateObject = new PrivateObject();
ThreadA threadA = new ThreadA(privateObject);
threadA.start();
ThreadB threadB = new ThreadB(privateObject);
threadB.start();
}
运行结果:
a set over
b set over
b num = 200
a num = 100class PrivateObject {
int num = 0;
public void add(String username) {
if ("a".equals(username)) {
num = 100;
System.out.println("a set over");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
} else {
num = 200;
System.out.println("b set over");
}
System.out.println(username + " num = " + num);
}
}
运行结果:
a set over
b set over
b num = 200
a num = 200public synchronized void add(String username) {
if ("a".equals(username)) {
num = 100;
System.out.println("a set over");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
} else {
num = 200;
System.out.println("b set over");
}
System.out.println(username + " num = " + num);
}
在该函数添加了synchronized之后你会发现输出的结果:
a set over
a num = 100
b set over
b num = 200public static void main(String[] args) {
PrivateObject privateObject1 = new PrivateObject();
PrivateObject privateObject2 = new PrivateObject();
ThreadA threadA = new ThreadA(privateObject1);
threadA.start();
ThreadB threadB = new ThreadB(privateObject2);
threadB.start();
}
创建多个实例让线程执行分成多路,这就是异步处理,我们可以看出不管ThreadA是否执行了耗时ThreadB还是会去执行,由于多个实例执行多个同步方法。结果输出:
a set over
b set over
b num = 200
a num = 100class Service {
private String name = "A";
private String password = "AA";
public synchronized void update(String name, String password){
try {
System.out.println("当前线程为:" + Thread.currentThread().getName() + " 在 " + System.currentTimeMillis() + "进入同步");
this.name = name;
Thread.sleep(3000);
this.password = password;
System.out.println("name = " + name + ", password = " + password);
System.out.println("当前线程为:" + Thread.currentThread().getName() + " 在 " + System.currentTimeMillis() + "离开同步");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public String select(){
return "name = " + name + ", password = " + password;
}
}
class MyThread extends Thread {
Service service;
public MyThread(Service service) {
super();
this.service = service;
}
@Override
public void run() {
super.run();
service.update("B", "BB");
}
}
public static void main(String[] args) {
Service service = new Service();
MyThread myThread = new MyThread(service);
myThread.start();
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(service.select());
}
结果:
当前线程为:Thread-0 在 1467706088809进入同步
name = B, password = AA
name = B, password = BB
当前线程为:Thread-0 在 1467706091811离开同步public synchronized String select(){
return "name = " + name + ", password = " + password;
}
结果:
当前线程为:Thread-0 在 1467706347098进入同步
name = B, password = BB
当前线程为:Thread-0 在 1467706350105离开同步
name = B, password = BB1.6.将任意对象作为对象监视器class Service {
String string = new String();
public void update(){
synchronized(string){
try {
System.out.println("当前线程为:" + Thread.currentThread().getName() + " 在 " + System.currentTimeMillis() + "进入同步");
Thread.sleep(3000);
System.out.println("当前线程为:" + Thread.currentThread().getName() + " 在 " + System.currentTimeMillis() + "离开同步");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public static void main(String[] args) {
Service service = new Service();
MyThread myThreadA = new MyThread(service);
myThreadA.start();
MyThread myThreadB = new MyThread(service);
myThreadB.start();
}
输出结果:
当前线程为:Thread-0 在 1467707013916进入同步
当前线程为:Thread-0 在 1467707016925离开同步
当前线程为:Thread-1 在 1467707016925进入同步
当前线程为:Thread-1 在 1467707019926离开同步class Service {
public synchronized void methodA(){
System.out.println("methodA begin");
boolean isContinueRun = true;
while(isContinueRun){
}
System.out.println("methodA end");
}
public synchronized void methodB(){
System.out.println("methodB begin");
System.out.println("methodB end");
}
}
class ThreadA extends Thread {
private Service service;
public ThreadA(Service service) {
this.service = service;
}
@Override
public void run() {
super.run();
service.methodA();
}
}
class ThreadB extends Thread {
private Service service;
public ThreadB(Service service) {
this.service = service;
}
@Override
public void run() {
super.run();
service.methodB();
}
}
public static void main(String[] args) {
Service service = new Service();
ThreadA threadA = new ThreadA(service);
threadA.start();
ThreadB threadB = new ThreadB(service);
threadB.start();
}
运行结果是死循环:
methodA begin那么如何解决死循环呢?那就是不要使用同一对象监视器,也就是使用异步处理线程class Service {
String a = "a";
public void methodA(){
synchronized (a) {
System.out.println("methodA begin");
boolean isContinueRun = true;
while(isContinueRun){
}
System.out.println("methodA end");
}
}
String b = "b";
public void methodB(){
synchronized (b) {
System.out.println("methodB begin");
System.out.println("methodB end");
}
}
}
这样就不会造成死锁了。
methodA begin
methodB begin
methodB end1.8.多线程死锁class DealThread implements Runnable{
public String username;
public Object lock1 = new Object();
public Object lock2 = new Object();
public void setFlag(String username) {
this.username = username;
}
@Override
public void run() {
if("a".equals(username)){
synchronized (lock1) {
System.out.println("username = " + username);
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("a-----");
synchronized (lock2) {
System.out.println("按lock1 -> lock2代码顺序执行了");
}
}
}
if("b".equals(username)){
synchronized (lock2) {
System.out.println("username = " + username);
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("b-----");
synchronized (lock1) {
System.out.println("按lock2 -> lock1代码顺序执行了");
}
}
}
}
}
public static void main(String[] args) {
DealThread t1 = new DealThread();
t1.setFlag("a");
Thread thread1 = new Thread(t1);
thread1.start();
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
t1.setFlag("b");
Thread thread2 = new Thread(t1);
thread2.start();
}
运行结果:
username = a
username = b
a-----
b-----线程1执行了a判断进入锁,锁了lock1对象进入耗时操作,另外一个线程2进入锁了lock2对象也进入了耗时操作,这时,lock1耗时已完毕执行了lock2操作,然而lock2对象还没有解锁,所以线程1被锁死无法释放而继续等待,这时线程2进入lock1对象锁,而lock1却已经发生死锁无法释放,这种情况之下两锁无法释放就会进入死锁状态。class PrintString {
private boolean isContinuePrint = true;
public boolean isContinuePrint() {
return isContinuePrint;
}
public void setContinuePrint(boolean isContinuePrint) {
this.isContinuePrint = isContinuePrint;
}
public void printStrngMethod() {
try {
while (isContinuePrint) {
System.out.println(isContinuePrint);
System.out.println("run printStringMethod threadName = "
+ Thread.currentThread().getName());
Thread.sleep(1000);
}
} catch (Exception e) {
}
}
}
public static void main(String[] args) {
PrintString printString = new PrintString();
printString.printStrngMethod();
System.out.println("我要停止它!stopThread="
+ Thread.currentThread().getName());
printString.setContinuePrint(false);
}
运行结果:
true
run printStringMethod threadName = main
true
run printStringMethod threadName = main
true
run printStringMethod threadName = main
true
run printStringMethod threadName = mainpublic void setContinuePrint(boolean isContinuePrint) {
this.isContinuePrint = isContinuePrint;
}如果在多线程中执行了死循环没有加volatile关键字就算你在主线程public void setContinuePrint(boolean isContinuePrint) {
this.isContinuePrint = isContinuePrint;
}设置了false,也是没有用的,因为你更新的是公有内存的栈,而私有的内存栈没有被修改,所以会出现死循环不停止。那么如何将公有内存的栈去更新私有的栈,去同步数据呢?在私有内存前加volatile关键字。class MyThread extends Thread{
volatile public static int count;
private static void addCount(){
for (int i = 0; i < 100; i++) {
count++;
}
System.out.println("count=" + count);
}
@Override
public void run() {
super.run();
addCount();
}
}
public static void main(String[] args) {
MyThread[] myThreads = new MyThread[100];
for (int i = 0; i < myThreads.length; i++) {
myThreads[i] = new MyThread();
}
for (int i = 0; i < myThreads.length; i++) {
myThreads[i].start();
}
}
运行结果:
count=8924
count=9024
count=9124
count=9224
count=9324
count=9424
count=9524
count=9624
count=9724
count=9824
count=9924值出现了脏读了,可见volatile不能同步函数,那么如何解决这个问题呢?因为要同步函数,那么在函数前加synchronized。class AddCountThread extends Thread {
private AtomicInteger count = new AtomicInteger(0);
@Override
public void run() {
super.run();
for (int i = 0; i < 100; i++) {
System.out.println(count.incrementAndGet());
}
}
}
public static void main(String[] args) {
AddCountThread countThread = new AddCountThread();
Thread t1 = new Thread(countThread);
t1.start();
Thread t2 = new Thread(countThread);
t2.start();
Thread t3 = new Thread(countThread);
t3.start();
Thread t4 = new Thread(countThread);
t4.start();
Thread t5 = new Thread(countThread);
t5.start();
}
运行结果:
492
493
494
495
496
497
498
499
500这样值一样不会出现脏读现象了。class MyService {
public AtomicLong atomicLong = new AtomicLong();
public void addNum() {
System.out.println(Thread.currentThread().getName() + "加了100之后的值就是:"
+ atomicLong.addAndGet(100));
atomicLong.addAndGet(1);
}
}
class MyThread extends Thread {
MyService myService;
public MyThread(MyService myService) {
this.myService = myService;
}
@Override
public void run() {
super.run();
myService.addNum();
}
}
public static void main(String[] args) {
try {
MyService service = new MyService();
MyThread[] myThreads = new MyThread[10];
for (int i = 0; i < myThreads.length; i++) {
myThreads[i] = new MyThread(service);
}
for (int i = 0; i < myThreads.length; i++) {
myThreads[i].start();
}
Thread.sleep(1000);
System.out.println(service.atomicLong.get());
} catch (Exception e) {
}
}
运行结果:
Thread-0加了100之后的值就是:100
Thread-3加了100之后的值就是:300
Thread-1加了100之后的值就是:200
Thread-5加了100之后的值就是:403
Thread-7加了100之后的值就是:504
Thread-9加了100之后的值就是:605
Thread-2加了100之后的值就是:706
Thread-4加了100之后的值就是:807
Thread-6加了100之后的值就是:908
Thread-8加了100之后的值就是:1009
1010值一样出现脏读现象,那么如何解决问题呢?一样使用synchronized关键字同步数据public synchronized void addNum() {
System.out.println(Thread.currentThread().getName() + "加了100之后的值就是:"
+ atomicLong.addAndGet(100));
atomicLong.addAndGet(1);
}
运行结果:
Thread-1加了100之后的值就是:100
Thread-3加了100之后的值就是:201
Thread-5加了100之后的值就是:302
Thread-7加了100之后的值就是:403
Thread-9加了100之后的值就是:504
Thread-0加了100之后的值就是:605
Thread-2加了100之后的值就是:706
Thread-4加了100之后的值就是:807
Thread-6加了100之后的值就是:908
Thread-8加了100之后的值就是:1009
1010关键字synchronized可以保证在同一时刻,只有一个线程可以执行某一个方法或某一个代码块。它包含两个特征:互斥性和可见性。同步synchronized不仅可以解决一个线程看到对象处于不一致的状态,还可以保证进入同步方法或者同步代码块的每个线程,都看到由同一个锁保护之前所有的修改效果。原文:http://blog.csdn.net/aaaaaaaaabaas/article/details/51861670