多线程访问会有什么问题??
public class TestMyThread extends Thread{
//共享变量
private int a = 5;
//重写run方法
@Override
public void run(){
a--;
System.out.println("a is :"+ a);
}
public static void main(String[] args) {
//实列需要多线程的对象
TestMyThread myThread = new TestMyThread();
//创建多个线程实列
Thread t1 = new Thread(myThread,"t1");
Thread t2 = new Thread(myThread,"t2");
Thread t3 = new Thread(myThread,"t3");
Thread t4 = new Thread(myThread,"t4");
Thread t5 = new Thread(myThread,"t5");
//运行
t1.start();
t2.start();
t3.start();
t4.start();
t5.start();
}
}
结果:
a is :3
a is :3
a is :2
a is :1
a is :0a is :4
a is :3
a is :2
a is :1
a is :0
等等,你会发现每次出来的结果都不一样,这就是多线程访问的问题
怎样解决???
package test19218;
import test517designpattern.test;
/**
* @Description
* @Author zengzhiqiang
* @Date 2019年2月18日
*/public class TestMyThread extends Thread{
//共享变量
private int a = 5;
//重写run方法
@Override
public synchronized void run(){
a--;
System.out.println("a is :"+ a);
}
public static void main(String[] args) {
//实列需要多线程的对象
TestMyThread myThread = new TestMyThread();
//创建多个线程实列
Thread t1 = new Thread(myThread,"t1");
Thread t2 = new Thread(myThread,"t2");
Thread t3 = new Thread(myThread,"t3");
Thread t4 = new Thread(myThread,"t4");
Thread t5 = new Thread(myThread,"t5");
//运行
t1.start();
t2.start();
t3.start();
t4.start();
t5.start();
}
}
我们加了synchronize关键字
a is :4
a is :3
a is :2
a is :1
a is :0
结果是我们想要的。
那么synchronize是锁的对象myThread ,还是那一段代码呢?
package test19218;
import test517designpattern.test;
/**
* @Description
* @Author zengzhiqiang
* @Date 2019年2月18日
*/public class TestMyThread extends Thread{
//共享变量
private int a = 5;
//重写run方法
@Override
public synchronized void run(){
a--;
System.out.println("a is :"+ a);
}
public static void main(String[] args) {
//实列需要多线程的对象
TestMyThread myThread = new TestMyThread();
TestMyThread myThread2 = new TestMyThread();
//创建多个线程实列
Thread t1 = new Thread(myThread,"t1");
Thread t2 = new Thread(myThread2,"t2");
//运行
t1.start();
t2.start();
}
}
结果
a is :4
a is :4
说明锁住的是代码。如果是锁住的对象,那么就应该减两次才对!!!
/**
* 关键字synchronized取得的锁都是对象锁,而不是把一段代码(方法)当做锁,
* 所以代码中哪个线程先执行synchronized关键字的方法,哪个线程就持有该方法所属对象的锁(Lock),
*
* 在静态方法上加synchronized关键字,表示锁定.class类,类一级别的锁(独占.class类)。
* @author alienware
*
*/
如果同一个对象,两个方法,一个用synchronize修饰,另一个不用,我访问非synchronize修饰的方法是否需要等待,其实就是是否会阻塞?
package test19218;
/**
* @Description
* @Author zengzhiqiang
* @Date 2019年2月18日
*/public class TestMyThread02 {
String tag;
public TestMyThread02(String tag) {
this.tag = tag;
}public TestMyThread02() {
}// 共享变量
private int a = 5;// 方法
public synchronized void deNum(String tag) throws InterruptedException {
a--;
Thread.sleep(1000);
System.out.println(tag + "---a is :" + a);
}// 方法
public void adNum(String tag) {
a++;
System.out.println(tag + "---a is :" + a);
}public static void main(String[] args) {
final String t1 = "t1";
final String t2 = "t2";// 实列需要多线程的对象
final TestMyThread02 myThread01 = new TestMyThread02();
// final TestMyThread02 myThread02 = new TestMyThread02();// 创建多个线程实列
Thread th = new Thread(new Runnable() {@Override
public void run() {try {
myThread01.deNum(t1);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}}
});Thread th1 = new Thread(new Runnable() {
@Override
public void run() {myThread01.adNum(t2);
}
});// 运行
th.start();
th1.start();
}}
结果
t2---a is :5
t1---a is :5
t2先执行了,所以并不会阻塞。
如果两个方法都加上synchronize呢?
package test19218;
/**
* @Description
* @Author zengzhiqiang
* @Date 2019年2月18日
*/public class TestMyThread02 {
String tag;
public TestMyThread02(String tag) {
this.tag = tag;
}public TestMyThread02() {
}// 共享变量
private int a = 5;// 方法
public synchronized void deNum(String tag) throws InterruptedException {
a--;
Thread.sleep(1000);
System.out.println(tag + "---a is :" + a);
}// 方法
public synchronized void adNum(String tag) {
a++;
System.out.println(tag + "---a is :" + a);
}public static void main(String[] args) {
final String t1 = "t1";
final String t2 = "t2";// 实列需要多线程的对象
final TestMyThread02 myThread01 = new TestMyThread02();
// final TestMyThread02 myThread02 = new TestMyThread02();// 创建多个线程实列
Thread th = new Thread(new Runnable() {@Override
public void run() {try {
myThread01.deNum(t1);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}}
});Thread th1 = new Thread(new Runnable() {
@Override
public void run() {myThread01.adNum(t2);
}
});// 运行
th.start();
th1.start();
}}
t1---a is :4
t2---a is :5
进行阻塞
所以,当同一个对象中的两个方法有逻辑联系的时候,你其中一个加了synchronize,那么你另一个也应该加上,才能保证返回结果的正确。
锁对象改变问题
package test19218;
/**
* @Description
* @Author zengzhiqiang
* @Date 2019年2月18日
*/public class TestMyThread02 {
private String tag = "tag";
public TestMyThread02() {
}// 共享变量
private int a = 5;// 方法
public void deNum() throws InterruptedException {synchronized (tag) {
tag = "change tag";System.out
.println(Thread.currentThread().getName() + "sleep begin");
Thread.sleep(6000);
System.out.println(Thread.currentThread().getName() + "sleep over");
}}
// 方法
public synchronized void adNum(String tag) {
a++;
System.out.println(tag + "---a is :" + a);
}public static void main(String[] args) {
final String t1 = "t1";
final String t2 = "t2";// 实列需要多线程的对象
final TestMyThread02 myThread01 = new TestMyThread02();
// final TestMyThread02 myThread02 = new TestMyThread02();// 创建多个线程实列
Thread th = new Thread(new Runnable() {@Override
public void run() {try {
myThread01.deNum();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}}
}, t1);Thread th1 = new Thread(new Runnable() {
@Override
public void run() {try {
myThread01.deNum();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}}
}, t2);// 运行
th.start();try {
th1.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
th1.start();
}}
th进入锁以后改变对象,th1立刻就进入,没有等t1执行完成
如果不改变锁对象,那么th1将等th执行完成后才进入。
死锁问题,
在设计程序时就应该避免双方相互持有对方的锁的情况
package com.bjsxt.base.sync006;
/**
* 死锁问题,在设计程序时就应该避免双方相互持有对方的锁的情况
* @author alienware
*
*/
public class DeadLock implements Runnable{private String tag;
private static Object lock1 = new Object();
private static Object lock2 = new Object();
public void setTag(String tag){
this.tag = tag;
}
@Override
public void run() {
if(tag.equals("a")){
synchronized (lock1) {
try {
System.out.println("当前线程 : " + Thread.currentThread().getName() + " 进入lock1执行");
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (lock2) {
System.out.println("当前线程 : " + Thread.currentThread().getName() + " 进入lock2执行");
}
}
}
if(tag.equals("b")){
synchronized (lock2) {
try {
System.out.println("当前线程 : " + Thread.currentThread().getName() + " 进入lock2执行");
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (lock1) {
System.out.println("当前线程 : " + Thread.currentThread().getName() + " 进入lock1执行");
}
}
}
}
public static void main(String[] args) {
DeadLock d1 = new DeadLock();
d1.setTag("a");
DeadLock d2 = new DeadLock();
d2.setTag("b");
Thread t1 = new Thread(d1, "t1");
Thread t2 = new Thread(d2, "t2");
t1.start();
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
t2.start();
}
}
同一对象属性的修改不会影响锁的情况
package com.bjsxt.base.sync006;
/**
* 同一对象属性的修改不会影响锁的情况
* @author alienware
*
*/
public class ModifyLock {
private String name ;
private int age ;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public synchronized void changeAttributte(String name, int age) {
try {
System.out.println("当前线程 : " + Thread.currentThread().getName() + " 开始");
this.setName(name);
this.setAge(age);
System.out.println("当前线程 : " + Thread.currentThread().getName() + " 修改对象内容为: "
+ this.getName() + ", " + this.getAge());
Thread.sleep(2000);
System.out.println("当前线程 : " + Thread.currentThread().getName() + " 结束");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
final ModifyLock modifyLock = new ModifyLock();
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
modifyLock.changeAttributte("张三", 20);
}
},"t1");
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
modifyLock.changeAttributte("李四", 21);
}
},"t2");
t1.start();
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
t2.start();
}
}
使用synchronized代码块加锁,比较灵活
package com.bjsxt.base.sync006;
/**
* 使用synchronized代码块加锁,比较灵活
* @author alienware
*
*/
public class ObjectLock {public void method1(){
synchronized (this) { //对象锁
try {
System.out.println("do method1..");
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public void method2(){ //类锁
synchronized (ObjectLock.class) {
try {
System.out.println("do method2..");
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
private Object lock = new Object();
public void method3(){ //任何对象锁
synchronized (lock) {
try {
System.out.println("do method3..");
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
final ObjectLock objLock = new ObjectLock();
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
objLock.method1();
}
});
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
objLock.method2();
}
});
Thread t3 = new Thread(new Runnable() {
@Override
public void run() {
objLock.method3();
}
});
t1.start();
t2.start();
t3.start();
}
}
synchronized代码块对字符串的锁,注意String常量池的缓存功能
package com.bjsxt.base.sync006;
/**
* synchronized代码块对字符串的锁,注意String常量池的缓存功能
* @author alienware
*
*/
public class StringLock {public void method() {
//new String("字符串常量")
synchronized ("字符串常量") {
try {
while(true){
System.out.println("当前线程 : " + Thread.currentThread().getName() + "开始");
Thread.sleep(1000);
System.out.println("当前线程 : " + Thread.currentThread().getName() + "结束");
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
final StringLock stringLock = new StringLock();
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
stringLock.method();
}
},"t1");
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
stringLock.method();
}
},"t2");
t1.start();
t2.start();
}
}
使用synchronized代码块减小锁的粒度,提高性能
package com.bjsxt.base.sync006;
/**
* 使用synchronized代码块减小锁的粒度,提高性能
* @author alienware
*
*/
public class Optimize {public void doLongTimeTask(){
try {
System.out.println("当前线程开始:" + Thread.currentThread().getName() +
", 正在执行一个较长时间的业务操作,其内容不需要同步");
Thread.sleep(2000);
synchronized(this){
System.out.println("当前线程:" + Thread.currentThread().getName() +
", 执行同步代码块,对其同步变量进行操作");
Thread.sleep(1000);
}
System.out.println("当前线程结束:" + Thread.currentThread().getName() +
", 执行完毕");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
final Optimize otz = new Optimize();
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
otz.doLongTimeTask();
}
},"t1");
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
otz.doLongTimeTask();
}
},"t2");
t1.start();
t2.start();
}
}
多个addAndGet在一个方法内是非原子性的,需要加synchronized进行修饰,保证4个addAndGet整体原子性
package other;
import java.util.concurrent.atomic.AtomicInteger;
/**
* @Description
* @Author zengzhiqiang
* @Date 2019年2月19日
*/public class AtomicTest {
//变量为atomic系列
private AtomicInteger num = new AtomicInteger(0);
//多次自增 synchronized
public void addNum(){
int i = num.incrementAndGet();
System.out.println("now num is :" + i);
num.incrementAndGet();
num.incrementAndGet();
num.incrementAndGet();
num.incrementAndGet();
}
public static void main(String[] args) {
//实列需要多线程对象
final AtomicTest test = new AtomicTest();
//起多个线程运行
for(int i = 0;i<=100;i++){
//System.out.println("now time is : "+ i);
Thread t = new Thread(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
test.addNum();
}
});
//启动
t.start();
}
}
}
结果:
now num is :418
now num is :433
now num is :432
结果是乱的,加上synchronize之后
//多次自增 synchronized
public synchronized void addNum(){
int i = num.incrementAndGet();
System.out.println("now num is :" + i);
num.incrementAndGet();
num.incrementAndGet();
num.incrementAndGet();
num.incrementAndGet();
}
now num is :486
now num is :491
now num is :496
now num is :501
输出结果总是一直且有序
volatile关键字不具备synchronized关键字的原子性(同步)
package other;
import java.util.ArrayList;
import java.util.List;import com.mysql.fabric.xmlrpc.base.Array;
/**
* @Description
* @Author zengzhiqiang
* @Date 2019年2月19日
*/public class VolatileTest {
//变量
private volatile int num = 0;
//变量的自增 synchronized
public void addNum(){
num++;
num++;
System.out.println("now num is :" + num);
}
public static void main(String[] args) {
//多线程实列对象
final VolatileTest test = new VolatileTest();
//定义多线程列表
List<Thread> list = new ArrayList<Thread>();
//初始化多个线程
for(int i = 0;i<100;i++){
Thread t = new Thread(new Runnable() {
@Override
public void run() {
test.addNum();
}
});
list.add(t);
}
//启动多线程
for(Thread th:list){
th.start();
}
}
}
结果
now num is :188
now num is :188
now num is :186
now num is :186
无序,显示不正确,但是结果为200,正确
使用synchronize修饰
//变量的自增 synchronized
public synchronized void addNum(){
num++;
num++;
System.out.println("now num is :" + num);
}
结果
now num is :194
now num is :196
now num is :198
now num is :200
有序,显示正确,且结果正确。