两个线程有单独的工作内存和共同访问的主内存不同
线程安全的概念:
1.如果多线程访问一个类、对象、方法表现的特征和一个线程访问的结果一致,那么这个类、对象、方法就是线程安全的
2.线程安全问题是由对象的成员变量和类的静态变量引起的
3.每个线程对成员变量和静态变量只有读操作,是线程安全的,有写操作,那么就是线程不安全的
package com.caolh.base;
class User {
private String name;
private String pass;
public User(String name, String pass) {
this.name = name;
this.pass = pass;
}
public synchronized void set(String name, String pass) {
this.name = name;
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
this.pass = pass;
System.out.println(Thread.currentThread().getName() + "-name=" + this.name + "pass=" + this.pass);
}
}
/**
* serlet是单例多线程的、Servlet的本身实现并不是一个单例实现、只是容器加载的时候只实例化一次,所造成的单例现象
*
* @ClassName: UserServlet
* @author: yin.hl
* @date: 2017年6月4日 上午9:04:24
*/
class UserServlet {
private User user;
public UserServlet() {
user = new User("张三", "123456");
}
public void setPass(String name, String pass) {
user.set(name, pass);
}
}
/**
*
*
* @ClassName: DemoThread00
* @author: yin.hl
* @date: 2017年2月19日 下午4:56:07
*/
public class DemoThread00 {
public static void main(String[] args) {
final UserServlet us = new UserServlet();
new Thread(new Runnable() {
@Override
public void run() {
us.setPass("李四", "777777");
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
us.setPass("王五", "888888");
}
}).start();
}
}
如果不加synchronized 那么就线程不安全.
synchronized 的作用是加锁:
synchronized 的方法执行,首先是去获得锁,第一个线程访问获得锁,其余线程等待,第一个线程执行完,释放锁,其余的线程就会 有锁竞争。
二、对象锁和类锁
synchronized 作用在非静态方法上是对象锁,多个线程不存在锁竞争;作用在静态方法是类锁,多线程访问同一对象存在所竞争
public synchronized static void add() { // 如果有static ,则 存在锁竞争,打印结果是有先后顺序;反之,同时打印结果
package com.caolh.base;
public class DemoThread02{
private static int count = 0;
//如果是static变量会怎样?
public synchronized static void add() {
count++;
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+">count="+count);
}
public static void main(String[] args) {
/**
Synchronized是获得对象锁,如果作用在static类型上,则升级为类锁
*/
//内部类无法访问非final对象
/**
* 为什么要用final修饰:
*
* 内部类对象的生命周期会超过局部变量的生命周期。
* 局部变量的生命周期:当该方法被调用时,该方法中的局部变量在栈中被创建,当方法调用结束时,退栈,这些局部变量全部死亡。
* 而内部类对象生命周期与其它类一样:自创建一个匿名内部类对象,系统为该对象分配内存,直到没有引用变量指向分配给该对象的内存,它才会死亡(被JVM垃圾回收)。
* 所以完全可能出现的一种情况是:成员方法已调用结束,局部变量已死亡,但匿名内部类的对象仍然活着。
*
* 匿名内部类对象可以访问同一个方法中被定义为final类型的局部变量。
* 定义为final后,编译程序的实现方法:对于匿名内部类对象要访问的所有final类型局部变量,都拷贝成为该对象中的一个数据成员。
* 这样,即使栈中局部变量已死亡,但被定义为final类型的局部变量的值永远不变,因而匿名内部类对象在局部变量死亡后,照样可以访问final类型的局部变量,因为它自己拷贝了一份,且与原局部变量的值始终一致。
*/
final DemoThread02 thread1 = new DemoThread02();
final DemoThread02 thread2 = new DemoThread02();
Thread t1 = new Thread(new Runnable(){
@Override
public void run() {
thread1.add(); //1.同一个对象、同一把锁
}
}, "thread1");
Thread t2 = new Thread(new Runnable(){
@Override
public void run() {
//thread1.add(); //1、同一个对象、同一把锁
thread2.add();
}
}, "thread2");
t1.start();
t2.start();
}
}
三、对象锁的同步和异步
对象锁只针对synchronized 修饰的方法生效
所以thread1和thread2结果同时打印,print2 方法并未参与锁竞争
public class DemoThread03{
//同步执行
public synchronized void print1() {
System.out.println(Thread.currentThread().getName()+">hello!");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//异步执行
public void print2() {
System.out.println(Thread.currentThread().getName()+">hello!");
}
public static void main(String[] args) throws Exception{
final DemoThread03 thread = new DemoThread03();
Thread t1 = new Thread(new Runnable(){
@Override
public void run() {
thread.print1();
}
}, "thread1");
Thread t2 = new Thread(new Runnable(){
@Override
public void run() {
//thread.print1();
thread.print2();
}
}, "thread2");
t1.start();
t2.start();
}
}
四、锁重入
同一个线程得到一个对象的锁以后,再访问其他的锁方法,不存在锁竞争
public class DemoThread05{
public synchronized void run1(){
System.out.println(Thread.currentThread().getName()+">run1...");
//调用同类中的synchronized方法不会引起死锁
run2();
}
public synchronized void run2(){
System.out.println(Thread.currentThread().getName()+">run2...");
}
public static void main(String[] args) {
final DemoThread05 demoThread05 = new DemoThread05();
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
demoThread05.run1();
}
});
thread.start();
}
}
五、异常释放锁
/**
*
* @Title: DemoThread03.java
* @Prject: DemoThread
* @Package: com.liang.demo
* @author: yin.hl
* @date: 2017年2月19日 下午4:56:07
* @version: V1.0
*/
package com.caolh.base;
/**
* 抛出异常释放锁
* @author: Kevin
* @官网: www.mimaxueyuan.com
* @Q Q群: 660567408
* @Email: mimaxueyuan@163.com
* [每天进步一点点、人生带来大改变...]
* [本代码对应视频地址:http://study.163.com/course/introduction/1004176043.htm]
*/
public class DemoThread07 {
private int i = 0;
public synchronized void run(){
while(true){
i++;
System.out.println(Thread.currentThread().getName()+"-run>i="+i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
int k = 10/0;
if (i == 10) {
throw new RuntimeException();
}
}
}
public synchronized void get(){
System.out.println(Thread.currentThread().getName()+"-get>i="+i);
}
public static void main(String[] args) throws InterruptedException {
final DemoThread07 demoThread07 = new DemoThread07();
new Thread(new Runnable() {
@Override
public void run() {
demoThread07.run();
}
},"t1").start();
//保证t1线程先执行
//因为同时启动两个线程,不确定哪个先执行的
Thread.sleep(1000);
new Thread(new Runnable() {
@Override
public void run() {
demoThread07.get();
}
},"t2").start();
}
}
六、synchronized 代码块
包括 类锁、当前对象锁、任意对象锁,代码块控制锁的粒度更细致
package com.caolh.base;
public class DemoThread08 {
public void run1() {
synchronized (this) {
try {
System.out.println(Thread.currentThread().getName()+">当前对象锁..");
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public void run2() {
synchronized (DemoThread08.class) {
try {
System.out.println(Thread.currentThread().getName()+">类锁..");
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
private Object objectLock = new Object();
public void run3() {
synchronized (objectLock) {
try {
System.out.println(Thread.currentThread().getName()+">任意对象锁..");
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
//测试方法
public static void test(final int type){
if(type==1){
System.out.println("当前对象锁测试...");
}else if(type==2){
System.out.println("类锁测试...");
}else{
System.out.println("任意对象锁测试...");
}
final DemoThread08 demo1 = new DemoThread08();
final DemoThread08 demo2 = new DemoThread08();
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
if(type==1){
demo1.run1();
}else if(type==2){
demo1.run2();
}else{
demo1.run3();
}
}
},"t1");
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
if(type==1){
demo1.run1();
}else if(type==2){
demo2.run2();
}else{
demo1.run3();
}
}
},"t2");
t1.start();
t2.start();
}
public static void main(String[] args) {
// test(1);
// test(2);
// test(3);
final DemoThread08 demo1 = new DemoThread08();
final DemoThread08 demo2 = new DemoThread08();
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
demo1.run2();
}
},"t1");
t1.start();
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
demo2.run1();
}
},"t2");
t2.start();
}
}