1.同步代码块
语法如下:
synchronized (对象)
{
要调用的代码;
}
如要完成同步,此处的对象,必须是所有要同步的线程共享(同一个锁)的一个对象。
范例
package 多线程基础;
public class ThreadSynchorization_2
{
public static void main( String[] args )
{
TestThreads t = new TestThreads();
//启动4个线程,都用的是一个对象,即 t 这个实例化对象
new Thread( t ).start();
new Thread( t ).start();
new Thread( t ).start();
new Thread( t ).start();
}
}
class TestThreads implements Runnable
{
private int tickets = 20;
public void run()
{
while( true )
{
/*形成了同步代码块。在同一时刻只能有一个线程可以进入同步代码块内运行煤制油当该线程离开同步代码块后,
* 其他线程才能才能进入同步代码块运行。
*
*/
synchronized( this )
{
if( tickets <= 0 )
break;
try
{
Thread.sleep( 100 );
}
catch( Exception e )
{
e.printStackTrace();
}
System.out.println( Thread.currentThread().getName() + "出售票"
+ tickets );
tickets -= 1;
}
}
}
}
输出:
Thread-0出售票20
Thread-0出售票19
Thread-0出售票18
Thread-0出售票17
Thread-0出售票16
Thread-0出售票15
Thread-0出售票14
Thread-0出售票13
Thread-0出售票12
Thread-0出售票11
Thread-0出售票10
Thread-0出售票9
Thread-0出售票8
Thread-0出售票7
Thread-0出售票6
Thread-0出售票5
Thread-0出售票4
Thread-0出售票3
Thread-0出售票2
Thread-0出售票1
虽然都是一个线程买的票,这是负载不均衡的情况,但我们实现了一票一卖的效果。
2.同步方法
除了对代码块进行同步外,也可以对方法实现同步,只要是需要同步的方法的方法定义前面加上synchronized关键字即可。
语法如下
访问控制符 synchronized 返回值类型方法名称(参数)
{
....;
}
或者
访问控制符 返回值类型 方法名称(参数)
{
synchronized (this)//{ }内的为同步代码块
{
.....;
}
}
范例:
package 多线程基础;
public class ThreadSynchorization_3 {
public static void main(String[] args) {
t t=new t();
new Thread(t).start();
new Thread(t).start();
new Thread(t).start();
new Thread(t).start();
}
}
class t implements Runnable
{
private int tickets=20;
public void run()
{
while(tickets>0)
{
sale();
}
}
public synchronized void sale ()
{
if(tickets>0)
{
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"出售票"+tickets);
tickets-=1;
}
}
}
上面的代码中sale( )也可以写成这样
public void sale ()
{synchronized(this)
{
if(tickets>0)
{
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"出售票"+tickets);
tickets-=1;
}
}
}
输出:
Thread-3出售票20
Thread-3出售票19
Thread-3出售票18
Thread-3出售票17
Thread-3出售票16
Thread-3出售票15
Thread-3出售票14
Thread-3出售票13
Thread-3出售票12
Thread-3出售票11
Thread-3出售票10
Thread-3出售票9
Thread-3出售票8
Thread-3出售票7
Thread-3出售票6
Thread-3出售票5
Thread-3出售票4
Thread-3出售票3
Thread-3出售票2
Thread-3出售票1
可算不是0线程了,-_-||。
3.死锁
如果有一组进程(或线程),其中的每一个都被无限等待被该组进程(或线程)中另一进程(或线程)所占有的资源,就会产生无限期地僵持下去的局面,这种现象称为死锁。
模拟死锁
构建了三个子类,静态类A和B以及掩饰类Demo。类A继承父类Thread,并覆写了run()方法,用以模拟甲占据资源“餐刀”,而又去申请资源“叉子”。类似地,类B也承父类Thread,并覆写了run()方法,用以模拟乙占据资源“叉子”,而又去申请资源“餐刀”。Demo类模拟一个守护线程,用来显示线程执行的状况。
在类A和B中定义了多个用synchronized修饰的方法。但一个线程获得了其中一个方法的锁后,它将同时获得该类中其他方法的锁。这是因为synchronized锁住的是对象,该方法所属的对象就是临界资源。例如类A中synchronized(knife) 的knife 和 类B中synchronized(fork)的fork都是临界资源。
package 多线程基础;
import javax.swing.plaf.synth.SynthColorChooserUI;
@SuppressWarnings("unused")
public class DeadLock
{
static String fork="叉子",knife="餐刀";
static class A extends Thread
{
public void run()
{
synchronized (knife) {
System.out.println("甲拿起了"+knife+",等待"+fork+"...");
try {
Thread.sleep(100);//让相应的线程都睡眠一会,让对方有机会获得部分资源,从而强制死锁条件出现
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (fork) {//由于死锁的原因该段永远不会被执行
System.out.println("甲又拿起了"+fork);
}
}
}
}
static class B extends Thread
{
public void run()
{
synchronized (fork) {
System.out.println("乙拿起了"+fork+",等待"+knife+"...");
try {
Thread.sleep(1000);//同类A中的sleep()
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (knife) {//由于死锁的原因该段永远不会被执行
System.out.println("乙又拿起了"+knife);
}
}
}
}
static class Demo extends Thread
{
public Demo()
{
this.setDaemon(true);
}
public void run()
{
while(true)
{
try {
Thread.sleep(1000);//每隔1s输出一下线程状态
} catch (InterruptedException e) {}
System.out.println("守护线程在运行...");
}
}
}
public static void main(String[] args)
{
// TODO Auto-generated method stub
new Demo().start();
new A().start();
new B().start();
}
}
输出:
甲拿起了餐刀,等待叉子...
乙拿起了叉子,等待餐刀...
守护线程在运行...
守护线程在运行...
守护线程在运行...
守护线程在运行...
守护线程在运行...
守护线程在运行...
守护线程在运行...
守护线程在运行...
........
守护线程在运行...会无限输出,因为此时出现了死锁。
修改一下,如果规定A和B必须先拿起餐刀,然后再拿起叉子就不会发生死锁
package 多线程基础;
import javax.swing.plaf.synth.SynthColorChooserUI;
@SuppressWarnings("unused")
public class DeadLock
{
static String fork="叉子",knife="餐刀";
static class A extends Thread
{
public void run()
{
synchronized (knife) {
System.out.println("甲拿起了"+knife+",等待"+fork+"...");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (fork) {
System.out.println("甲又拿起了"+fork);
}
}
}
}
static class B extends Thread
{
public void run()
{
synchronized (knife) {
System.out.println("乙拿起了"+knife+",等待"+fork+"...");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (fork) {
System.out.println("乙又拿起了"+fork);
}
}
}
}
static class Demo extends Thread
{
public Demo()
{
this.setDaemon(true);
}
public void run()
{
while(true)
{
try {
Thread.sleep(1000);
} catch (InterruptedException e) {}
System.out.println("守护线程在运行...");
}
}
}
public static void main(String[] args)
{
// TODO Auto-generated method stub
new Demo().start();
new A().start();
new B().start();
}
}
输出:
甲拿起了餐刀,等待叉子...
甲又拿起了叉子
乙拿起了餐刀,等待叉子...
守护线程在运行...
乙又拿起了叉子