重庆分公司,新征程启航
为企业提供网站建设、域名注册、服务器等服务
1.非阻塞算法
创新互联公司是网站建设专家,致力于互联网品牌建设与网络营销,专业领域包括成都网站设计、网站建设、电商网站制作开发、小程序开发、微信营销、系统平台开发,与其他网站设计及系统开发公司不同,我们的整合解决方案结合了恒基网络品牌建设经验和互联网整合营销的理念,并将策略和执行紧密结合,且不断评估并优化我们的方案,为客户提供全方位的互联网品牌整合方案!
非阻塞算法属于并发算法,它们可以安全地派生它们的线程,不通过锁定派生,而是通过低级的原子性的硬件原生形式 —— 例如比较和交换。非阻塞算法的设计与实现极为困难,但是它们能够提供更好的吞吐率,对生存问题(例如死锁和优先级反转)也能提供更好的防御。使用底层的原子化机器指令取代锁,比如比较并交换(CAS,compare-and-swap).
2.悲观技术
独占锁是一种悲观的技术.它假设最坏的情况发生(如果不加锁,其它线程会破坏对象状态),即使没有发生最坏的情况,仍然用锁保护对象状态.
3.乐观技术
依赖冲突监测.先更新,如果监测发生冲突发生,则放弃更新后重试,否则更新成功.现在处理器都有原子化的读-改-写指令,比如比较并交换(CAS,compare-and-swap).
4.CAS操作
CAS有3个操作数,内存值V,旧的预期值A,要修改的新值B。当且仅当预期值A和内存值V相同时,将内存值V修改为B,否则什么都不做。CAS典型使用模式是:首先从V中读取A,并根据A计算新值B,然后再通过CAS以原子方式将V中的值由A变成B(只要在这期间没有任何线程将V的值修改为其他值)。
清单 3. 说明比较并交换的行为(而不是性能)的代码
public class SimulatedCAS {
private int value;
public synchronized int getValue() { return value; }
public synchronized int compareAndSwap(int expectedValue, int newValue) {
int oldValue = value;
if (value == expectedValue)
value = newValue;
return oldValue;
}
}
清单 4. 使用比较并交换实现计数器
public class CasCounter {
private SimulatedCAS value;
public int getValue() {
return value.getValue();
}
public int increment() {
int oldValue = value.getValue();
while (value.compareAndSwap(oldValue, oldValue + 1) != oldValue)
oldValue = value.getValue();
return oldValue + 1;
}
}
5.原子变量
原子变量支持不用锁保护就能原子性更新操作,其底层用CAS实现。共有12个原子变量,可分为4组:标量类、更新器类、数组类以及复合变量类。最常用的原子变量就是标量类:AtomicInteger、AtomicLong、AtomicBoolean以及AtomicReference。所有类型都支持CAS。
6.性能比较:锁与原子变量
在中低程度的竞争下,原子变量能提供很高的可伸缩性,原子变量性能超过锁;而在高强度的竞争下,锁能够更有效地避免竞争,锁的性能将超过原子变量的性能。但在更真实的实际情况中,原子变量的性能将超过锁的性能。
//解决方案-1 设置3把锁, 然后把锁们应用到所有线程中 (涉及到synchronized wait notify等, 嫌麻烦. 略)
解决方案-2 设置3个全局共享的信号标记(信号灯) + 3子线程分别占用标记1 2 3
+ 主线程轮询/等待
(简洁明快 推荐)
//解决方案-2 实现如下:
static boolean t1_done = false;
static boolean t2_done = false;
static boolean t3_done = false;
//t1------run() { ............ ; t1_done = true; }
//t2、 3: 同理,略
main () { .............;
启动t1;
启动t2;
启动t3;
//轮询 or 等待
while ( true )
if ( t1_done t2_done t3_done) break;
else
Thread.yield () ;
// 或 Thread.sleep(xxxx) ----若子线程运行超过100ms以上,应予考虑
//轮询结束,主线程继续工作
} //main END
have fun
读-写互斥:加个boolean writeFlag = false;写的时候保持该值为true,有读操作的时候判断该值是否为false,否则等待读。
读者优先的附加限制:即多个读操作可以同时进行。
写者优先的附加限制:设置线程的优先级,保持写的线程优先级始终高于读线程的优先级。
public synchronized void CunQu(int number)//存取方法
将整个方法同步后,不能达到演示效果,可以采用同步块的方式来保证对money变量的同步.修改如下.
import java.awt.Button;
import java.awt.FlowLayout;
import java.awt.Frame;
import java.awt.TextArea;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.text.SimpleDateFormat;
import java.util.Date;
public class Example9_13 {
public static void main(String args[]) {
new FrameMoney();
}
}
class FrameMoney extends Frame implements Runnable, ActionListener {
int money = 100;
TextArea text1, text2;
Thread Cashier, Accounting;
int weekDay;
Button start = new Button("开始演示");
FrameMoney() {
Cashier = new Thread(this);
Accounting = new Thread(this);
text1 = new TextArea(12, 30);
text2 = new TextArea(12, 30);
setLayout(new FlowLayout());
add(start);
add(text1);
add(text2);
setVisible(true);
setSize(600, 300);
validate();
addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
System.exit(0);
}
});
start.addActionListener(this);
}
public void actionPerformed(ActionEvent e) { for (int i = 0; i 3; i++) {
while (Cashier.isAlive() || Accounting.isAlive()) {
try {
Thread.sleep(1000);
} catch (InterruptedException e1) {
}
}
weekDay = i + 1;
Accounting = new Thread(this);
Cashier = new Thread(this);
try {
Accounting.start();
Cashier.start();
} catch (Exception e1) {
}
}
}
public void CunQu(int number)// 存取方法
{
if (Thread.currentThread() == Accounting) {
text1.append("今天是星期:" + weekDay + "\n");
for (int i = 1; i = 3; i++) {
synchronized (this) {
money = money + number;
text1.append(new SimpleDateFormat("HH:mm:ss")
.format(new Date()) + " 账上有" + money + "万\n");
}
try {
Thread.sleep(Math.round(Math.random() * 3000) + 1000);
} catch (InterruptedException e) {
}
}
text1.append("\n");
} else if (Thread.currentThread() == Cashier) {
text2.append("Today is星期" + weekDay + "\n");
for (int i = 1; i = 2; i++) {
synchronized (this) {
money = money - number / 2;
text2.append(new SimpleDateFormat("HH:mm:ss")
.format(new Date()) + " 账上有" + money + "万\n");
}
try {
Thread.sleep(Math.round(Math.random() * 3000) + 1000);
} catch (InterruptedException e) {
}
}
text2.append("\n\n");
}
}
public void run() {
if (Thread.currentThread() == Accounting
|| Thread.currentThread() == Cashier) {
CunQu(30);
}
}
}
一般有两种方法 同步方法和同步代码块
假设P1、P2是同一个类的不同对象,这个类中定义了以下几种情况的同步块或同步方法,P1、P2就都可以调用它们。
1. 把synchronized当作函数修饰符时,示例代码如下:
Public synchronized void methodAAA()
{
//….
}
这也就是同步方法,那这时synchronized锁定的是哪个对象呢?它锁定的是调用这个同步方法对象。也就是说,当一个对象P1在不同的线程中执行这个同步方法时,它们之间会形成互斥,达到同步的效果。但是这个对象所属的Class所产生的另一对象P2却可以任意调用这个被加了synchronized关键字的方法。
上边的示例代码等同于如下代码:
public void methodAAA()
{
synchronized (this) // (1)
{
//…..
}
}
(1)处的this指的是什么呢?它指的就是调用这个方法的对象,如P1。可见同步方法实质是将synchronized作用于object reference。――那个拿到了P1对象锁的线程,才可以调用P1的同步方法,而对P2而言,P1这个锁与它毫不相干,程序也可能在这种情形下摆脱同步机制的控制,造成数据混乱:(
2.同步块,示例代码如下:
public void method3(SomeObject so)
{
synchronized(so)
{
//…..
}
}
这时,锁就是so这个对象,谁拿到这个锁谁就可以运行它所控制的那段代码。当有一个明确的对象作为锁时,就可以这样写程序,但当没有明确的对象作为锁,只是想让一段代码同步时,可以创建一个特殊的instance变量(它得是一个对象)来充当锁:
class Foo implements Runnable
{
private byte[] lock = new byte[0]; // 特殊的instance变量
Public void methodA()
{
synchronized(lock) { //… }
}
//…..
}
注:零长度的byte数组对象创建起来将比任何对象都经济――查看编译后的字节码:生成零长度的byte[]对象只需3条操作码,而Object lock = new Object()则需要7行操作码。
3.将synchronized作用于static 函数,示例代码如下:
Class Foo
{
public synchronized static void methodAAA() // 同步的static 函数
{
//….
}
public void methodBBB()
{
synchronized(Foo.class) // class literal(类名称字面常量)
}
}
代码中的methodBBB()方法是把class literal作为锁的情况,它和同步的static函数产生的效果是一样的,取得的锁很特别,是当前调用这个方法的对象所属的类(Class,而不再是由这个Class产生的某个具体对象了)。
记得在《Effective Java》一书中看到过将 Foo.class和 P1.getClass()用于作同步锁还不一样,不能用P1.getClass()来达到锁这个Class的目的。P1指的是由Foo类产生的对象。
可以推断:如果一个类中定义了一个synchronized的static函数A,也定义了一个synchronized 的instance函数B,那么这个类的同一对象Obj在多线程中分别访问A和B两个方法时,不会构成同步,因为它们的锁都不一样。A方法的锁是Obj这个对象,而B的锁是Obj所属的那个Class。
同步和异步一般是指多线程中对资源的访问的。最简单的例子是在多线程中对一个静态整数进行递增操作,然后在线程run方法上加synchronizied关键字试试。