多线程的弊端
- 共享数据错误
当多个线程共享数据,且共享区有两条以上代码时,线程切换可能发生在共享区,这时很容易引起数据错误
简单的比方就是本来是一批资源被多个人使用,本来应该是一个人使用结束后再下个人使用,但是现在每个人用一会儿,而且每个人都不知道这个资源还有别人使用,自己只能记录自己使用的情况,如果有个产品使用这个资源要三道工序,第一道供需完了后实际资源被给了其他人,那自己再次使用时记录的还是资源之前的状态,就会出现问题
- 解决办法
用同步可以解决这个问题:提供“安全锁”,每次只让一个线程进入共享区,且进入前判断共享区标识,若空则进入,否则等待不能进入
同步的前提:
同步中必须有多个线程且必须使用同一个锁
- 方式一:同步代码块
synchronized(对象)
{
需要同步的代码
} - 方式二:同步函数
直接将函数加上synchronized
注意:同步函数使用的锁是this(当前对象)
(静态同步函数的锁:该函数所属字节码文件对象,可以用getClass方法获取,或是当前方法所属对象.getClass获取)
同步函数和同步代码块的区别
同步函数的锁是固定的this,后者的锁是任意的对象
虽然同步函数更为简洁,但是同步函数只是同步代码块的简写形式,功能体现更明显效率更高的是同步代码块
建议使用同步代码块,面试中也最为常见
eg:
class Ticket implements Runnable
{
private int count = 100;
Object obj = new Object();
public void run()
{
//局部代码块封装可能出现线程安全的代码,然后同步
synchronized(obj)
{
while(count>0)
{
try
{
Thread.sleep(10);
task();
//线程休眠10毫秒,sleep是静态方法,且抛出了InterruptedException异常,但是不能在run方法头上声明
//因为接口Runnable没有声明异常,所以只能用try catch来捕捉异常
}
catch (InterruptedException ex)
{
//处理
}
}
}
}
void task()
{
System.out.println(Thread.currentThread().getName()+"count : "+count--);
}
}
class ThreadSafe
{
public static void main(String[] args)
{
Ticket t = new Ticket();
Thread t1 = new Thread(t);
Thread t2 = new Thread(t);
Thread t3 = new Thread(t);
t1.start();
t2.start();
t3.start();
}
}