2.3谨慎活跃问题(Beware of Liveness Problems)

活跃指的是一些最终以有利方面结束。当一个应用到达的状态是为不可能继续向下执行时,那么就是非活跃的。在单一线程应用中,死循环就是一个例子。在多线程的应用中,活跃是死锁、活锁和饥饿:

        Deadlock:线程1等待的资源是线程2所持有的,和线程2等待资源是线程1所持有的。两个线程会同时在等待。

        LiveLock:线程x保持着尝试一个操作,而这个总是失败。这个的情况是不能继续执行的。

        Starvation:线程x继续延迟(通过调度程序列表),它需要资源让自己的程序继续下去。在低优先权线程之前可能存在高优先权调度程序执行,而这里总是有高优先权的程序。饥饿也通常指无限延期(indefinite postponement)。

思考死锁。之个问题出现是因为有太多同步通过同步锁。如果你不注意,你可能遇到这样的问题,锁被多个线程请求,没有线程持有它自己的锁,但是持有的锁需要其它线程的资源,没有线程可以在这个临界区进入或退出去释放自己持有的锁,因为其它线程在这个临界区持有这个锁。下面例子证明了这个问题:

Listing 2-1 死锁例子。

package com.owen.thread.chapter2;

public class DeadlockDemo
{
	private final Object lock1 = new Object();
	private final Object lock2 = new Object();

	public void instanceMethod1()
	{
		synchronized (lock1)
		{
			synchronized (lock2)
			{
				System.out.println("first thread in instanceMethod1");
				// critical section guarded first by
				// lock1 and then by lock2
			}
		}
	}

	public void instanceMethod2()
	{
		synchronized (lock2)
		{
			synchronized (lock1)
			{
				System.out.println("second thread in instanceMethod2");
				// critical section guarded first by
				// lock2 and then by lock1
			}
		}
	}

	public static void main(String[] args)
	{
		final DeadlockDemo dld = new DeadlockDemo();
		Runnable r1 = new Runnable()
		{
			@Override
			public void run()
			{
				while (true)
				{
					dld.instanceMethod1();
					try
					{
						Thread.sleep(50);
					} catch (InterruptedException ie)
					{
					}
				}
			}
		};
		Thread thdA = new Thread(r1);
		Runnable r2 = new Runnable()
		{
			@Override
			public void run()
			{
				while (true)
				{
					dld.instanceMethod2();
					try
					{
						Thread.sleep(50);
					} catch (InterruptedException ie)
					{
					}
				}
			}
		};
		Thread thdB = new Thread(r2);
		thdA.start();
		thdB.start();
	}
}

Listing2-1线程A和线程B分别请求instanceMethod1()和instanceMethod2(),在不同的时间。思考下面的执行顺序:

        线程A请求instanceMehtod1(),包含一个锁注册lock1引用对象,和进入它外部的临界区。(但是不能请求注册lock2引用对象。)

        线程B请求instanceMehtod2(),包含一个锁注册lock2引用对象,和进入它外部的临界区。(但是不能请求注册lock1引用对象。)

        线程A试图去请求锁连接lock2,但是java虚拟机强迫线程在临界外部等待,因为线程B持有这个锁。

        线程B试图去请求锁连接lock1,但是java虚拟机强迫线程在临界外部等待,因为线程A持有这个锁。

        没有线程可以继续执行,因为另一个线程持有该线程需要的锁。你拥有了死锁的状态,和这个项目(至少在上下文存在两线程)被“冻结”。

   你可以观察第一个线程在instanceMethod1和第二个线程在instanceMethod2的信息正常输出,直到应用被“冻结”,因为死锁。

     尽管前面的例子明确了一个死锁状态的应用,但是在实际应用中并不是那么容易发现死锁。例如,你的代码可能包含下面多个类的循环关系(在向几个资源文件):

        类A同步方法请求类B的同步方法。

        类B同步方法请求类C的同步方法。

        类C同步方法请求类A的同步方法。

     如果线程A请求类A的同步方法和线程B请求类C的同步方法,当线程B试图去请求类A的同步方法时,它将会阻塞,而线程A问题在这个方法内部,线程A继续执行直到它请求到的类C同步方法,然后就会阻塞。死锁就产生了。

                  源码下载:git@github.com:owenwilliam/Thread.git


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
这段代码定义了两个结构体,分别是 nf_conn 和 nf_conntrack。 nf_conn 结构体包含了以下成员: - ct_general:一个 nf_conntrack 结构体,用于跟踪连接的一般信息。 - lock:自旋锁,用于保护对 nf_conn 结构体的并发访问。 - cpu:一个 16 位的无符号整数,表示该连接所在的 CPU 编号。 - zone:一个 nf_conntrack_zone 结构体,在 CONFIG_NF_CONNTRACK_ZONES 宏开启时有效。 - tuplehash:一个包含了 IP_CT_DIR_MAX 个元素的 nf_conntrack_tuple_hash 数组,用于存储连接的原始和回复的元组信息。 - status:一个无符号长整型数,用于表示连接的状态。 - timeout:一个 32 位的无符号整数,表示连接被认为已经死亡的时间戳(以 jiffies32 表示)。 - ct_net:一个 possible_net_t 类型的变量,表示连接所属的网络命名空间。 - nat_bysource:一个 hlist_node 结构体,在 CONFIG_NF_NAT 宏开启时有效。 - __nfct_init_offset:一个空结构体,用于初始化其他成员。 - master:一个指向 nf_conn 结构体的指针,表示该连接的期望连接(expectation)。 - mark:一个 32 位的无符号整数,在 CONFIG_NF_CONNTRACK_MARK 宏开启时有效。 - secmark:一个 32 位的无符号整数,用于安全标记,在 CONFIG_NF_CONNTRACK_SECMARK 宏开启时有效。 - ext:一个指向 nf_ct_ext 结构体的指针,表示连接的扩展信息。 - proto:一个联合体,用于存储其他模块保留的数据。 nf_conntrack 结构体包含了一个名为 use 的 atomic_t 类型成员,用于记录 nf_conntrack 结构体的使用计数。atomic_t 是一个原子类型,用于实现原子操作的计数器。 需要注意的是,这段代码只是结构体的定义,没有展示结构体成员的具体实现。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值