一、同步代码块、同步方法
1、同步代码块
synchronized(锁对象){
需要被锁的代码 //线程只有拿到了锁对象,才能执行这里的代码!!!换言之,这里的代码如果执行了,说 明该 线程拿到了锁对象,其他线程不能拿到该锁对象
}
多个线程必须使用同一个锁对象,要不然锁无效
2、同步方法
public synchronized void show(){} //普通方法的锁是this
public static synchronized void show(){} //静态方法的锁是当前类的字节码文件对象 类名.class
3、注意问题
多个线程必须使用同一个锁对象,要不然锁无效
同步代码块锁可以是任意对象
同步方法的锁是this
静态方法的锁是当前类的字节码文件对象 类名.class
二、死锁
死锁原因:线程1占有着A锁,线程2拿着B锁,当线程1要用B锁,线程2要用A锁时,发生死锁。
代码体现
package cn.itcast.thread;
/**
* 线程1类
* @author LBK
*
*/
public class Thread01 extends Thread {
@Override
public void run() {
synchronized (Lock.LOCK_A) {
System.out.println("我是线程1,我拿着锁A,现在需要拿锁B");
synchronized (Lock.LOCK_B) {
System.out.println("我是线程1,成功拿到锁B");
}
}
}
}
package cn.itcast.thread;
/**
* 线程2类
* @author LBK
*
*/
public class Thread02 extends Thread{
@Override
public void run() {
synchronized (Lock.LOCK_B) {
System.out.println("我是线程2,我拿着锁B,现在需要拿锁A");
synchronized (Lock.LOCK_A) {
System.out.println("我是线程2,成功拿到锁A");
}
}
}
}
package cn.itcast.thread;
/**
* 锁类
* @author LBK
*
*/
public class Lock {
public static final Object LOCK_A=new Object();
public static final Object LOCK_B=new Object();
}
package cn.itcast.thread;
/**
* 测试类
* @author LBK
*
*/
public class ThreadTest {
public static void main(String[] args) {
Thread01 t1=new Thread01();
Thread02 t2=new Thread02();
t1.start();
t2.start();
}
}
结果:
右边红色证明程序一直没有停止,发生了死锁。(解决方案在这里先不说了,以后再和大家分享)
三、多个线程操作同一数据的问题(线程间的通讯问题)
其实就是多个线程同时操作同一个对象
卖票案例就是线程间通讯问题
下面是问题代码(因为没有对操作共享数据的代码加入同步代码块)
class Student {
public String name;
public int age;
@Override
public String toString() {
return "Student [name=" + name + ", age=" + age + "]";
}
}
class SetThread extends Thread{
private Student stu;
public SetThread(Student stu){
this.stu = stu;
}
@Override
public void run() {
int i=0;
while(true){
if(i%2 ==0){//执行%2操作,是为了写入不同的数据,测试在写入过程中,是否影响另一个线程的读取操作
stu.name = "张三";
stu.age = 13;
}else{
stu.name = "李四";
stu.age = 14;
}
i++;
}
}
}
class GetThread extends Thread {
private Student stu;
public GetThread(Student stu){
this.stu = stu;
}
@Override
public void run() {
while(true){
System.out.println(stu);
}
}
}
public class Test {
public static void main(String[] args) {
//创建共享数据
Student stu = new Student();
//创建两个线程,并且让这两个线程同时操作这个共享数据
GetThread get = new GetThread(stu);
SetThread set = new SetThread(stu);
get.start();
set.start();
}
}
结果:出现错误数据
错误原因:在SetThread刚做完if赋值时,GetThread抢到,开始执行,此时输出正确,但是当SetThread执行到else中的对年龄赋值时,GetThread抢到开始执行,输出的是张三 ,14。
解决方案1:加锁(把上面两个线程中的共享数据加锁,锁就是stu对象)
class SetThread extends Thread {
private Student stu;
public SetThread(Student stu) {
this.stu = stu;
}
@Override
public void run() {
int i = 0;
while (true) {
synchronized (stu) {
if (i % 2 == 0) {// 执行%2操作,是为了写入不同的数据,测试在写入过程中,是否影响另一个线程的读取操作
stu.name = "张三";
stu.age = 13;
} else {
stu.name = "李四";
stu.age = 14;
}
i++;
}
}
}
}
class GetThread extends Thread {
private Student stu;
public GetThread(Student stu) {
this.stu = stu;
}
@Override
public void run() {
while (true) {
synchronized (stu) {
System.out.println(stu);
}
}
}
}
解决方案2:(等待唤醒机制)请看下篇分解