持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第24天,点击查看活动详情
与前文衔接的地方
解释:
大家认真看上述代码即注释
我们可以发现写法一 实质上就是把A的实例对象aa或者aa对象的成员或者A类的成员作为监听器 把aa对象的tickets或者A类的tickets作为票池
写法二的实质是把A类的成员作为监听器 A类的tickets作为票池
实际中我们采用写法一
代码示例
class A extends Thread
{
public static int tickets = 10000;
static String str = new String("aa");
public void run()
{
while(true)
{
synchronized(str)
{
if(tickets>0)
{
System.out.printf("%s线程正在卖出第%d张票\n",Thread.currentThread().getName(),tickets);
tickets--;
}
else
{
break;
}
}
}
}
}
public class TestTickets_3
{
public static void main(String[] args)
{
A aa1=new A();
A aa2=new A();
aa1.start();
aa2.start();
/*这种写法类似于TestTickets_2.java里的写法二,必须创建一个公共对象,不推荐使用*/
}
}
解释:
本质和写法二一样,只是之前的两种写法是用Runnable接口实现 这个写法是直接继承Thread类
错误代码1
class A implements Runnable
{
public int tickets = 10000;
public A()
{
System.out.println(this);
}
public synchronized void run()
{
while(true)
{
if(tickets>0)
{
System.out.printf("%s线程正在卖出第%d张票\n",Thread.currentThread().getName(),tickets);
tickets--;
}
else
{
break;
}
}
}
}
public class TestTickets_4
{
public static void main(String[] args)
{
A aa=new A();
Thread t1=new Thread(aa);
Thread t2=new Thread(aa);
t1.start();
t2.start();
}
}
PS:
- 错误写法 对于此程序来说synchronized加在这里会导致 只有一个线程卖票 另一个线程完全不能卖
- 因为放在方法前是保证此方法完全运行 但此方法就是卖10000张票 会导致一个线程把这10000张票全部卖完
- 放在代码块是为了 保证 每卖一张票就必须减一张票 这个才是本程序的真正目的
解释:
- 这里的错误很明显 此时锁定的是整个方法 这样会导致 某一个线程先得到锁 就会直接把票卖完 另一个线程完全陷入阻塞
错误代码2
class A implements Runnable
{
public int tickets = 10000;
public A()
{
System.out.println(this);
}
public void run()
{
//String str=new String("aa");
String str="aa";
while(true)
{
synchronized(str)
{
if(tickets>0)
{
System.out.printf("%s线程正在卖出第%d张票\n",Thread.currentThread().getName(),tickets);
tickets--;
}
else
{
break;
}
}
}
}
}
public class TestTickets_5
{
public static void main(String[] args)
{
A aa=new A();
Thread t1=new Thread(aa);
Thread t2=new Thread(aa);
t1.start();
t2.start();
}
}
PS:
- 错误写法 此时str是局部变量 不再是属性对象
- 正确写法 因为字符串存放在统一的数据区
- 锁定的不是同一个str对象
解释:
- 这个错误也很容易理解 因为此时str变量不是对象的成员变量 只是一个局部变量而局部变量当前代码块运行完毕后就会释放 起不了监听器的作用,但是==String str=“aa”;则可以 原因是这种创建方法的"aa"是在堆中不会随代码块释放 只有当程序运行结束由gc清除,但是String str=new String(“aa”);==就是栈中创建了一个对象会随代码块的结束释放。
线程总结
-
代码块锁是一个防止数据发生错误的一个重要手段;
-
对象的统一性是非常重要的,这要想到对象的传入问题,要操作的对象只能new一次,其他的操作都是对这个传入的对象进行的,才能保证数据一致性,完整性和正确性。
-
保证所有线程使用的都是同一把锁
-
锁可以使用任意一个对象(同一个对象就行)
-
线程之间的通信更方便,同一进程下的线程共享全局变量、静态变量等数据,而进程之间的通信需要以通信的方式(IPC)进行。不过如何处理好同步与互斥是编写多线程程序的难点。
-
但是多进程程序更健壮,多线程程序只要有一个线程死掉,整个进程也死掉了,而一个进程死掉并不会对另外一个进程造成影响,因为进程有自己独立的地址空间。