------- android培训、java培训、期待与您交流! ----------
1:同步方法
A:当一个方法的方法体都被同步代码块给包围起来了,
我们就可以使用另外一种方式来表示。就是同步方法。
B:同步方法的使用格式非常简单。就是在方法声明上加同步关键字。
C:同步方法的锁是什么呢?如何测试的呢?
同步方法的锁是this对象。
通过在run里面同时使用同步方法和同步代码块来测试的。
D:静态方法的锁是什么呢?
静态方法的锁是当前类的class文件描述类对象
思考:单例设计模式懒汉式为什么会出现线程安全问题。
class Student
{
private Student(){}
private static Student s = null;
public static Student getInstance()
{
if(s==null)
{
s = new Student();
}
return s;
}
}
由于其存在着线程安全问题,所以,我们考虑改进。改进如下
class Student
{
private Student(){}
private static Student s = null;
public static synchronized Student getInstance()
{
if(s==null)
{
s = new Student();
}
return s;
}
}
那么,我们到底是使用同步方法还是使用同步代码块呢?
练习:
有两个老师选择学生,每个老师选择一名男生一名女生,然后由第二个老师选,
反复如此。选择完毕后,我们可以打印一下看每个老师选择的男生和女生人数是否相等。
用多线程模拟一下。
class Student
{
private int boy = 0;
private int girl = 0;
void add()
{
boy++;
girl++;
System.out.println(boy+"***"+girl+":"+(boy==girl));
}
}
class Teacher implements Runnable
{
Student s = new Student();
public void run()
{
while(true)
{
s.add();
}
}
}
class StudentTest
{
public static void main(String[] args)
{
Teacher t = new Teacher();
Thread t1 = new Thread(t);
Thread t2 = new Thread(t);
t1.start();
t2.start();
}
}
2:死锁问题
A:产生原因
A锁和B锁互相等待。
B:解决方案
不写出死锁的代码。
3:线程间的通信(交互)
有一个学生对象,我们分别用两个线程进行输入和输出数据。
第一版本:
class Student
{
String name;
int age;
}
class InputStudent implements Runnable
{
Student s = new Student();
public void run()
{
s.name = "Jack";
s.age = 20;
}
}
class OutputStudent implements Runnable
{
Student s = new Student();
public void run()
{
sop(s.name+"***"+s.age);
}
}
class StudentTest
{
public static void main(String[] args)
{
InputStudent is = new InputStudent();
OutputStudent os = new OutputStudent();
Thread t1 = new Thread(is);
Thread t2 = new Thread(os);
t1.start();
t2.start();
}
}
第一版本的问题:
第一个:学生对象是个资源对象,是被输入和输出线程共享的。
而现在,他们确实两个对象。
第二个:线程的操作,一般只有多次执行才能较好的看到效果。
所以,我们在run方法里面加入循环
第二版本:
class InputStudent implements Runnable
{
private Student s;
public InputStudent(){}
public InputStudent(Student s)
{
this.s = s;
}
public void run()
{
int x = 0;
while(true)
{
if(x%2==0)
{
s.name = "Jack";
s.age = 20;
}
else
{
s.name = "Rose";
s.age = 25;
}
x++;
}
}
}
class OutputStudent implements Runnable
{
private Student s;
public OutputStudent(){}
public OutputStudent(Student s)
{
this.s = s;
}
public void run()
{
while(true)
{
sop(s.name+"***"+s.age);
}
}
}
class StudentTest
{
public static void main(String[] args)
{
Student s = new Student();
InputStudent is = new InputStudent(s);
OutputStudent os = new OutputStudent(s);
Thread t1 = new Thread(is);
Thread t2 = new Thread(os);
t1.start();
t2.start();
}
}
第二版问题:
第一个:数据出现了不匹配的问题。
思考,怎么产生的?由于多线程操作的随机性导致的。
解决方案:加入同步,并且用s这个对象锁。
对多个线程都要加锁,并且是同一把锁。
第三版:
class InputStudent implements Runnable
{
private Student s;
public InputStudent(){}
public InputStudent(Student s)
{
this.s = s;
}
public void run()
{
int x = 0;
while(true)
{
synchronized(s)
{
if(x%2==0)
{
s.name = "Jack";
s.age = 20;
}
else
{
s.name = "Rose";
s.age = 25;
}
}
x++;
}
}
}
class OutputStudent implements Runnable
{
private Student s;
public OutputStudent(){}
public OutputStudent(Student s)
{
this.s = s;
}
public void run()
{
while(true)
{
synchronized(s)
{
sop(s.name+"***"+s.age);
}
}
}
}
到此为止,我们解决了线程的安全问题。但是这个程序还是有不足之处。
第三版的问题:
数据打印是一片一片的。所以,需要改进。
改进的时候,我们思考到了两种方案:sleep和wait。
最终选择了wait。
第四版:
class Student
{
String name;
int age;
boolean flag = false;
}
class InputStudent implements Runnable
{
private Student s;
public InputStudent(){}
public InputStudent(Student s)
{
this.s = s;
}
public void run()
{
int x = 0;
while(true)
{
synchronized(s)
{
if(s.flag)
{
try
{
s.wait();
}
catch(InterruptedException e)
{
e.printStackTrace();
}
}
if(x%2==0)
{
s.name = "Jack";
s.age = 20;
}
else
{
s.name = "Rose";
s.age = 25;
}
//改变标记。唤醒线程
s.flag = true;
s.notify();
}
x++;
}
}
}
class OutputStudent implements Runnable
{
private Student s;
public OutputStudent(){}
public OutputStudent(Student s)
{
this.s = s;
}
public void run()
{
while(true)
{
synchronized(s)
{
if(!s.flag)
{
try
{
s.wait();
}
catch(InterruptedException e)
{
e.printStackTrace();
}
}
sop(s.name+"***"+s.age);
s.flag = false;
s.notify();
}
}
}
}
wait:让线程等待
notify:唤醒单个线程
notifyAll:唤醒所有线程
举例:抓人游戏。
4:wait和sleep的区别
A:对于时间的指定
sleep()必须指定时间。
wait()方法有重载的形式,可以指定时间,也可以不指定时间。
B:对执行权和锁的释放
sleep释放执行权,不释放锁。
wait释放执行权,释放锁。
5:停止线程
A:run自动结束。
B:stop方法。但是已经过时了。被interrupt替代。
C:可以通过控制循环条件来结束。
6:Thread类中的几个方法
A:interrupt():中断线程,并抛出一个异常
B:setDaemon(boolean b):设置线程为守护线程。(坦克大战)
C:join():加入线程,具有优先执行权
D:setPriority(int num):设置线程优先级。线程的优先级从1到10,默认为5。
E:toString():返回该线程的字符串表示形式,包括线程名称、优先级和线程组。
F:yield():给其他线程让路,让多个线程间的操作趋近于平衡。