Java线程共享变量

介绍

这篇文章描述两种线程共享实例变量的通用模型,一个Java线程是Thread类的实例,一个thread必须从一个thread base创建。一个thread包含一个thread base和隐藏的控制结构,隐藏的控制结构允许thread可以与其他线程并行执行。
每个Java应用至少包含两个线程,一个是主线程,另一个是垃圾回收线程。

Thread Base

thread base是以下一个class的实例

  • implements Runnable
  • extends Thread

thread base必须拥有下面的方法
public void run();

第一个thread base必须定义run方法,是为了implements Runnable接口。第一个thread base是Thread类本身作为thread base,提供run方法的简单定义,继承这个类的class必须覆写这个简单定义。

只要线程t从thread base中创建,通过执行start()方法来启动。

方法1:共享thread base

假如有一个implements Runnable的ThreadBase,主方法可以创建一个thread base:

ThreadBase base = new ThreadBase();

线程可以从同一个thread base中创建

Thread A = new Thread(base, "A");
Thread B = new Thread(base, "B");

执行start()方法调用

A.start();
B.start();

A和B共享thread base的成员变量,A和B拥有相同的run方法,但是他们的线程名字是不同的,仅仅成员变量可以共享,run方法使用的本地变量并不会共享。

方法2:分开的bases

假设除了main类还有三个类

  • Shared
  • ThreadA extends Thread
  • ThreadB extends Thread

主方法创建一个对象,这个对象的成员变量要被线程共享。

Shared s = new Shared();

创建线程

ThreadA A = new ThreadA(s);
ThreadB B = new ThreadB(s);

线程A和B使用不同的thread bases创建,A和B拥有不同的thread base。
线程A和B通过构造函数传递对象s,线程A如下:

class ThreadA extends Thread 
{   
  ThreadA( Shared s ) { this.s = s; }  // set member = parameter

  private Shared s;

  ...
}

在线程A中的s成员变量指向原对象s。
两种模型的区别

  • 模型1在thread base和thread之间做了明确区分,而模型2是thread base和thread进行了有效合并
  • 模型1中,线程共享成员变量,而在模型2中,仅仅指向的对象是共享的,模型2不能共享基础数据类型,如,int,boolean等等。必须只用包装类Integer,Float等等。
  • 当线程执行不同的操作时,模型2更容易使用,例如,协同问题,发布者/订阅者等等。

Java互斥锁

在Java中的每个对象都有一个内部lock(有一个locked变量)和一个阻塞线程的队列,互斥锁代码块可以通过synchronized关键字创建

Object x = new Object();

synchronized(x)
{
	// mutual exclusive access for all threads which share x
}

一个线程仅仅能够运行在synchronized(x)区域,如果它获得这个x锁。否则如果它尝试运行,将会阻塞。当一个线程离开这个区域,将会唤醒一个阻塞的线程。
另一种方法创建互斥锁的方法是

class MyClass {
  public synchronized void foo() {
     // ...
  }
}

在这种情况下,如果多个线程共享对象
MyClass c = new MyClass();
所有对c.foo()的调用都是互斥的。在MyClass类中对foo()方法的调用实际是this.foo(),在这种情况,this的lock被使用。

两个线程共享计数

假如两个线程共享integer counter,对counter的访问是在cs()方法中,我们想要确保在cs()方法的互斥。

方法1

class Main 
{
  public static void main(String args[]) 
  {
    ThreadBase base = new ThreadBase();
		
    Thread A = new Thread( base, "A" );
    Thread B = new Thread( base, "B" );
		
    A.start(); B.start();
    // ...
  }
}


class ThreadBase implements Runnable 
{
  int counter = 0;
  public void run() 
  {
    while (true) {
      /* non-critical section */
      cs();
    }
  }
	
  private void cs() {
    int x = counter;  // read into non-shared variable
    x = x + 1;        // update (this could be more elaborate)
    counter = x;      // write
  }
}

线程A和B共享base对象。base对象是最适合添加lock的选择,有两种方法添加锁,后一种方法更好

private void cs() {
  synchronized(this) { 
    /* body of cs() */
  }
}

private synchronized void cs() {
  /* body of cs() */
}

方法2

class Main 
{
  public static void main(String args[]) 
  {
    Counter c = new Counter();
		
    Thread A = new ThreadA( c );
    Thread B = new ThreadB( c );
		
    A.start(); B.start();
		
    // ...
  }
}

class Counter 
{
  int val = 0;      // for simplicity, val is not private
}

class ThreadA extends Thread 
{
  private Counter c;
	
  ThreadA( Counter c ) { this.c = c; }
	
  public void run() 
  {
    while (true) {
      /* non-critical section */
      cs();
    }
  }
	
  private void cs() {
    int x = c.val;  // read into non-shared variable
    x = x + 1;      // update (this could be more elaborate)
    c.val = x;      // write
  }
}

class ThreadB extends Thread 
{
  private Counter c;
	
  ThreadB( Counter c ) { this.c = c; }
  
  // everything else is identical to ThreadA
}

c对象是最适合添加锁的选择,仅仅有一种方式

private void cs() 
{
  synchronized(c) { 
    /* body of cs() */
  }
}

互斥锁方法可以作为Counter的成员方法

class Counter 
{
  private int val = 0;      // now we can make val private
  
  public synchronized void cs() {
    int x = val;
    x = x + 1;
    val = x;
  }
}

在这种情况下,cs()不是线程A和线程B的成员方法

public void run() 
{
  while (true) {
    /* non-critical section */
    
    c.cs();  // call cs() through Counter c
  }
}

你可能感兴趣的:(Java)